mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
refactor outline logics
This commit is contained in:
parent
598e8144bb
commit
183b24915a
@ -44,7 +44,6 @@ SOURCES += main.cpp\
|
|||||||
veditwindow.cpp \
|
veditwindow.cpp \
|
||||||
vedittab.cpp \
|
vedittab.cpp \
|
||||||
voutline.cpp \
|
voutline.cpp \
|
||||||
vtoc.cpp \
|
|
||||||
vsingleinstanceguard.cpp \
|
vsingleinstanceguard.cpp \
|
||||||
vdirectory.cpp \
|
vdirectory.cpp \
|
||||||
vfile.cpp \
|
vfile.cpp \
|
||||||
@ -78,7 +77,8 @@ SOURCES += main.cpp\
|
|||||||
vnotefile.cpp \
|
vnotefile.cpp \
|
||||||
vattachmentlist.cpp \
|
vattachmentlist.cpp \
|
||||||
dialog/vsortdialog.cpp \
|
dialog/vsortdialog.cpp \
|
||||||
vfilesessioninfo.cpp
|
vfilesessioninfo.cpp \
|
||||||
|
vtableofcontent.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
@ -108,7 +108,6 @@ HEADERS += vmainwindow.h \
|
|||||||
veditwindow.h \
|
veditwindow.h \
|
||||||
vedittab.h \
|
vedittab.h \
|
||||||
voutline.h \
|
voutline.h \
|
||||||
vtoc.h \
|
|
||||||
vsingleinstanceguard.h \
|
vsingleinstanceguard.h \
|
||||||
vdirectory.h \
|
vdirectory.h \
|
||||||
vfile.h \
|
vfile.h \
|
||||||
@ -144,7 +143,8 @@ HEADERS += vmainwindow.h \
|
|||||||
vnotefile.h \
|
vnotefile.h \
|
||||||
vattachmentlist.h \
|
vattachmentlist.h \
|
||||||
dialog/vsortdialog.h \
|
dialog/vsortdialog.h \
|
||||||
vfilesessioninfo.h
|
vfilesessioninfo.h \
|
||||||
|
vtableofcontent.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc \
|
vnote.qrc \
|
||||||
|
@ -16,9 +16,15 @@ class VDocument : public QObject
|
|||||||
public:
|
public:
|
||||||
// @p_file could be NULL.
|
// @p_file could be NULL.
|
||||||
VDocument(const VFile *p_file, QObject *p_parent = 0);
|
VDocument(const VFile *p_file, QObject *p_parent = 0);
|
||||||
|
|
||||||
QString getToc();
|
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 scrollToAnchor(const QString &anchor);
|
||||||
|
|
||||||
void setHtml(const QString &html);
|
void setHtml(const QString &html);
|
||||||
|
|
||||||
// Request to highlight a segment text.
|
// Request to highlight a segment text.
|
||||||
// Use p_id to identify the result.
|
// Use p_id to identify the result.
|
||||||
void highlightTextAsync(const QString &p_text, int p_id, int p_timeStamp);
|
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.
|
// When the Web view has been scrolled, it will signal current header anchor.
|
||||||
// Empty @anchor to indicate an invalid header.
|
// Empty @anchor to indicate an invalid header.
|
||||||
|
// The header does not begins with '#'.
|
||||||
void setHeader(const QString &anchor);
|
void setHeader(const QString &anchor);
|
||||||
|
|
||||||
void setLog(const QString &p_log);
|
void setLog(const QString &p_log);
|
||||||
@ -49,8 +56,12 @@ public slots:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void textChanged(const QString &text);
|
void textChanged(const QString &text);
|
||||||
|
|
||||||
void tocChanged(const QString &toc);
|
void tocChanged(const QString &toc);
|
||||||
|
|
||||||
void requestScrollToAnchor(const QString &anchor);
|
void requestScrollToAnchor(const QString &anchor);
|
||||||
|
|
||||||
|
// @anchor is the id of that anchor, without '#'.
|
||||||
void headerChanged(const QString &anchor);
|
void headerChanged(const QString &anchor);
|
||||||
void htmlChanged(const QString &html);
|
void htmlChanged(const QString &html);
|
||||||
void logChanged(const QString &p_log);
|
void logChanged(const QString &p_log);
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "vedit.h"
|
#include "vedit.h"
|
||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vtoc.h"
|
#include "vtableofcontent.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "utils/veditutils.h"
|
#include "utils/veditutils.h"
|
||||||
#include "veditoperations.h"
|
#include "veditoperations.h"
|
||||||
@ -157,15 +157,16 @@ void VEdit::reloadFile()
|
|||||||
setModified(false);
|
setModified(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEdit::scrollToLine(int p_lineNumber)
|
bool VEdit::scrollToBlock(int p_blockNumber)
|
||||||
{
|
{
|
||||||
Q_ASSERT(p_lineNumber >= 0);
|
QTextBlock block = document()->findBlockByNumber(p_blockNumber);
|
||||||
|
|
||||||
QTextBlock block = document()->findBlockByLineNumber(p_lineNumber);
|
|
||||||
if (block.isValid()) {
|
if (block.isValid()) {
|
||||||
VEditUtils::scrollBlockInPage(this, block.blockNumber(), 0);
|
VEditUtils::scrollBlockInPage(this, block.blockNumber(), 0);
|
||||||
moveCursor(QTextCursor::EndOfBlock);
|
moveCursor(QTextCursor::EndOfBlock);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEdit::isModified() const
|
bool VEdit::isModified() const
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
#include <QRect>
|
#include <QRect>
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vtoc.h"
|
|
||||||
#include "vnotefile.h"
|
#include "vnotefile.h"
|
||||||
|
|
||||||
class VEditOperations;
|
class VEditOperations;
|
||||||
@ -81,7 +80,9 @@ public:
|
|||||||
virtual void setModified(bool p_modified);
|
virtual void setModified(bool p_modified);
|
||||||
bool isModified() const;
|
bool isModified() const;
|
||||||
virtual void reloadFile();
|
virtual void reloadFile();
|
||||||
virtual void scrollToLine(int p_lineNumber);
|
|
||||||
|
virtual bool scrollToBlock(int p_blockNumber);
|
||||||
|
|
||||||
// User requests to insert an image.
|
// User requests to insert an image.
|
||||||
virtual void insertImage();
|
virtual void insertImage();
|
||||||
|
|
||||||
|
@ -77,9 +77,9 @@ void VEditArea::insertSplitWindow(int idx)
|
|||||||
connect(win, &VEditWindow::getFocused,
|
connect(win, &VEditWindow::getFocused,
|
||||||
this, &VEditArea::handleWindowFocused);
|
this, &VEditArea::handleWindowFocused);
|
||||||
connect(win, &VEditWindow::outlineChanged,
|
connect(win, &VEditWindow::outlineChanged,
|
||||||
this, &VEditArea::handleOutlineChanged);
|
this, &VEditArea::handleWindowOutlineChanged);
|
||||||
connect(win, &VEditWindow::curHeaderChanged,
|
connect(win, &VEditWindow::currentHeaderChanged,
|
||||||
this, &VEditArea::handleCurHeaderChanged);
|
this, &VEditArea::handleWindowCurrentHeaderChanged);
|
||||||
connect(win, &VEditWindow::statusMessage,
|
connect(win, &VEditWindow::statusMessage,
|
||||||
this, &VEditArea::handleWindowStatusMessage);
|
this, &VEditArea::handleWindowStatusMessage);
|
||||||
connect(win, &VEditWindow::vimStatusUpdated,
|
connect(win, &VEditWindow::vimStatusUpdated,
|
||||||
@ -237,15 +237,13 @@ void VEditArea::updateWindowStatus()
|
|||||||
Q_ASSERT(splitter->count() == 0);
|
Q_ASSERT(splitter->count() == 0);
|
||||||
|
|
||||||
emit tabStatusUpdated(VEditTabInfo());
|
emit tabStatusUpdated(VEditTabInfo());
|
||||||
emit outlineChanged(VToc());
|
emit outlineChanged(VTableOfContent());
|
||||||
emit curHeaderChanged(VAnchor());
|
emit currentHeaderChanged(VHeaderPointer());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VEditWindow *win = getWindow(curWindowIndex);
|
VEditWindow *win = getWindow(curWindowIndex);
|
||||||
win->updateTabStatus();
|
win->updateTabStatus();
|
||||||
win->requestUpdateOutline();
|
|
||||||
win->requestUpdateCurHeader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEditArea::closeFile(const VFile *p_file, bool p_forced)
|
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();
|
QObject *winObject = sender();
|
||||||
if (splitter->widget(curWindowIndex) == winObject) {
|
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();
|
QObject *winObject = sender();
|
||||||
if (splitter->widget(curWindowIndex) == winObject) {
|
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
|
VEditWindow *win = getCurrentWindow();
|
||||||
getWindow(curWindowIndex)->scrollCurTab(anchor);
|
if (win) {
|
||||||
|
win->scrollToHeader(p_header);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEditArea::isFileOpened(const VFile *p_file)
|
bool VEditArea::isFileOpened(const VFile *p_file)
|
||||||
@ -648,6 +648,7 @@ VEditWindow *VEditArea::getCurrentWindow() const
|
|||||||
if (curWindowIndex < 0) {
|
if (curWindowIndex < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getWindow(curWindowIndex);
|
return getWindow(curWindowIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
#include "veditwindow.h"
|
#include "veditwindow.h"
|
||||||
#include "vtoc.h"
|
|
||||||
#include "vnavigationmode.h"
|
#include "vnavigationmode.h"
|
||||||
|
|
||||||
class VNote;
|
class VNote;
|
||||||
@ -82,8 +81,11 @@ signals:
|
|||||||
// Emit when current window's tab status updated.
|
// Emit when current window's tab status updated.
|
||||||
void tabStatusUpdated(const VEditTabInfo &p_info);
|
void tabStatusUpdated(const VEditTabInfo &p_info);
|
||||||
|
|
||||||
void outlineChanged(const VToc &toc);
|
// Emit when current window's tab's outline changed.
|
||||||
void curHeaderChanged(const VAnchor &anchor);
|
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.
|
// Emit when want to show message in status bar.
|
||||||
void statusMessage(const QString &p_msg);
|
void statusMessage(const QString &p_msg);
|
||||||
@ -106,7 +108,10 @@ public slots:
|
|||||||
void saveFile();
|
void saveFile();
|
||||||
void readFile();
|
void readFile();
|
||||||
void saveAndReadFile();
|
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 handleFileUpdated(const VFile *p_file);
|
||||||
void handleDirectoryUpdated(const VDirectory *p_dir);
|
void handleDirectoryUpdated(const VDirectory *p_dir);
|
||||||
void handleNotebookUpdated(const VNotebook *p_notebook);
|
void handleNotebookUpdated(const VNotebook *p_notebook);
|
||||||
@ -118,8 +123,11 @@ private slots:
|
|||||||
|
|
||||||
void handleRemoveSplitRequest(VEditWindow *curWindow);
|
void handleRemoveSplitRequest(VEditWindow *curWindow);
|
||||||
void handleWindowFocused();
|
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 handleFindTextChanged(const QString &p_text, uint p_options);
|
||||||
void handleFindOptionChanged(uint p_options);
|
void handleFindOptionChanged(uint p_options);
|
||||||
void handleFindNext(const QString &p_text, uint p_options, bool p_forward);
|
void handleFindNext(const QString &p_text, uint p_options, bool p_forward);
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
#include <QWheelEvent>
|
#include <QWheelEvent>
|
||||||
|
|
||||||
VEditTab::VEditTab(VFile *p_file, VEditArea *p_editArea, QWidget *p_parent)
|
VEditTab::VEditTab(VFile *p_file, VEditArea *p_editArea, QWidget *p_parent)
|
||||||
: QWidget(p_parent), m_file(p_file), m_isEditMode(false),
|
: QWidget(p_parent),
|
||||||
m_modified(false), m_editArea(p_editArea)
|
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,
|
connect(qApp, &QApplication::focusChanged,
|
||||||
this, &VEditTab::handleFocusChanged);
|
this, &VEditTab::handleFocusChanged);
|
||||||
}
|
}
|
||||||
@ -33,7 +34,7 @@ bool VEditTab::isEditMode() const
|
|||||||
|
|
||||||
bool VEditTab::isModified() const
|
bool VEditTab::isModified() const
|
||||||
{
|
{
|
||||||
return m_modified;
|
return m_file->isModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
VFile *VEditTab::getFile() const
|
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)
|
void VEditTab::wheelEvent(QWheelEvent *p_event)
|
||||||
{
|
{
|
||||||
QPoint angle = p_event->angleDelta();
|
QPoint angle = p_event->angleDelta();
|
||||||
@ -78,41 +69,41 @@ void VEditTab::wheelEvent(QWheelEvent *p_event)
|
|||||||
p_event->ignore();
|
p_event->ignore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditTab::updateStatus()
|
VEditTabInfo VEditTab::fetchTabInfo() const
|
||||||
{
|
|
||||||
m_modified = m_file->isModified();
|
|
||||||
|
|
||||||
emit statusUpdated(fetchTabInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
VEditTabInfo VEditTab::fetchTabInfo()
|
|
||||||
{
|
{
|
||||||
VEditTabInfo info;
|
VEditTabInfo info;
|
||||||
info.m_editTab = this;
|
info.m_editTab = const_cast<VEditTab *>(this);
|
||||||
|
|
||||||
return info;
|
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)
|
void VEditTab::tryRestoreFromTabInfo(const VEditTabInfo &p_info)
|
||||||
{
|
{
|
||||||
if (p_info.m_editTab != this) {
|
if (p_info.m_editTab != this) {
|
||||||
// Clear and return.
|
m_infoToRestore.clear();
|
||||||
m_infoToRestore.m_editTab = NULL;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (restoreFromTabInfo(p_info)) {
|
if (restoreFromTabInfo(p_info)) {
|
||||||
// Clear and return.
|
m_infoToRestore.clear();
|
||||||
m_infoToRestore.m_editTab = NULL;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save it and restore later.
|
// Save it and restore later.
|
||||||
m_infoToRestore = p_info;
|
m_infoToRestore = p_info;
|
||||||
qDebug() << "save info for restore later" << p_info.m_anchorIndex;
|
}
|
||||||
|
|
||||||
|
void VEditTab::updateStatus()
|
||||||
|
{
|
||||||
|
emit statusUpdated(fetchTabInfo());
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include "vtoc.h"
|
#include "vtableofcontent.h"
|
||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
#include "utils/vvim.h"
|
#include "utils/vvim.h"
|
||||||
#include "vedittabinfo.h"
|
#include "vedittabinfo.h"
|
||||||
@ -37,12 +37,9 @@ public:
|
|||||||
|
|
||||||
void focusTab();
|
void focusTab();
|
||||||
|
|
||||||
virtual void requestUpdateOutline();
|
// Scroll to @p_header.
|
||||||
|
// Will emit currentHeaderChanged() if @p_header is valid.
|
||||||
virtual void requestUpdateCurHeader();
|
virtual void scrollToHeader(const VHeaderPointer &p_header) { Q_UNUSED(p_header) }
|
||||||
|
|
||||||
// Scroll to anchor @p_anchor.
|
|
||||||
virtual void scrollToAnchor(const VAnchor& p_anchor) = 0;
|
|
||||||
|
|
||||||
VFile *getFile() const;
|
VFile *getFile() const;
|
||||||
|
|
||||||
@ -72,21 +69,23 @@ public:
|
|||||||
virtual void decorateText(TextDecoration p_decoration) {Q_UNUSED(p_decoration);}
|
virtual void decorateText(TextDecoration p_decoration) {Q_UNUSED(p_decoration);}
|
||||||
|
|
||||||
// Create a filled VEditTabInfo.
|
// 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.
|
// Restore status from @p_info.
|
||||||
// If this tab is not ready yet, it will restore once it is ready.
|
// If this tab is not ready yet, it will restore once it is ready.
|
||||||
void tryRestoreFromTabInfo(const VEditTabInfo &p_info);
|
void tryRestoreFromTabInfo(const VEditTabInfo &p_info);
|
||||||
|
|
||||||
|
// Emit signal to update current status.
|
||||||
|
virtual void updateStatus();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Enter edit mode
|
// Enter edit mode
|
||||||
virtual void editFile() = 0;
|
virtual void editFile() = 0;
|
||||||
|
|
||||||
// Update status of current tab. Emit statusUpdated().
|
|
||||||
virtual void updateStatus();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void wheelEvent(QWheelEvent *p_event) Q_DECL_OVERRIDE;
|
void wheelEvent(QWheelEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
@ -102,10 +101,15 @@ protected:
|
|||||||
|
|
||||||
// File related to this tab.
|
// File related to this tab.
|
||||||
QPointer<VFile> m_file;
|
QPointer<VFile> m_file;
|
||||||
|
|
||||||
bool m_isEditMode;
|
bool m_isEditMode;
|
||||||
bool m_modified;
|
|
||||||
VToc m_toc;
|
// Table of content of this tab.
|
||||||
VAnchor m_curHeader;
|
VTableOfContent m_outline;
|
||||||
|
|
||||||
|
// Current header in m_outline of this tab.
|
||||||
|
VHeaderPointer m_currentHeader;
|
||||||
|
|
||||||
VEditArea *m_editArea;
|
VEditArea *m_editArea;
|
||||||
|
|
||||||
// Tab info to restore from once ready.
|
// Tab info to restore from once ready.
|
||||||
@ -114,9 +118,9 @@ protected:
|
|||||||
signals:
|
signals:
|
||||||
void getFocused();
|
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.
|
// The status of current tab has updates.
|
||||||
void statusUpdated(const VEditTabInfo &p_info);
|
void statusUpdated(const VEditTabInfo &p_info);
|
||||||
|
@ -10,10 +10,19 @@ struct VEditTabInfo
|
|||||||
m_cursorBlockNumber(-1),
|
m_cursorBlockNumber(-1),
|
||||||
m_cursorPositionInBlock(-1),
|
m_cursorPositionInBlock(-1),
|
||||||
m_blockCount(-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;
|
VEditTab *m_editTab;
|
||||||
|
|
||||||
// Cursor information. -1 for invalid info.
|
// Cursor information. -1 for invalid info.
|
||||||
@ -21,8 +30,8 @@ struct VEditTabInfo
|
|||||||
int m_cursorPositionInBlock;
|
int m_cursorPositionInBlock;
|
||||||
int m_blockCount;
|
int m_blockCount;
|
||||||
|
|
||||||
// Anchor index in outline.
|
// Header index in outline.
|
||||||
int m_anchorIndex;
|
int m_headerIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VEDITTABINFO_H
|
#endif // VEDITTABINFO_H
|
||||||
|
@ -466,15 +466,15 @@ void VEditWindow::updateTabStatus(int p_index)
|
|||||||
|
|
||||||
if (p_index == -1) {
|
if (p_index == -1) {
|
||||||
emit tabStatusUpdated(VEditTabInfo());
|
emit tabStatusUpdated(VEditTabInfo());
|
||||||
emit outlineChanged(VToc());
|
emit outlineChanged(VTableOfContent());
|
||||||
emit curHeaderChanged(VAnchor());
|
emit currentHeaderChanged(VHeaderPointer());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VEditTab *tab = getTab(p_index);
|
VEditTab *tab = getTab(p_index);
|
||||||
tab->updateStatus();
|
emit tabStatusUpdated(tab->fetchTabInfo());
|
||||||
tab->requestUpdateOutline();
|
emit outlineChanged(tab->getOutline());
|
||||||
tab->requestUpdateCurHeader();
|
emit currentHeaderChanged(tab->getCurrentHeader());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditWindow::updateTabInfo(int p_index)
|
void VEditWindow::updateTabInfo(int p_index)
|
||||||
@ -506,26 +506,24 @@ void VEditWindow::updateAllTabsSequence()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Be requested to report current outline
|
VTableOfContent VEditWindow::getOutline() const
|
||||||
void VEditWindow::requestUpdateOutline()
|
|
||||||
{
|
{
|
||||||
int idx = currentIndex();
|
int idx = currentIndex();
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
emit outlineChanged(VToc());
|
return VTableOfContent();
|
||||||
return;
|
|
||||||
}
|
|
||||||
getTab(idx)->requestUpdateOutline();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Be requested to report current header
|
return getTab(idx)->getOutline();
|
||||||
void VEditWindow::requestUpdateCurHeader()
|
}
|
||||||
|
|
||||||
|
VHeaderPointer VEditWindow::getCurrentHeader() const
|
||||||
{
|
{
|
||||||
int idx = currentIndex();
|
int idx = currentIndex();
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
emit curHeaderChanged(VAnchor());
|
return VHeaderPointer();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
getTab(idx)->requestUpdateCurHeader();
|
|
||||||
|
return getTab(idx)->getCurrentHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus this windows. Try to focus current tab.
|
// Focus this windows. Try to focus current tab.
|
||||||
@ -681,44 +679,39 @@ bool VEditWindow::canRemoveSplit()
|
|||||||
return splitter->count() > 1;
|
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
|
// Only propagate it if it is current tab.
|
||||||
int idx = currentIndex();
|
VEditTab *tab = getCurrentTab();
|
||||||
if (idx == -1) {
|
if (tab) {
|
||||||
emit outlineChanged(VToc());
|
if (tab->getFile() == p_outline.getFile()) {
|
||||||
return;
|
emit outlineChanged(p_outline);
|
||||||
}
|
}
|
||||||
const VFile *file = getTab(idx)->getFile();
|
} else {
|
||||||
if (p_toc.m_file == file) {
|
emit outlineChanged(VTableOfContent());
|
||||||
emit outlineChanged(p_toc);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditWindow::handleCurHeaderChanged(const VAnchor &p_anchor)
|
void VEditWindow::handleTabCurrentHeaderChanged(const VHeaderPointer &p_header)
|
||||||
{
|
{
|
||||||
// Only propagate it if it is current tab
|
// Only propagate it if it is current tab.
|
||||||
int idx = currentIndex();
|
VEditTab *tab = getCurrentTab();
|
||||||
if (idx == -1) {
|
if (tab) {
|
||||||
emit curHeaderChanged(VAnchor());
|
if (tab->getFile() == p_header.m_file) {
|
||||||
return;
|
emit currentHeaderChanged(p_header);
|
||||||
}
|
}
|
||||||
const VFile *file = getTab(idx)->getFile();
|
} else {
|
||||||
if (p_anchor.m_file == file) {
|
emit currentHeaderChanged(VHeaderPointer());
|
||||||
emit curHeaderChanged(p_anchor);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditWindow::scrollCurTab(const VAnchor &p_anchor)
|
void VEditWindow::scrollToHeader(const VHeaderPointer &p_header)
|
||||||
{
|
{
|
||||||
int idx = currentIndex();
|
VEditTab *tab = getCurrentTab();
|
||||||
if (idx == -1) {
|
if (tab) {
|
||||||
emit curHeaderChanged(VAnchor());
|
tab->scrollToHeader(p_header);
|
||||||
return;
|
|
||||||
}
|
|
||||||
const VFile *file = getTab(idx)->getFile();
|
|
||||||
if (file == p_anchor.m_file) {
|
|
||||||
getTab(idx)->scrollToAnchor(p_anchor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,9 +909,9 @@ void VEditWindow::connectEditTab(const VEditTab *p_tab)
|
|||||||
connect(p_tab, &VEditTab::getFocused,
|
connect(p_tab, &VEditTab::getFocused,
|
||||||
this, &VEditWindow::getFocused);
|
this, &VEditWindow::getFocused);
|
||||||
connect(p_tab, &VEditTab::outlineChanged,
|
connect(p_tab, &VEditTab::outlineChanged,
|
||||||
this, &VEditWindow::handleOutlineChanged);
|
this, &VEditWindow::handleTabOutlineChanged);
|
||||||
connect(p_tab, &VEditTab::curHeaderChanged,
|
connect(p_tab, &VEditTab::currentHeaderChanged,
|
||||||
this, &VEditWindow::handleCurHeaderChanged);
|
this, &VEditWindow::handleTabCurrentHeaderChanged);
|
||||||
connect(p_tab, &VEditTab::statusUpdated,
|
connect(p_tab, &VEditTab::statusUpdated,
|
||||||
this, &VEditWindow::handleTabStatusUpdated);
|
this, &VEditWindow::handleTabStatusUpdated);
|
||||||
connect(p_tab, &VEditTab::statusMessage,
|
connect(p_tab, &VEditTab::statusMessage,
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
#include "vtoc.h"
|
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vnotefile.h"
|
#include "vnotefile.h"
|
||||||
|
|
||||||
@ -32,11 +31,19 @@ public:
|
|||||||
void readFile();
|
void readFile();
|
||||||
void saveAndReadFile();
|
void saveAndReadFile();
|
||||||
bool closeAllFiles(bool p_forced);
|
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
|
// Focus to current tab's editor
|
||||||
void focusWindow();
|
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 updateFileInfo(const VFile *p_file);
|
||||||
void updateDirectoryInfo(const VDirectory *p_dir);
|
void updateDirectoryInfo(const VDirectory *p_dir);
|
||||||
void updateNotebookInfo(const VNotebook *p_notebook);
|
void updateNotebookInfo(const VNotebook *p_notebook);
|
||||||
@ -84,8 +91,10 @@ 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);
|
|
||||||
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.
|
// Emit when want to show message in status bar.
|
||||||
void statusMessage(const QString &p_msg);
|
void statusMessage(const QString &p_msg);
|
||||||
@ -105,8 +114,11 @@ private slots:
|
|||||||
void handleCurrentIndexChanged(int p_index);
|
void handleCurrentIndexChanged(int p_index);
|
||||||
void contextMenuRequested(QPoint pos);
|
void contextMenuRequested(QPoint pos);
|
||||||
void tabListJump(VFile *p_file);
|
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 updateSplitMenu();
|
||||||
void tabbarContextMenuRequested(QPoint p_pos);
|
void tabbarContextMenuRequested(QPoint p_pos);
|
||||||
void handleLocateAct();
|
void handleLocateAct();
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#include "vedittabinfo.h"
|
#include "vedittabinfo.h"
|
||||||
#include "vtoc.h"
|
#include "vtableofcontent.h"
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
|
|
||||||
|
|
||||||
VFileSessionInfo::VFileSessionInfo()
|
VFileSessionInfo::VFileSessionInfo()
|
||||||
: m_mode(OpenFileMode::Read),
|
: m_mode(OpenFileMode::Read),
|
||||||
m_anchorIndex(-1),
|
m_headerIndex(-1),
|
||||||
m_cursorBlockNumber(-1),
|
m_cursorBlockNumber(-1),
|
||||||
m_cursorPositionInBlock(-1)
|
m_cursorPositionInBlock(-1)
|
||||||
{
|
{
|
||||||
@ -19,7 +19,7 @@ VFileSessionInfo::VFileSessionInfo(const QString &p_file,
|
|||||||
OpenFileMode p_mode)
|
OpenFileMode p_mode)
|
||||||
: m_file(p_file),
|
: m_file(p_file),
|
||||||
m_mode(p_mode),
|
m_mode(p_mode),
|
||||||
m_anchorIndex(-1),
|
m_headerIndex(-1),
|
||||||
m_cursorBlockNumber(-1),
|
m_cursorBlockNumber(-1),
|
||||||
m_cursorPositionInBlock(-1)
|
m_cursorPositionInBlock(-1)
|
||||||
{
|
{
|
||||||
@ -32,7 +32,7 @@ VFileSessionInfo VFileSessionInfo::fromEditTabInfo(const VEditTabInfo *p_tabInfo
|
|||||||
VEditTab *tab = p_tabInfo->m_editTab;
|
VEditTab *tab = p_tabInfo->m_editTab;
|
||||||
VFileSessionInfo info(tab->getFile()->fetchPath(),
|
VFileSessionInfo info(tab->getFile()->fetchPath(),
|
||||||
tab->isEditMode() ? OpenFileMode::Edit : OpenFileMode::Read);
|
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_cursorBlockNumber = p_tabInfo->m_cursorBlockNumber;
|
||||||
info.m_cursorPositionInBlock = p_tabInfo->m_cursorPositionInBlock;
|
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
|
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_cursorBlockNumber = m_cursorBlockNumber;
|
||||||
p_tabInfo->m_cursorPositionInBlock = m_cursorPositionInBlock;
|
p_tabInfo->m_cursorPositionInBlock = m_cursorPositionInBlock;
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ VFileSessionInfo VFileSessionInfo::fromSettings(const QSettings *p_settings)
|
|||||||
info.m_mode = OpenFileMode::Read;
|
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_cursorBlockNumber = p_settings->value(FileSessionConfig::c_cursorBlockNumber).toInt();
|
||||||
info.m_cursorPositionInBlock = p_settings->value(FileSessionConfig::c_cursorPositionInBlock).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_file, m_file);
|
||||||
p_settings->setValue(FileSessionConfig::c_mode, (int)m_mode);
|
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_cursorBlockNumber, m_cursorBlockNumber);
|
||||||
p_settings->setValue(FileSessionConfig::c_cursorPositionInBlock, m_cursorPositionInBlock);
|
p_settings->setValue(FileSessionConfig::c_cursorPositionInBlock, m_cursorPositionInBlock);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace FileSessionConfig
|
|||||||
static const QString c_mode = "mode";
|
static const QString c_mode = "mode";
|
||||||
|
|
||||||
// Index in outline of the anchor.
|
// 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_cursorBlockNumber = "cursor_block_number";
|
||||||
static const QString c_cursorPositionInBlock = "cursor_position_in_block";
|
static const QString c_cursorPositionInBlock = "cursor_position_in_block";
|
||||||
@ -44,8 +44,8 @@ public:
|
|||||||
// Mode of this file in this session.
|
// Mode of this file in this session.
|
||||||
OpenFileMode m_mode;
|
OpenFileMode m_mode;
|
||||||
|
|
||||||
// Index in outline of the anchor.
|
// Index in outline of the header.
|
||||||
int m_anchorIndex;
|
int m_headerIndex;
|
||||||
|
|
||||||
// Block number of cursor block.
|
// Block number of cursor block.
|
||||||
int m_cursorBlockNumber;
|
int m_cursorBlockNumber;
|
||||||
|
@ -32,7 +32,7 @@ void VHtmlTab::setupUI()
|
|||||||
{
|
{
|
||||||
m_editor = new VEdit(m_file, this);
|
m_editor = new VEdit(m_file, this);
|
||||||
connect(m_editor, &VEdit::textChanged,
|
connect(m_editor, &VEdit::textChanged,
|
||||||
this, &VHtmlTab::handleTextChanged);
|
this, &VHtmlTab::updateStatus);
|
||||||
connect(m_editor, &VEdit::saveAndRead,
|
connect(m_editor, &VEdit::saveAndRead,
|
||||||
this, &VHtmlTab::saveAndRead);
|
this, &VHtmlTab::saveAndRead);
|
||||||
connect(m_editor, &VEdit::discardAndRead,
|
connect(m_editor, &VEdit::discardAndRead,
|
||||||
@ -52,17 +52,6 @@ void VHtmlTab::setupUI()
|
|||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VHtmlTab::handleTextChanged()
|
|
||||||
{
|
|
||||||
V_ASSERT(m_file->isModifiable());
|
|
||||||
|
|
||||||
if (m_modified) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VHtmlTab::showFileReadMode()
|
void VHtmlTab::showFileReadMode()
|
||||||
{
|
{
|
||||||
m_isEditMode = false;
|
m_isEditMode = false;
|
||||||
@ -194,10 +183,6 @@ void VHtmlTab::discardAndRead()
|
|||||||
readFile();
|
readFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VHtmlTab::scrollToAnchor(const VAnchor & /* p_anchor */)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void VHtmlTab::insertImage()
|
void VHtmlTab::insertImage()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,6 @@ public:
|
|||||||
// Save file.
|
// Save file.
|
||||||
bool saveFile() Q_DECL_OVERRIDE;
|
bool saveFile() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
// Scroll to anchor @p_anchor.
|
|
||||||
void scrollToAnchor(const VAnchor& p_anchor) Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
void insertImage() Q_DECL_OVERRIDE;
|
void insertImage() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
// Search @p_text in current note.
|
// Search @p_text in current note.
|
||||||
@ -53,9 +50,6 @@ public slots:
|
|||||||
void editFile() Q_DECL_OVERRIDE;
|
void editFile() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Handle text changed in m_editor.
|
|
||||||
void handleTextChanged();
|
|
||||||
|
|
||||||
// m_editor requests to save changes and enter read mode.
|
// m_editor requests to save changes and enter read mode.
|
||||||
void saveAndRead();
|
void saveAndRead();
|
||||||
|
|
||||||
|
@ -1100,13 +1100,16 @@ void VMainWindow::initDockWindows()
|
|||||||
toolDock->setObjectName("tools_dock");
|
toolDock->setObjectName("tools_dock");
|
||||||
toolDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
toolDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||||
toolBox = new QToolBox(this);
|
toolBox = new QToolBox(this);
|
||||||
|
|
||||||
|
// Outline tree.
|
||||||
outline = new VOutline(this);
|
outline = new VOutline(this);
|
||||||
connect(editArea, &VEditArea::outlineChanged,
|
connect(editArea, &VEditArea::outlineChanged,
|
||||||
outline, &VOutline::updateOutline);
|
outline, &VOutline::updateOutline);
|
||||||
|
connect(editArea, &VEditArea::currentHeaderChanged,
|
||||||
|
outline, &VOutline::updateCurrentHeader);
|
||||||
connect(outline, &VOutline::outlineItemActivated,
|
connect(outline, &VOutline::outlineItemActivated,
|
||||||
editArea, &VEditArea::handleOutlineItemActivated);
|
editArea, &VEditArea::scrollToHeader);
|
||||||
connect(editArea, &VEditArea::curHeaderChanged,
|
|
||||||
outline, &VOutline::updateCurHeader);
|
|
||||||
toolBox->addItem(outline, QIcon(":/resources/icons/outline.svg"), tr("Outline"));
|
toolBox->addItem(outline, QIcon(":/resources/icons/outline.svg"), tr("Outline"));
|
||||||
toolDock->setWidget(toolBox);
|
toolDock->setWidget(toolBox);
|
||||||
addDockWidget(Qt::RightDockWidgetArea, toolDock);
|
addDockWidget(Qt::RightDockWidgetArea, toolDock);
|
||||||
|
@ -145,6 +145,7 @@ private slots:
|
|||||||
void handleVimStatusUpdated(const VVim *p_vim);
|
void handleVimStatusUpdated(const VVim *p_vim);
|
||||||
|
|
||||||
// Handle the status update of the current tab of VEditArea.
|
// Handle the status update of the current tab of VEditArea.
|
||||||
|
// Will be called frequently.
|
||||||
void handleAreaTabStatusUpdated(const VEditTabInfo &p_info);
|
void handleAreaTabStatusUpdated(const VEditTabInfo &p_info);
|
||||||
|
|
||||||
// Check the shared memory between different instances to see if we have
|
// Check the shared memory between different instances to see if we have
|
||||||
|
142
src/vmdedit.cpp
142
src/vmdedit.cpp
@ -5,7 +5,7 @@
|
|||||||
#include "vmdeditoperations.h"
|
#include "vmdeditoperations.h"
|
||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vtoc.h"
|
#include "vtableofcontent.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "utils/veditutils.h"
|
#include "utils/veditutils.h"
|
||||||
#include "utils/vpreviewutils.h"
|
#include "utils/vpreviewutils.h"
|
||||||
@ -35,7 +35,7 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
document());
|
document());
|
||||||
|
|
||||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::headersUpdated,
|
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
|
// After highlight, the cursor may trun into non-visible. We should make it visible
|
||||||
// in this case.
|
// in this case.
|
||||||
@ -74,7 +74,7 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
this, &VEdit::vimStatusUpdated);
|
this, &VEdit::vimStatusUpdated);
|
||||||
|
|
||||||
connect(this, &VMdEdit::cursorPositionChanged,
|
connect(this, &VMdEdit::cursorPositionChanged,
|
||||||
this, &VMdEdit::updateCurHeader);
|
this, &VMdEdit::updateCurrentHeader);
|
||||||
|
|
||||||
connect(QApplication::clipboard(), &QClipboard::changed,
|
connect(QApplication::clipboard(), &QClipboard::changed,
|
||||||
this, &VMdEdit::handleClipboardChanged);
|
this, &VMdEdit::handleClipboardChanged);
|
||||||
@ -111,7 +111,7 @@ void VMdEdit::beginEdit()
|
|||||||
setReadOnly(false);
|
setReadOnly(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOutline(m_mdHighlighter->getHeaderRegions());
|
updateHeaders(m_mdHighlighter->getHeaderRegions());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEdit::endEdit()
|
void VMdEdit::endEdit()
|
||||||
@ -345,43 +345,9 @@ void VMdEdit::clearUnusedImages()
|
|||||||
m_initImages.clear();
|
m_initImages.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int VMdEdit::currentCursorHeader() const
|
void VMdEdit::updateCurrentHeader()
|
||||||
{
|
{
|
||||||
if (m_headers.isEmpty()) {
|
emit currentHeaderChanged(textCursor().block().blockNumber());
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addHeaderSequence(QVector<int> &p_sequence, int p_level, int p_baseLevel)
|
static void addHeaderSequence(QVector<int> &p_sequence, int p_level, int p_baseLevel)
|
||||||
@ -448,11 +414,11 @@ static void insertSequenceToHeader(QTextBlock p_block,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEdit::updateOutline(const QVector<VElementRegion> &p_headerRegions)
|
void VMdEdit::updateHeaders(const QVector<VElementRegion> &p_headerRegions)
|
||||||
{
|
{
|
||||||
QTextDocument *doc = document();
|
QTextDocument *doc = document();
|
||||||
|
|
||||||
QVector<VHeader> headers;
|
QVector<VTableOfContentItem> headers;
|
||||||
QVector<int> headerBlockNumbers;
|
QVector<int> headerBlockNumbers;
|
||||||
QVector<QString> headerSequences;
|
QVector<QString> headerSequences;
|
||||||
if (!p_headerRegions.isEmpty()) {
|
if (!p_headerRegions.isEmpty()) {
|
||||||
@ -480,8 +446,10 @@ void VMdEdit::updateOutline(const QVector<VElementRegion> &p_headerRegions)
|
|||||||
if ((block.userState() == HighlightBlockState::Normal) &&
|
if ((block.userState() == HighlightBlockState::Normal) &&
|
||||||
headerReg.exactMatch(block.text())) {
|
headerReg.exactMatch(block.text())) {
|
||||||
int level = headerReg.cap(1).length();
|
int level = headerReg.cap(1).length();
|
||||||
VHeader header(level, headerReg.cap(2).trimmed(),
|
VTableOfContentItem header(headerReg.cap(2).trimmed(),
|
||||||
"", block.firstLineNumber(), headers.size());
|
level,
|
||||||
|
block.blockNumber(),
|
||||||
|
headers.size());
|
||||||
headers.append(header);
|
headers.append(header);
|
||||||
headerBlockNumbers.append(block.blockNumber());
|
headerBlockNumbers.append(block.blockNumber());
|
||||||
headerSequences.append(headerReg.cap(3));
|
headerSequences.append(headerReg.cap(3));
|
||||||
@ -506,22 +474,25 @@ void VMdEdit::updateOutline(const QVector<VElementRegion> &p_headerRegions)
|
|||||||
QRegExp preReg(VUtils::c_headerPrefixRegExp);
|
QRegExp preReg(VUtils::c_headerPrefixRegExp);
|
||||||
int curLevel = baseLevel - 1;
|
int curLevel = baseLevel - 1;
|
||||||
for (int i = 0; i < headers.size(); ++i) {
|
for (int i = 0; i < headers.size(); ++i) {
|
||||||
VHeader &item = headers[i];
|
VTableOfContentItem &item = headers[i];
|
||||||
while (item.level > curLevel + 1) {
|
while (item.m_level > curLevel + 1) {
|
||||||
curLevel += 1;
|
curLevel += 1;
|
||||||
|
|
||||||
// Insert empty level which is an invalid header.
|
// 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) {
|
if (autoSequence) {
|
||||||
addHeaderSequence(seqs, curLevel, headingSequenceBaseLevel);
|
addHeaderSequence(seqs, curLevel, headingSequenceBaseLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item.index = m_headers.size();
|
item.m_index = m_headers.size();
|
||||||
m_headers.append(item);
|
m_headers.append(item);
|
||||||
curLevel = item.level;
|
curLevel = item.m_level;
|
||||||
if (autoSequence) {
|
if (autoSequence) {
|
||||||
addHeaderSequence(seqs, item.level, headingSequenceBaseLevel);
|
addHeaderSequence(seqs, item.m_level, headingSequenceBaseLevel);
|
||||||
|
|
||||||
QString seqStr = headerSequenceStr(seqs);
|
QString seqStr = headerSequenceStr(seqs);
|
||||||
if (headerSequences[i] != seqStr) {
|
if (headerSequences[i] != seqStr) {
|
||||||
@ -536,41 +507,18 @@ void VMdEdit::updateOutline(const QVector<VElementRegion> &p_headerRegions)
|
|||||||
|
|
||||||
emit headersChanged(m_headers);
|
emit headersChanged(m_headers);
|
||||||
|
|
||||||
updateCurHeader();
|
updateCurrentHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEdit::scrollToAnchor(const VAnchor &p_anchor)
|
bool VMdEdit::scrollToHeader(int p_blockNumber)
|
||||||
{
|
{
|
||||||
if (p_anchor.lineNumber == -1
|
if (p_blockNumber < 0) {
|
||||||
|| 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return scrollToBlock(p_blockNumber);
|
||||||
|
}
|
||||||
|
|
||||||
QString VMdEdit::toPlainTextWithoutImg()
|
QString VMdEdit::toPlainTextWithoutImg()
|
||||||
{
|
{
|
||||||
QString text;
|
QString text;
|
||||||
@ -742,9 +690,21 @@ void VMdEdit::resizeEvent(QResizeEvent *p_event)
|
|||||||
VEdit::resizeEvent(p_event);
|
VEdit::resizeEvent(p_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVector<VHeader> &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)
|
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();
|
QTextCursor cursor = textCursor();
|
||||||
int cursorLine = cursor.block().firstLineNumber();
|
int cursorLine = cursor.block().blockNumber();
|
||||||
int targetIdx = -1;
|
int targetIdx = -1;
|
||||||
// -1: skip level check.
|
// -1: skip level check.
|
||||||
int targetLevel = 0;
|
int targetLevel = 0;
|
||||||
int idx = currentCursorHeader();
|
int idx = indexOfCurrentHeader();
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
// Cursor locates at the beginning, before any headers.
|
// Cursor locates at the beginning, before any headers.
|
||||||
if (p_relativeLevel < 0 || !p_forward) {
|
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;
|
for (targetIdx = idx == -1 ? 0 : idx;
|
||||||
targetIdx >= 0 && targetIdx < m_headers.size();
|
targetIdx >= 0 && targetIdx < m_headers.size();
|
||||||
targetIdx += delta) {
|
targetIdx += delta) {
|
||||||
const VHeader &header = m_headers[targetIdx];
|
const VTableOfContentItem &header = m_headers[targetIdx];
|
||||||
if (header.isEmpty()) {
|
if (header.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -783,7 +743,7 @@ bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat)
|
|||||||
if (targetLevel == 0) {
|
if (targetLevel == 0) {
|
||||||
// The target level has not been init yet.
|
// The target level has not been init yet.
|
||||||
Q_ASSERT(firstHeader);
|
Q_ASSERT(firstHeader);
|
||||||
targetLevel = header.level;
|
targetLevel = header.m_level;
|
||||||
if (p_relativeLevel < 0) {
|
if (p_relativeLevel < 0) {
|
||||||
targetLevel += p_relativeLevel;
|
targetLevel += p_relativeLevel;
|
||||||
if (targetLevel < 1) {
|
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
|
if (firstHeader
|
||||||
&& (cursorLine == header.lineNumber
|
&& (cursorLine == header.m_blockNumber
|
||||||
|| p_forward)
|
|| p_forward)
|
||||||
&& idx != -1) {
|
&& idx != -1) {
|
||||||
// This header is not counted for the repeat.
|
// 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.
|
// Found.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (header.level < targetLevel) {
|
} else if (header.m_level < targetLevel) {
|
||||||
// Stop by higher level.
|
// Stop by higher level.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -822,9 +782,9 @@ bool VMdEdit::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Jump to target header.
|
// Jump to target header.
|
||||||
int line = m_headers[targetIdx].lineNumber;
|
int line = m_headers[targetIdx].m_blockNumber;
|
||||||
if (line > -1) {
|
if (line > -1) {
|
||||||
QTextBlock block = document()->findBlockByLineNumber(line);
|
QTextBlock block = document()->findBlockByNumber(line);
|
||||||
if (block.isValid()) {
|
if (block.isValid()) {
|
||||||
cursor.setPosition(block.position());
|
cursor.setPosition(block.position());
|
||||||
setTextCursor(cursor);
|
setTextCursor(cursor);
|
||||||
@ -851,7 +811,7 @@ void VMdEdit::finishOneAsyncJob(int p_idx)
|
|||||||
m_freshEdit = false;
|
m_freshEdit = false;
|
||||||
emit statusChanged();
|
emit statusChanged();
|
||||||
|
|
||||||
updateOutline(m_mdHighlighter->getHeaderRegions());
|
updateHeaders(m_mdHighlighter->getHeaderRegions());
|
||||||
|
|
||||||
emit ready();
|
emit ready();
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include "vtoc.h"
|
#include "vtableofcontent.h"
|
||||||
#include "veditoperations.h"
|
#include "veditoperations.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
@ -32,35 +32,34 @@ public:
|
|||||||
// @p_path is the absolute path of the inserted image.
|
// @p_path is the absolute path of the inserted image.
|
||||||
void imageInserted(const QString &p_path);
|
void imageInserted(const QString &p_path);
|
||||||
|
|
||||||
void scrollToAnchor(const VAnchor &p_anchor);
|
// Scroll to header @p_blockNumber.
|
||||||
|
// Return true if @p_blockNumber is valid to scroll to.
|
||||||
// Scroll to anchor given the the index in outline.
|
bool scrollToHeader(int p_blockNumber);
|
||||||
// Return true if @p_anchorIndex is valid.
|
|
||||||
bool scrollToAnchor(int p_anchorIndex);
|
|
||||||
|
|
||||||
// Like toPlainText(), but remove image preview characters.
|
// Like toPlainText(), but remove image preview characters.
|
||||||
QString toPlainTextWithoutImg();
|
QString toPlainTextWithoutImg();
|
||||||
|
|
||||||
const QVector<VHeader> &getHeaders() const;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
|
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void headersChanged(const QVector<VHeader> &headers);
|
// Signal when headers change.
|
||||||
|
void headersChanged(const QVector<VTableOfContentItem> &p_headers);
|
||||||
|
|
||||||
// Signal when current header change.
|
// Signal when current header change.
|
||||||
void curHeaderChanged(VAnchor p_anchor);
|
void currentHeaderChanged(int p_blockNumber);
|
||||||
|
|
||||||
// Signal when the status of VMdEdit changed.
|
// Signal when the status of VMdEdit changed.
|
||||||
// Will be emitted by VImagePreviewer for now.
|
// Will be emitted by VImagePreviewer for now.
|
||||||
void statusChanged();
|
void statusChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateOutline(const QVector<VElementRegion> &p_headerRegions);
|
// Update m_headers according to elements.
|
||||||
|
void updateHeaders(const QVector<VElementRegion> &p_headerRegions);
|
||||||
|
|
||||||
|
// Update current header according to cursor position.
|
||||||
// When there is no header in current cursor, will signal an invalid header.
|
// When there is no header in current cursor, will signal an invalid header.
|
||||||
void updateCurHeader();
|
void updateCurrentHeader();
|
||||||
|
|
||||||
void handleClipboardChanged(QClipboard::Mode p_mode);
|
void handleClipboardChanged(QClipboard::Mode p_mode);
|
||||||
|
|
||||||
@ -99,9 +98,6 @@ private:
|
|||||||
// in the selection. Get the QImage.
|
// in the selection. Get the QImage.
|
||||||
QImage tryGetSelectedImage();
|
QImage tryGetSelectedImage();
|
||||||
|
|
||||||
// Return the header index in m_headers where current cursor locates.
|
|
||||||
int currentCursorHeader() const;
|
|
||||||
|
|
||||||
QString getPlainTextWithoutPreviewImage() const;
|
QString getPlainTextWithoutPreviewImage() const;
|
||||||
|
|
||||||
// Try to get all the regions of preview image within @p_block.
|
// Try to get all the regions of preview image within @p_block.
|
||||||
@ -111,6 +107,9 @@ private:
|
|||||||
|
|
||||||
void finishOneAsyncJob(int p_idx);
|
void finishOneAsyncJob(int p_idx);
|
||||||
|
|
||||||
|
// Index in m_headers of current header which contains the cursor.
|
||||||
|
int indexOfCurrentHeader() const;
|
||||||
|
|
||||||
HGMarkdownHighlighter *m_mdHighlighter;
|
HGMarkdownHighlighter *m_mdHighlighter;
|
||||||
VCodeBlockHighlightHelper *m_cbHighlighter;
|
VCodeBlockHighlightHelper *m_cbHighlighter;
|
||||||
VImagePreviewer *m_imagePreviewer;
|
VImagePreviewer *m_imagePreviewer;
|
||||||
@ -121,7 +120,8 @@ private:
|
|||||||
// Image links right at the beginning of the edit.
|
// Image links right at the beginning of the edit.
|
||||||
QVector<ImageLink> m_initImages;
|
QVector<ImageLink> m_initImages;
|
||||||
|
|
||||||
QVector<VHeader> m_headers;
|
// Mainly used for title jump.
|
||||||
|
QVector<VTableOfContentItem> m_headers;
|
||||||
|
|
||||||
bool m_freshEdit;
|
bool m_freshEdit;
|
||||||
|
|
||||||
|
337
src/vmdtab.cpp
337
src/vmdtab.cpp
@ -11,7 +11,7 @@
|
|||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
#include "vtoc.h"
|
#include "vtableofcontent.h"
|
||||||
#include "vmdedit.h"
|
#include "vmdedit.h"
|
||||||
#include "dialog/vfindreplacedialog.h"
|
#include "dialog/vfindreplacedialog.h"
|
||||||
#include "veditarea.h"
|
#include "veditarea.h"
|
||||||
@ -50,72 +50,109 @@ void VMdTab::setupUI()
|
|||||||
setLayout(m_stacks);
|
setLayout(m_stacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::handleTextChanged()
|
|
||||||
{
|
|
||||||
V_ASSERT(m_file->isModifiable());
|
|
||||||
|
|
||||||
if (m_modified) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMdTab::showFileReadMode()
|
void VMdTab::showFileReadMode()
|
||||||
{
|
{
|
||||||
m_isEditMode = false;
|
m_isEditMode = false;
|
||||||
|
|
||||||
int outlineIndex = m_curHeader.m_outlineIndex;
|
VHeaderPointer header(m_currentHeader);
|
||||||
|
|
||||||
if (m_mdConType == MarkdownConverterType::Hoedown) {
|
if (m_mdConType == MarkdownConverterType::Hoedown) {
|
||||||
viewWebByConverter();
|
viewWebByConverter();
|
||||||
} else {
|
} else {
|
||||||
m_document->updateText();
|
m_document->updateText();
|
||||||
updateTocFromHtml(m_document->getToc());
|
updateOutlineFromHtml(m_document->getToc());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_stacks->setCurrentWidget(m_webViewer);
|
m_stacks->setCurrentWidget(m_webViewer);
|
||||||
clearSearchedWordHighlight();
|
clearSearchedWordHighlight();
|
||||||
|
|
||||||
scrollWebViewToAnchor(outlineIndex);
|
scrollWebViewToHeader(header);
|
||||||
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdTab::scrollWebViewToAnchor(int p_anchorIndex, bool p_strict)
|
bool VMdTab::scrollWebViewToHeader(const VHeaderPointer &p_header)
|
||||||
{
|
{
|
||||||
QString anchor;
|
if (!m_outline.isMatched(p_header)
|
||||||
|
|| m_outline.getType() != VTableOfContentType::Anchor) {
|
||||||
VAnchor anch(m_file, anchor, -1, p_anchorIndex);
|
return false;
|
||||||
|
|
||||||
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 (validIndex || !p_strict) {
|
if (p_header.isValid()) {
|
||||||
m_curHeader = anch;
|
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<VMdEdit *>(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;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdTab::scrollToAnchor(int p_anchorIndex, bool p_strict)
|
bool VMdTab::scrollToHeaderInternal(const VHeaderPointer &p_header)
|
||||||
{
|
{
|
||||||
if (m_isEditMode) {
|
if (m_isEditMode) {
|
||||||
return dynamic_cast<VMdEdit *>(getEditor())->scrollToAnchor(p_anchorIndex);
|
return scrollEditorToHeader(p_header);
|
||||||
} else {
|
} else {
|
||||||
return scrollWebViewToAnchor(p_anchorIndex, p_strict);
|
return scrollWebViewToHeader(p_header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +164,7 @@ void VMdTab::viewWebByConverter()
|
|||||||
g_config->getMarkdownExtensions(),
|
g_config->getMarkdownExtensions(),
|
||||||
toc);
|
toc);
|
||||||
m_document->setHtml(html);
|
m_document->setHtml(html);
|
||||||
updateTocFromHtml(toc);
|
updateOutlineFromHtml(toc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::showFileEditMode()
|
void VMdTab::showFileEditMode()
|
||||||
@ -136,42 +173,28 @@ void VMdTab::showFileEditMode()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VHeaderPointer header(m_currentHeader);
|
||||||
|
|
||||||
m_isEditMode = true;
|
m_isEditMode = true;
|
||||||
|
|
||||||
VMdEdit *mdEdit = dynamic_cast<VMdEdit *>(getEditor());
|
VMdEdit *mdEdit = dynamic_cast<VMdEdit *>(getEditor());
|
||||||
V_ASSERT(mdEdit);
|
V_ASSERT(mdEdit);
|
||||||
|
|
||||||
// beginEdit() may change m_curHeader.
|
|
||||||
int outlineIndex = m_curHeader.m_outlineIndex;
|
|
||||||
|
|
||||||
mdEdit->beginEdit();
|
mdEdit->beginEdit();
|
||||||
m_stacks->setCurrentWidget(mdEdit);
|
m_stacks->setCurrentWidget(mdEdit);
|
||||||
|
|
||||||
int lineNumber = -1;
|
|
||||||
const QVector<VHeader> &headers = mdEdit->getHeaders();
|
|
||||||
// If editor is not init, we need to wait for it to init headers.
|
// If editor is not init, we need to wait for it to init headers.
|
||||||
// Generally, beginEdit() will generate the headers. Wait is needed when
|
// Generally, beginEdit() will generate the headers. Wait is needed when
|
||||||
// highlight completion is going to re-generate the headers.
|
// highlight completion is going to re-generate the headers.
|
||||||
int nrRetry = 5;
|
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";
|
qDebug() << "wait another 100 ms for editor's headers ready";
|
||||||
VUtils::sleepWait(100);
|
VUtils::sleepWait(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outlineIndex < 0 || outlineIndex >= headers.size()) {
|
scrollEditorToHeader(header);
|
||||||
lineNumber = -1;
|
|
||||||
outlineIndex = -1;
|
|
||||||
} else {
|
|
||||||
lineNumber = headers[outlineIndex].lineNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
VAnchor anchor(m_file, "", lineNumber, outlineIndex);
|
|
||||||
|
|
||||||
mdEdit->scrollToAnchor(anchor);
|
|
||||||
|
|
||||||
mdEdit->setFocus();
|
mdEdit->setFocus();
|
||||||
|
|
||||||
updateStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdTab::closeFile(bool p_forced)
|
bool VMdTab::closeFile(bool p_forced)
|
||||||
@ -273,8 +296,6 @@ bool VMdTab::saveFile()
|
|||||||
m_editor->setModified(true);
|
m_editor->setModified(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatus();
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,9 +325,9 @@ void VMdTab::setupMarkdownViewer()
|
|||||||
QWebChannel *channel = new QWebChannel(m_webViewer);
|
QWebChannel *channel = new QWebChannel(m_webViewer);
|
||||||
channel->registerObject(QStringLiteral("content"), m_document);
|
channel->registerObject(QStringLiteral("content"), m_document);
|
||||||
connect(m_document, &VDocument::tocChanged,
|
connect(m_document, &VDocument::tocChanged,
|
||||||
this, &VMdTab::updateTocFromHtml);
|
this, &VMdTab::updateOutlineFromHtml);
|
||||||
connect(m_document, SIGNAL(headerChanged(const QString &)),
|
connect(m_document, SIGNAL(headerChanged(const QString &)),
|
||||||
this, SLOT(updateCurHeader(const QString &)));
|
this, SLOT(updateCurrentHeader(const QString &)));
|
||||||
connect(m_document, &VDocument::keyPressed,
|
connect(m_document, &VDocument::keyPressed,
|
||||||
this, &VMdTab::handleWebKeyPressed);
|
this, &VMdTab::handleWebKeyPressed);
|
||||||
connect(m_document, SIGNAL(logicsFinished(void)),
|
connect(m_document, SIGNAL(logicsFinished(void)),
|
||||||
@ -325,13 +346,13 @@ void VMdTab::setupMarkdownEditor()
|
|||||||
qDebug() << "create Markdown editor";
|
qDebug() << "create Markdown editor";
|
||||||
m_editor = new VMdEdit(m_file, m_document, m_mdConType, this);
|
m_editor = new VMdEdit(m_file, m_document, m_mdConType, this);
|
||||||
connect(dynamic_cast<VMdEdit *>(m_editor), &VMdEdit::headersChanged,
|
connect(dynamic_cast<VMdEdit *>(m_editor), &VMdEdit::headersChanged,
|
||||||
this, &VMdTab::updateTocFromHeaders);
|
this, &VMdTab::updateOutlineFromHeaders);
|
||||||
|
connect(dynamic_cast<VMdEdit *>(m_editor), SIGNAL(currentHeaderChanged(int)),
|
||||||
|
this, SLOT(updateCurrentHeader(int)));
|
||||||
connect(dynamic_cast<VMdEdit *>(m_editor), &VMdEdit::statusChanged,
|
connect(dynamic_cast<VMdEdit *>(m_editor), &VMdEdit::statusChanged,
|
||||||
this, &VMdTab::updateStatus);
|
this, &VMdTab::updateStatus);
|
||||||
connect(m_editor, SIGNAL(curHeaderChanged(VAnchor)),
|
|
||||||
this, SLOT(updateCurHeader(VAnchor)));
|
|
||||||
connect(m_editor, &VEdit::textChanged,
|
connect(m_editor, &VEdit::textChanged,
|
||||||
this, &VMdTab::handleTextChanged);
|
this, &VMdTab::updateStatus);
|
||||||
connect(m_editor, &VEdit::cursorPositionChanged,
|
connect(m_editor, &VEdit::cursorPositionChanged,
|
||||||
this, &VMdTab::updateStatus);
|
this, &VMdTab::updateStatus);
|
||||||
connect(m_editor, &VEdit::saveAndRead,
|
connect(m_editor, &VEdit::saveAndRead,
|
||||||
@ -355,185 +376,71 @@ void VMdTab::setupMarkdownEditor()
|
|||||||
m_stacks->addWidget(m_editor);
|
m_stacks->addWidget(m_editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parseTocUl(QXmlStreamReader &p_xml, QVector<VHeader> &p_headers,
|
void VMdTab::updateOutlineFromHtml(const QString &p_tocHtml)
|
||||||
int p_level);
|
|
||||||
|
|
||||||
static void parseTocLi(QXmlStreamReader &p_xml, QVector<VHeader> &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 <a> should be ended by </a>" << 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 <li> should contain <a> or <ul>" << p_xml.name();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (p_xml.readNext()) {
|
|
||||||
if (p_xml.isEndElement()) {
|
|
||||||
if (p_xml.name() == "li") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (p_xml.name() == "ul") {
|
|
||||||
// Nested unordered list
|
|
||||||
parseTocUl(p_xml, p_headers, p_level + 1);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void parseTocUl(QXmlStreamReader &p_xml, QVector<VHeader> &p_headers,
|
|
||||||
int p_level)
|
|
||||||
{
|
|
||||||
Q_ASSERT(p_xml.isStartElement() && p_xml.name() == "ul");
|
|
||||||
|
|
||||||
while (p_xml.readNextStartElement()) {
|
|
||||||
if (p_xml.name() == "li") {
|
|
||||||
parseTocLi(p_xml, p_headers, p_level);
|
|
||||||
} else {
|
|
||||||
qWarning() << "TOC HTML <ul> should contain <li>" << p_xml.name();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool parseTocHtml(const QString &p_tocHtml,
|
|
||||||
QVector<VHeader> &p_headers)
|
|
||||||
{
|
|
||||||
if (!p_tocHtml.isEmpty()) {
|
|
||||||
QXmlStreamReader xml(p_tocHtml);
|
|
||||||
if (xml.readNextStartElement()) {
|
|
||||||
if (xml.name() == "ul") {
|
|
||||||
parseTocUl(xml, p_headers, 1);
|
|
||||||
} else {
|
|
||||||
qWarning() << "TOC HTML does not start with <ul>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xml.hasError()) {
|
|
||||||
qWarning() << "fail to parse TOC in HTML";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMdTab::updateTocFromHtml(const QString &p_tocHtml)
|
|
||||||
{
|
{
|
||||||
if (m_isEditMode) {
|
if (m_isEditMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_toc.type = VHeaderType::Anchor;
|
m_outline.clear();
|
||||||
m_toc.headers.clear();
|
|
||||||
|
|
||||||
if (!parseTocHtml(p_tocHtml, m_toc.headers)) {
|
if (m_outline.parseTableFromHtml(p_tocHtml)) {
|
||||||
return;
|
m_outline.setFile(m_file);
|
||||||
|
m_outline.setType(VTableOfContentType::Anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_toc.m_file = m_file;
|
m_currentHeader.reset();
|
||||||
m_toc.valid = true;
|
|
||||||
|
|
||||||
emit outlineChanged(m_toc);
|
emit outlineChanged(m_outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::updateTocFromHeaders(const QVector<VHeader> &p_headers)
|
void VMdTab::updateOutlineFromHeaders(const QVector<VTableOfContentItem> &p_headers)
|
||||||
{
|
{
|
||||||
if (!m_isEditMode) {
|
if (!m_isEditMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_toc.type = VHeaderType::LineNumber;
|
m_outline.update(m_file,
|
||||||
m_toc.headers = p_headers;
|
p_headers,
|
||||||
m_toc.m_file = m_file;
|
VTableOfContentType::BlockNumber);
|
||||||
m_toc.valid = true;
|
|
||||||
|
|
||||||
// Clear current header.
|
m_currentHeader.reset();
|
||||||
m_curHeader = VAnchor(m_file, "", -1, -1);
|
|
||||||
emit curHeaderChanged(m_curHeader);
|
|
||||||
|
|
||||||
emit outlineChanged(m_toc);
|
emit outlineChanged(m_outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::scrollToAnchor(const VAnchor &p_anchor)
|
void VMdTab::scrollToHeader(const VHeaderPointer &p_header)
|
||||||
{
|
{
|
||||||
if (p_anchor == m_curHeader) {
|
if (m_outline.isMatched(p_header)) {
|
||||||
return;
|
// Scroll only when @p_header is valid.
|
||||||
}
|
scrollToHeaderInternal(p_header);
|
||||||
|
|
||||||
m_curHeader = p_anchor;
|
|
||||||
|
|
||||||
if (m_isEditMode) {
|
|
||||||
dynamic_cast<VMdEdit *>(getEditor())->scrollToAnchor(p_anchor);
|
|
||||||
} else {
|
|
||||||
if (!p_anchor.anchor.isEmpty()) {
|
|
||||||
m_document->scrollToAnchor(p_anchor.anchor.mid(1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::updateCurHeader(const QString &p_anchor)
|
void VMdTab::updateCurrentHeader(const QString &p_anchor)
|
||||||
{
|
|
||||||
if (m_isEditMode || m_curHeader.anchor.mid(1) == p_anchor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_curHeader = VAnchor(m_file, "#" + p_anchor, -1);
|
|
||||||
if (!p_anchor.isEmpty()) {
|
|
||||||
const QVector<VHeader> &headers = m_toc.headers;
|
|
||||||
for (int i = 0; i < headers.size(); ++i) {
|
|
||||||
if (headers[i].anchor == m_curHeader.anchor) {
|
|
||||||
V_ASSERT(headers[i].index == i);
|
|
||||||
m_curHeader.m_outlineIndex = headers[i].index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit curHeaderChanged(m_curHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMdTab::updateCurHeader(VAnchor p_anchor)
|
|
||||||
{
|
{
|
||||||
if (m_isEditMode) {
|
if (m_isEditMode) {
|
||||||
if (!p_anchor.anchor.isEmpty() || p_anchor.lineNumber == m_curHeader.lineNumber) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (p_anchor.lineNumber != -1 || p_anchor.anchor == m_curHeader.anchor) {
|
// Find the index of the anchor in outline.
|
||||||
|
int idx = m_outline.indexOfItemByAnchor(p_anchor);
|
||||||
|
m_currentHeader.update(m_file, idx);
|
||||||
|
|
||||||
|
emit currentHeaderChanged(m_currentHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdTab::updateCurrentHeader(int p_blockNumber)
|
||||||
|
{
|
||||||
|
if (!m_isEditMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
m_curHeader = p_anchor;
|
// Find the index of the block number in outline.
|
||||||
|
int idx = m_outline.indexOfItemByBlockNumber(p_blockNumber);
|
||||||
|
m_currentHeader.update(m_file, idx);
|
||||||
|
|
||||||
emit curHeaderChanged(m_curHeader);
|
emit currentHeaderChanged(m_currentHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::insertImage()
|
void VMdTab::insertImage()
|
||||||
@ -705,7 +612,7 @@ void VMdTab::requestUpdateVimStatus()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VEditTabInfo VMdTab::fetchTabInfo()
|
VEditTabInfo VMdTab::fetchTabInfo() const
|
||||||
{
|
{
|
||||||
VEditTabInfo info = VEditTab::fetchTabInfo();
|
VEditTabInfo info = VEditTab::fetchTabInfo();
|
||||||
|
|
||||||
@ -716,7 +623,7 @@ VEditTabInfo VMdTab::fetchTabInfo()
|
|||||||
info.m_blockCount = m_editor->document()->blockCount();
|
info.m_blockCount = m_editor->document()->blockCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
info.m_anchorIndex = m_curHeader.m_outlineIndex;
|
info.m_headerIndex = m_currentHeader.m_index;
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@ -730,15 +637,13 @@ void VMdTab::decorateText(TextDecoration p_decoration)
|
|||||||
|
|
||||||
bool VMdTab::restoreFromTabInfo(const VEditTabInfo &p_info)
|
bool VMdTab::restoreFromTabInfo(const VEditTabInfo &p_info)
|
||||||
{
|
{
|
||||||
qDebug() << "restoreFromTabInfo" << p_info.m_anchorIndex;
|
|
||||||
if (p_info.m_editTab != this) {
|
if (p_info.m_editTab != this) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore anchor.
|
// Restore header.
|
||||||
int anchorIdx = p_info.m_anchorIndex;
|
VHeaderPointer header(m_file, p_info.m_headerIndex);
|
||||||
bool ret = scrollToAnchor(anchorIdx, true);
|
bool ret = scrollToHeaderInternal(header);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,5 +652,5 @@ void VMdTab::restoreFromTabInfo()
|
|||||||
restoreFromTabInfo(m_infoToRestore);
|
restoreFromTabInfo(m_infoToRestore);
|
||||||
|
|
||||||
// Clear it anyway.
|
// Clear it anyway.
|
||||||
m_infoToRestore.m_editTab = NULL;
|
m_infoToRestore.clear();
|
||||||
}
|
}
|
||||||
|
32
src/vmdtab.h
32
src/vmdtab.h
@ -31,8 +31,8 @@ public:
|
|||||||
// Save file.
|
// Save file.
|
||||||
bool saveFile() Q_DECL_OVERRIDE;
|
bool saveFile() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
// Scroll to anchor @p_anchor.
|
// Scroll to @p_header.
|
||||||
void scrollToAnchor(const VAnchor& p_anchor) Q_DECL_OVERRIDE;
|
void scrollToHeader(const VHeaderPointer &p_header) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
void insertImage() Q_DECL_OVERRIDE;
|
void insertImage() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
@ -63,27 +63,25 @@ public:
|
|||||||
void decorateText(TextDecoration p_decoration) Q_DECL_OVERRIDE;
|
void decorateText(TextDecoration p_decoration) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
// Create a filled VEditTabInfo.
|
// Create a filled VEditTabInfo.
|
||||||
VEditTabInfo fetchTabInfo() Q_DECL_OVERRIDE;
|
VEditTabInfo fetchTabInfo() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Enter edit mode.
|
// Enter edit mode.
|
||||||
void editFile() Q_DECL_OVERRIDE;
|
void editFile() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Handle text changed in m_editor.
|
// Update m_outline according to @p_tocHtml for read mode.
|
||||||
void handleTextChanged();
|
void updateOutlineFromHtml(const QString &p_tocHtml);
|
||||||
|
|
||||||
// Update m_toc according to @p_tocHtml for read mode.
|
// Update m_outline accroding to @p_headers for edit mode.
|
||||||
void updateTocFromHtml(const QString &p_tocHtml);
|
void updateOutlineFromHeaders(const QVector<VTableOfContentItem> &p_headers);
|
||||||
|
|
||||||
// Update m_toc accroding to @p_headers for edit mode.
|
|
||||||
void updateTocFromHeaders(const QVector<VHeader> &p_headers);
|
|
||||||
|
|
||||||
// Web viewer requests to update current header.
|
// Web viewer requests to update current header.
|
||||||
void updateCurHeader(const QString &p_anchor);
|
// @p_anchor is the anchor of the header, like "toc_1".
|
||||||
|
void updateCurrentHeader(const QString &p_anchor);
|
||||||
|
|
||||||
// Editor requests to update current header.
|
// Editor requests to update current header.
|
||||||
void updateCurHeader(VAnchor p_anchor);
|
void updateCurrentHeader(int p_blockNumber);
|
||||||
|
|
||||||
// Handle key press event in Web view.
|
// Handle key press event in Web view.
|
||||||
void handleWebKeyPressed(int p_key, bool p_ctrl, bool p_shift);
|
void handleWebKeyPressed(int p_key, bool p_ctrl, bool p_shift);
|
||||||
@ -117,16 +115,14 @@ private:
|
|||||||
void viewWebByConverter();
|
void viewWebByConverter();
|
||||||
|
|
||||||
// Scroll Web view to given header.
|
// Scroll Web view to given header.
|
||||||
// @p_anchorIndex is the index in m_toc.headers.
|
|
||||||
// @p_strict: if true, scroll only when @p_anchorIndex is valid.
|
|
||||||
// Return true if scroll was made.
|
// Return true if scroll was made.
|
||||||
bool scrollWebViewToAnchor(int p_anchorIndex, bool p_strict = false);
|
bool scrollWebViewToHeader(const VHeaderPointer &p_header);
|
||||||
|
|
||||||
|
bool scrollEditorToHeader(const VHeaderPointer &p_header);
|
||||||
|
|
||||||
// Scroll web/editor to given header.
|
// Scroll web/editor to given header.
|
||||||
// @p_anchorIndex is the index in m_toc.headers.
|
|
||||||
// @p_strict: if true, scroll only when @p_anchorIndex is valid.
|
|
||||||
// Return true if scroll was made.
|
// Return true if scroll was made.
|
||||||
bool scrollToAnchor(int p_anchorIndex, bool p_strict = false);
|
bool scrollToHeaderInternal(const VHeaderPointer &p_header);
|
||||||
|
|
||||||
// Search text in Web view.
|
// Search text in Web view.
|
||||||
void findTextInWebView(const QString &p_text, uint p_options, bool p_peek,
|
void findTextInWebView(const QString &p_text, uint p_options, bool p_peek,
|
||||||
|
167
src/voutline.cpp
167
src/voutline.cpp
@ -1,44 +1,39 @@
|
|||||||
#include <QDebug>
|
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include "voutline.h"
|
#include "voutline.h"
|
||||||
#include "vtoc.h"
|
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
|
#include "vfile.h"
|
||||||
|
|
||||||
extern VNote *g_vnote;
|
extern VNote *g_vnote;
|
||||||
|
|
||||||
VOutline::VOutline(QWidget *parent)
|
VOutline::VOutline(QWidget *parent)
|
||||||
: QTreeWidget(parent), VNavigationMode()
|
: QTreeWidget(parent),
|
||||||
|
VNavigationMode(),
|
||||||
|
m_muted(false)
|
||||||
{
|
{
|
||||||
setColumnCount(1);
|
setColumnCount(1);
|
||||||
setHeaderHidden(true);
|
setHeaderHidden(true);
|
||||||
setSelectionMode(QAbstractItemView::SingleSelection);
|
setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
|
||||||
|
// TODO: jump to the header when user click the same item twice.
|
||||||
connect(this, &VOutline::currentItemChanged,
|
connect(this, &VOutline::currentItemChanged,
|
||||||
this, &VOutline::handleCurItemChanged);
|
this, &VOutline::handleCurrentItemChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::checkOutline(const VToc &p_toc) const
|
void VOutline::updateOutline(const VTableOfContent &p_outline)
|
||||||
{
|
{
|
||||||
const QVector<VHeader> &headers = p_toc.headers;
|
if (p_outline == m_outline) {
|
||||||
|
return;
|
||||||
for (int i = 0; i < headers.size(); ++i) {
|
|
||||||
V_ASSERT(headers[i].index == i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::updateOutline(const VToc &toc)
|
|
||||||
{
|
|
||||||
// Clear current header
|
// Clear current header
|
||||||
curHeader = VAnchor();
|
m_currentHeader.clear();
|
||||||
|
|
||||||
checkOutline(toc);
|
m_outline = p_outline;
|
||||||
|
|
||||||
outline = toc;
|
|
||||||
|
|
||||||
updateTreeFromOutline();
|
updateTreeFromOutline();
|
||||||
|
|
||||||
@ -49,22 +44,25 @@ void VOutline::updateTreeFromOutline()
|
|||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
if (!outline.valid) {
|
if (m_outline.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVector<VHeader> &headers = outline.headers;
|
const QVector<VTableOfContentItem> &headers = m_outline.getTable();
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
updateTreeByLevel(headers, idx, NULL, NULL, 1);
|
updateTreeByLevel(headers, idx, NULL, NULL, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::updateTreeByLevel(const QVector<VHeader> &headers, int &index,
|
void VOutline::updateTreeByLevel(const QVector<VTableOfContentItem> &headers,
|
||||||
QTreeWidgetItem *parent, QTreeWidgetItem *last, int level)
|
int &index,
|
||||||
|
QTreeWidgetItem *parent,
|
||||||
|
QTreeWidgetItem *last,
|
||||||
|
int level)
|
||||||
{
|
{
|
||||||
while (index < headers.size()) {
|
while (index < headers.size()) {
|
||||||
const VHeader &header = headers[index];
|
const VTableOfContentItem &header = headers[index];
|
||||||
QTreeWidgetItem *item;
|
QTreeWidgetItem *item;
|
||||||
if (header.level == level) {
|
if (header.m_level == level) {
|
||||||
if (parent) {
|
if (parent) {
|
||||||
item = new QTreeWidgetItem(parent);
|
item = new QTreeWidgetItem(parent);
|
||||||
} else {
|
} else {
|
||||||
@ -75,7 +73,7 @@ void VOutline::updateTreeByLevel(const QVector<VHeader> &headers, int &index,
|
|||||||
|
|
||||||
last = item;
|
last = item;
|
||||||
++index;
|
++index;
|
||||||
} else if (header.level < level) {
|
} else if (header.m_level < level) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
updateTreeByLevel(headers, index, last, NULL, level + 1);
|
updateTreeByLevel(headers, index, last, NULL, level + 1);
|
||||||
@ -83,11 +81,11 @@ void VOutline::updateTreeByLevel(const QVector<VHeader> &headers, int &index,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::fillItem(QTreeWidgetItem *p_item, const VHeader &p_header)
|
void VOutline::fillItem(QTreeWidgetItem *p_item, const VTableOfContentItem &p_header)
|
||||||
{
|
{
|
||||||
p_item->setData(0, Qt::UserRole, p_header.index);
|
p_item->setData(0, Qt::UserRole, p_header.m_index);
|
||||||
p_item->setText(0, p_header.name);
|
p_item->setText(0, p_header.m_name);
|
||||||
p_item->setToolTip(0, p_header.name);
|
p_item->setToolTip(0, p_header.m_name);
|
||||||
|
|
||||||
if (p_header.isEmpty()) {
|
if (p_header.isEmpty()) {
|
||||||
p_item->setForeground(0, QColor("grey"));
|
p_item->setForeground(0, QColor("grey"));
|
||||||
@ -99,127 +97,81 @@ void VOutline::expandTree()
|
|||||||
if (topLevelItemCount() == 0) {
|
if (topLevelItemCount() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
expandAll();
|
expandAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::handleCurItemChanged(QTreeWidgetItem *p_curItem, QTreeWidgetItem * /*p_preItem*/)
|
void VOutline::handleCurrentItemChanged(QTreeWidgetItem *p_curItem,
|
||||||
|
QTreeWidgetItem * p_preItem)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(p_preItem);
|
||||||
|
|
||||||
if (!p_curItem) {
|
if (!p_curItem) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VHeader *header = getHeaderFromItem(p_curItem);
|
const VTableOfContentItem *header = getHeaderFromItem(p_curItem);
|
||||||
if (!header) {
|
Q_ASSERT(header);
|
||||||
return;
|
m_currentHeader.update(m_outline.getFile(), header->m_index);
|
||||||
}
|
|
||||||
|
|
||||||
VAnchor tmp(outline.m_file, header->anchor, header->lineNumber, header->index);
|
if (!header->isEmpty() && !m_muted) {
|
||||||
if (tmp == curHeader) {
|
emit outlineItemActivated(m_currentHeader);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
curHeader = tmp;
|
|
||||||
|
|
||||||
if (!header->isEmpty()) {
|
|
||||||
emit outlineItemActivated(curHeader);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::updateCurHeader(const VAnchor &anchor)
|
void VOutline::updateCurrentHeader(const VHeaderPointer &p_header)
|
||||||
{
|
{
|
||||||
if (anchor == curHeader) {
|
if (p_header == m_currentHeader
|
||||||
|
|| !m_outline.isMatched(p_header)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
curHeader = anchor;
|
// Item change should not emit the signal.
|
||||||
if (outline.type == VHeaderType::Anchor) {
|
m_muted = true;
|
||||||
selectAnchor(anchor.anchor);
|
m_currentHeader = p_header;
|
||||||
} else {
|
selectHeader(m_currentHeader);
|
||||||
// Select by lineNumber.
|
m_muted = false;
|
||||||
selectLineNumber(anchor.lineNumber);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::selectAnchor(const QString &anchor)
|
void VOutline::selectHeader(const VHeaderPointer &p_header)
|
||||||
{
|
{
|
||||||
setCurrentItem(NULL);
|
setCurrentItem(NULL);
|
||||||
|
|
||||||
if (anchor.isEmpty()) {
|
if (!m_outline.getItem(p_header)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nrTop = topLevelItemCount();
|
int nrTop = topLevelItemCount();
|
||||||
for (int i = 0; i < nrTop; ++i) {
|
for (int i = 0; i < nrTop; ++i) {
|
||||||
if (selectAnchorOne(topLevelItem(i), anchor)) {
|
if (selectHeaderOne(topLevelItem(i), p_header)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VOutline::selectAnchorOne(QTreeWidgetItem *item, const QString &anchor)
|
bool VOutline::selectHeaderOne(QTreeWidgetItem *p_item, const VHeaderPointer &p_header)
|
||||||
{
|
{
|
||||||
if (!item) {
|
if (!p_item) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VHeader *header = getHeaderFromItem(item);
|
const VTableOfContentItem *header = getHeaderFromItem(p_item);
|
||||||
if (!header) {
|
if (!header) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header->anchor == anchor) {
|
if (header->isMatched(p_header)) {
|
||||||
setCurrentItem(item);
|
setCurrentItem(p_item);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nrChild = item->childCount();
|
int nrChild = p_item->childCount();
|
||||||
for (int i = 0; i < nrChild; ++i) {
|
for (int i = 0; i < nrChild; ++i) {
|
||||||
if (selectAnchorOne(item->child(i), anchor)) {
|
if (selectHeaderOne(p_item->child(i), p_header)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VOutline::selectLineNumber(int lineNumber)
|
|
||||||
{
|
|
||||||
setCurrentItem(NULL);
|
|
||||||
|
|
||||||
if (lineNumber == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const VHeader *header = getHeaderFromItem(item);
|
|
||||||
if (!header) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header->lineNumber == lineNumber) {
|
|
||||||
// Select this item
|
|
||||||
setCurrentItem(item);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nrChild = item->childCount();
|
|
||||||
for (int i = 0; i < nrChild; ++i) {
|
|
||||||
if (selectLineNumberOne(item->child(i), lineNumber)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,17 +325,8 @@ QList<QTreeWidgetItem *> VOutline::getVisibleChildItems(const QTreeWidgetItem *p
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VHeader *VOutline::getHeaderFromItem(QTreeWidgetItem *p_item) const
|
const VTableOfContentItem *VOutline::getHeaderFromItem(QTreeWidgetItem *p_item) const
|
||||||
{
|
{
|
||||||
const VHeader *header = NULL;
|
|
||||||
|
|
||||||
int index = p_item->data(0, Qt::UserRole).toInt();
|
int index = p_item->data(0, Qt::UserRole).toInt();
|
||||||
if (index < 0 || index >= outline.headers.size()) {
|
return m_outline.getItem(index);
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
header = &(outline.headers[index]);
|
|
||||||
Q_ASSERT(header->index == index);
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QChar>
|
#include <QChar>
|
||||||
#include "vtoc.h"
|
#include "vtableofcontent.h"
|
||||||
#include "vnavigationmode.h"
|
#include "vnavigationmode.h"
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
|
|
||||||
|
// Display table of content as a tree and enable user to click an item to
|
||||||
|
// jump to that header.
|
||||||
class VOutline : public QTreeWidget, public VNavigationMode
|
class VOutline : public QTreeWidget, public VNavigationMode
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -23,45 +25,60 @@ public:
|
|||||||
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
|
bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void outlineItemActivated(const VAnchor &anchor);
|
// Emit when current item changed by user and header of that item is not empty.
|
||||||
|
// Do not worry about infinite recursion.
|
||||||
|
void outlineItemActivated(const VHeaderPointer &p_header);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateOutline(const VToc &toc);
|
// Called to update outline and the tree.
|
||||||
void updateCurHeader(const VAnchor &anchor);
|
// Just clear the tree if @p_outline is empty.
|
||||||
|
void updateOutline(const VTableOfContent &p_outline);
|
||||||
|
|
||||||
|
// Called to update current header in the tree.
|
||||||
|
// Will not emit outlineItemActivated().
|
||||||
|
void updateCurrentHeader(const VHeaderPointer &p_header);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
|
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleCurItemChanged(QTreeWidgetItem *p_curItem, QTreeWidgetItem *p_preItem);
|
// Handle current item change even of the tree.
|
||||||
|
// Do not response if m_muted is true.
|
||||||
|
void handleCurrentItemChanged(QTreeWidgetItem *p_curItem, QTreeWidgetItem *p_preItem);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Update tree according to outline.
|
// Update tree according to outline.
|
||||||
void updateTreeFromOutline();
|
void updateTreeFromOutline();
|
||||||
|
|
||||||
// @index: the index in @headers.
|
// @index: the index in @headers.
|
||||||
void updateTreeByLevel(const QVector<VHeader> &headers, int &index, QTreeWidgetItem *parent,
|
void updateTreeByLevel(const QVector<VTableOfContentItem> &headers,
|
||||||
QTreeWidgetItem *last, int level);
|
int &index,
|
||||||
|
QTreeWidgetItem *parent,
|
||||||
|
QTreeWidgetItem *last,
|
||||||
|
int level);
|
||||||
|
|
||||||
void expandTree();
|
void expandTree();
|
||||||
void selectAnchor(const QString &anchor);
|
|
||||||
bool selectAnchorOne(QTreeWidgetItem *item, const QString &anchor);
|
// Set the item corresponding to @p_header as current item.
|
||||||
void selectLineNumber(int lineNumber);
|
void selectHeader(const VHeaderPointer &p_header);
|
||||||
bool selectLineNumberOne(QTreeWidgetItem *item, int lineNumber);
|
|
||||||
|
bool selectHeaderOne(QTreeWidgetItem *p_item, const VHeaderPointer &p_header);
|
||||||
|
|
||||||
QList<QTreeWidgetItem *> getVisibleItems() const;
|
QList<QTreeWidgetItem *> getVisibleItems() const;
|
||||||
QList<QTreeWidgetItem *> getVisibleChildItems(const QTreeWidgetItem *p_item) const;
|
QList<QTreeWidgetItem *> getVisibleChildItems(const QTreeWidgetItem *p_item) const;
|
||||||
|
|
||||||
// Fill the info of @p_item.
|
// Fill the info of @p_item.
|
||||||
void fillItem(QTreeWidgetItem *p_item, const VHeader &p_header);
|
void fillItem(QTreeWidgetItem *p_item, const VTableOfContentItem &p_header);
|
||||||
|
|
||||||
// Check if @p_toc is valid.
|
|
||||||
void checkOutline(const VToc &p_toc) const;
|
|
||||||
|
|
||||||
// Return NULL if no corresponding header in outline.
|
// Return NULL if no corresponding header in outline.
|
||||||
const VHeader *getHeaderFromItem(QTreeWidgetItem *p_item) const;
|
const VTableOfContentItem *getHeaderFromItem(QTreeWidgetItem *p_item) const;
|
||||||
|
|
||||||
VToc outline;
|
VTableOfContent m_outline;
|
||||||
VAnchor curHeader;
|
|
||||||
|
VHeaderPointer m_currentHeader;
|
||||||
|
|
||||||
|
// When true, won't emit outlineItemActivated().
|
||||||
|
bool m_muted;
|
||||||
|
|
||||||
// Navigation Mode.
|
// Navigation Mode.
|
||||||
// Map second key to QTreeWidgetItem.
|
// Map second key to QTreeWidgetItem.
|
||||||
|
176
src/vtableofcontent.cpp
Normal file
176
src/vtableofcontent.cpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#include "vtableofcontent.h"
|
||||||
|
#include "vconstants.h"
|
||||||
|
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
|
||||||
|
VTableOfContent::VTableOfContent()
|
||||||
|
: m_file(NULL), m_type(VTableOfContentType::Anchor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VTableOfContent::VTableOfContent(const VFile *p_file)
|
||||||
|
: m_file(p_file), m_type(VTableOfContentType::Anchor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void VTableOfContent::update(const VFile *p_file,
|
||||||
|
const QVector<VTableOfContentItem> &p_table,
|
||||||
|
VTableOfContentType p_type)
|
||||||
|
{
|
||||||
|
m_file = p_file;
|
||||||
|
m_table = p_table;
|
||||||
|
m_type = p_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseTocUl(QXmlStreamReader &p_xml,
|
||||||
|
QVector<VTableOfContentItem> &p_table,
|
||||||
|
int p_level);
|
||||||
|
|
||||||
|
static bool parseTocLi(QXmlStreamReader &p_xml,
|
||||||
|
QVector<VTableOfContentItem> &p_table,
|
||||||
|
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().mid(1);
|
||||||
|
QString name;
|
||||||
|
if (p_xml.readNext()) {
|
||||||
|
if (p_xml.tokenString() == "Characters") {
|
||||||
|
name = p_xml.text().toString();
|
||||||
|
} else if (!p_xml.isEndElement()) {
|
||||||
|
qWarning() << "TOC HTML <a> should be ended by </a>" << p_xml.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VTableOfContentItem header(name, p_level, anchor, p_table.size());
|
||||||
|
p_table.append(header);
|
||||||
|
} else {
|
||||||
|
// Error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (p_xml.name() == "ul") {
|
||||||
|
// Such as header 3 under header 1 directly
|
||||||
|
VTableOfContentItem header(c_emptyHeaderName, p_level, "", p_table.size());
|
||||||
|
p_table.append(header);
|
||||||
|
parseTocUl(p_xml, p_table, p_level + 1);
|
||||||
|
} else {
|
||||||
|
qWarning() << "TOC HTML <li> should contain <a> or <ul>" << p_xml.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (p_xml.readNext()) {
|
||||||
|
if (p_xml.isEndElement()) {
|
||||||
|
if (p_xml.name() == "li") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_xml.name() == "ul") {
|
||||||
|
// Nested unordered list
|
||||||
|
if (!parseTocUl(p_xml, p_table, p_level + 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseTocUl(QXmlStreamReader &p_xml,
|
||||||
|
QVector<VTableOfContentItem> &p_table,
|
||||||
|
int p_level)
|
||||||
|
{
|
||||||
|
bool ret = true;
|
||||||
|
Q_ASSERT(p_xml.isStartElement() && p_xml.name() == "ul");
|
||||||
|
|
||||||
|
while (p_xml.readNextStartElement()) {
|
||||||
|
if (p_xml.name() == "li") {
|
||||||
|
if (!parseTocLi(p_xml, p_table, p_level)) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qWarning() << "TOC HTML <ul> should contain <li>" << p_xml.name();
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VTableOfContent::parseTableFromHtml(const QString &p_html)
|
||||||
|
{
|
||||||
|
bool ret = true;
|
||||||
|
m_table.clear();
|
||||||
|
|
||||||
|
if (!p_html.isEmpty()) {
|
||||||
|
QXmlStreamReader xml(p_html);
|
||||||
|
if (xml.readNextStartElement()) {
|
||||||
|
if (xml.name() == "ul") {
|
||||||
|
ret = parseTocUl(xml, m_table, 1);
|
||||||
|
} else {
|
||||||
|
qWarning() << "TOC HTML does not start with <ul>" << p_html;
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xml.hasError()) {
|
||||||
|
qWarning() << "fail to parse TOC in HTML" << p_html;
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VTableOfContent::indexOfItemByAnchor(const QString &p_anchor) const
|
||||||
|
{
|
||||||
|
if (p_anchor.isEmpty()
|
||||||
|
|| isEmpty()
|
||||||
|
|| m_type != VTableOfContentType::Anchor) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_table.size(); ++i) {
|
||||||
|
if (m_table[i].m_anchor == p_anchor) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VTableOfContent::indexOfItemByBlockNumber(int p_blockNumber) const
|
||||||
|
{
|
||||||
|
if (p_blockNumber == -1
|
||||||
|
|| isEmpty()
|
||||||
|
|| m_type != VTableOfContentType::BlockNumber) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = m_table.size() - 1; i >= 0; --i) {
|
||||||
|
if (!m_table[i].isEmpty()
|
||||||
|
&& m_table[i].m_blockNumber <= p_blockNumber) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VTableOfContent::operator==(const VTableOfContent &p_outline) const
|
||||||
|
{
|
||||||
|
return m_file == p_outline.getFile()
|
||||||
|
&& m_type == p_outline.getType()
|
||||||
|
&& m_table == p_outline.getTable();
|
||||||
|
}
|
286
src/vtableofcontent.h
Normal file
286
src/vtableofcontent.h
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
#ifndef VTABLEOFCONTENT_H
|
||||||
|
#define VTABLEOFCONTENT_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
class VFile;
|
||||||
|
|
||||||
|
|
||||||
|
struct VHeaderPointer
|
||||||
|
{
|
||||||
|
VHeaderPointer()
|
||||||
|
: m_file(NULL), m_index(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VHeaderPointer(const VFile *p_file, int p_index)
|
||||||
|
: m_file(p_file), m_index(p_index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const VHeaderPointer &p_header) const
|
||||||
|
{
|
||||||
|
return m_file == p_header.m_file
|
||||||
|
&& m_index == p_header.m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
m_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_file = NULL;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(const VFile *p_file, int p_index)
|
||||||
|
{
|
||||||
|
m_file = p_file;
|
||||||
|
m_index = p_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return m_index > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString toString() const
|
||||||
|
{
|
||||||
|
return QString("VHeaderPointer file: %1 index: %2")
|
||||||
|
.arg((long long)m_file)
|
||||||
|
.arg(m_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The corresponding file.
|
||||||
|
const VFile *m_file;
|
||||||
|
|
||||||
|
// Index of the header item in VTableOfContent which this instance points to.
|
||||||
|
int m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct VTableOfContentItem
|
||||||
|
{
|
||||||
|
VTableOfContentItem()
|
||||||
|
: m_level(1), m_blockNumber(-1), m_index(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VTableOfContentItem(const QString &p_name,
|
||||||
|
int p_level,
|
||||||
|
const QString &p_anchor,
|
||||||
|
int p_index)
|
||||||
|
: m_name(p_name),
|
||||||
|
m_level(p_level),
|
||||||
|
m_anchor(p_anchor),
|
||||||
|
m_blockNumber(-1),
|
||||||
|
m_index(p_index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VTableOfContentItem(const QString &p_name,
|
||||||
|
int p_level,
|
||||||
|
int p_blockNumber,
|
||||||
|
int p_index)
|
||||||
|
: m_name(p_name),
|
||||||
|
m_level(p_level),
|
||||||
|
m_blockNumber(p_blockNumber),
|
||||||
|
m_index(p_index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether it is an empty item.
|
||||||
|
// An empty item points to nothing.
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
if (m_anchor.isEmpty()) {
|
||||||
|
return m_blockNumber == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isMatched(const VHeaderPointer &p_header) const
|
||||||
|
{
|
||||||
|
return m_index == p_header.m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const VTableOfContentItem &p_item) const
|
||||||
|
{
|
||||||
|
return m_name == p_item.m_name
|
||||||
|
&& m_level == p_item.m_level
|
||||||
|
&& m_anchor == p_item.m_anchor
|
||||||
|
&& m_blockNumber == p_item.m_blockNumber
|
||||||
|
&& m_index == p_item.m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name of the item to display.
|
||||||
|
QString m_name;
|
||||||
|
|
||||||
|
// Level of this item, based on 1.
|
||||||
|
int m_level;
|
||||||
|
|
||||||
|
// Use an anchor to identify the position of this item.
|
||||||
|
QString m_anchor;
|
||||||
|
|
||||||
|
// Use block number to identify the position of this item.
|
||||||
|
// -1 to indicate invalid.
|
||||||
|
int m_blockNumber;
|
||||||
|
|
||||||
|
// Index in VTableOfContent, based on 0.
|
||||||
|
// -1 for invalid value.
|
||||||
|
int m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class VTableOfContentType
|
||||||
|
{
|
||||||
|
Anchor = 0,
|
||||||
|
BlockNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class VTableOfContent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VTableOfContent();
|
||||||
|
|
||||||
|
VTableOfContent(const VFile *p_file);
|
||||||
|
|
||||||
|
void update(const VFile *p_file,
|
||||||
|
const QVector<VTableOfContentItem> &p_table,
|
||||||
|
VTableOfContentType p_type);
|
||||||
|
|
||||||
|
// Parse m_table from html.
|
||||||
|
bool parseTableFromHtml(const QString &p_html);
|
||||||
|
|
||||||
|
const VFile *getFile() const;
|
||||||
|
|
||||||
|
void setFile(const VFile *p_file);
|
||||||
|
|
||||||
|
VTableOfContentType getType() const;
|
||||||
|
|
||||||
|
void setType(VTableOfContentType p_type);
|
||||||
|
|
||||||
|
void clearTable();
|
||||||
|
|
||||||
|
const QVector<VTableOfContentItem> &getTable() const;
|
||||||
|
|
||||||
|
void setTable(const QVector<VTableOfContentItem> &p_table);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
// Return the index in @m_table of @p_anchor.
|
||||||
|
int indexOfItemByAnchor(const QString &p_anchor) const;
|
||||||
|
|
||||||
|
// Return the last index in @m_table which has smaller block number than @p_blockNumber.
|
||||||
|
int indexOfItemByBlockNumber(int p_blockNumber) const;
|
||||||
|
|
||||||
|
const VTableOfContentItem *getItem(int p_idx) const;
|
||||||
|
|
||||||
|
const VTableOfContentItem *getItem(const VHeaderPointer &p_header) const;
|
||||||
|
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
// Whether @p_header is pointing to this outline.
|
||||||
|
bool isMatched(const VHeaderPointer &p_header) const;
|
||||||
|
|
||||||
|
bool operator==(const VTableOfContent &p_outline) const;
|
||||||
|
|
||||||
|
QString toString() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Corresponding file.
|
||||||
|
const VFile *m_file;
|
||||||
|
|
||||||
|
// Table of content.
|
||||||
|
QVector<VTableOfContentItem> m_table;
|
||||||
|
|
||||||
|
// Type of the table of content: by anchor or by block number.
|
||||||
|
VTableOfContentType m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline VTableOfContentType VTableOfContent::getType() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VTableOfContent::setType(VTableOfContentType p_type)
|
||||||
|
{
|
||||||
|
m_type = p_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VTableOfContent::clearTable()
|
||||||
|
{
|
||||||
|
m_table.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QVector<VTableOfContentItem> &VTableOfContent::getTable() const
|
||||||
|
{
|
||||||
|
return m_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VTableOfContent::setTable(const QVector<VTableOfContentItem> &p_table)
|
||||||
|
{
|
||||||
|
m_table = p_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VTableOfContent::clear()
|
||||||
|
{
|
||||||
|
m_file = NULL;
|
||||||
|
m_table.clear();
|
||||||
|
m_type = VTableOfContentType::Anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VTableOfContent::setFile(const VFile *p_file)
|
||||||
|
{
|
||||||
|
m_file = p_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const VFile *VTableOfContent::getFile() const
|
||||||
|
{
|
||||||
|
return m_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const VTableOfContentItem *VTableOfContent::getItem(int p_idx) const
|
||||||
|
{
|
||||||
|
if (!m_file
|
||||||
|
|| p_idx < 0
|
||||||
|
|| p_idx >= m_table.size()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m_table[p_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const VTableOfContentItem *VTableOfContent::getItem(const VHeaderPointer &p_header) const
|
||||||
|
{
|
||||||
|
if (p_header.m_file != m_file) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getItem(p_header.m_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool VTableOfContent::isEmpty() const
|
||||||
|
{
|
||||||
|
return !m_file || m_table.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool VTableOfContent::isMatched(const VHeaderPointer &p_header) const
|
||||||
|
{
|
||||||
|
return m_file && m_file == p_header.m_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString VTableOfContent::toString() const
|
||||||
|
{
|
||||||
|
return QString("VTableOfContent file: %1 isAnchor: %2 tableSize: %3")
|
||||||
|
.arg((long long)m_file)
|
||||||
|
.arg(m_type == VTableOfContentType::Anchor)
|
||||||
|
.arg(m_table.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // VTABLEOFCONTENT_H
|
@ -1,6 +0,0 @@
|
|||||||
#include "vtoc.h"
|
|
||||||
|
|
||||||
VToc::VToc()
|
|
||||||
: type(VHeaderType::Anchor), valid(false)
|
|
||||||
{
|
|
||||||
}
|
|
78
src/vtoc.h
78
src/vtoc.h
@ -1,78 +0,0 @@
|
|||||||
#ifndef VTOC_H
|
|
||||||
#define VTOC_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
class VFile;
|
|
||||||
|
|
||||||
enum VHeaderType
|
|
||||||
{
|
|
||||||
Anchor = 0,
|
|
||||||
LineNumber
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VHeader
|
|
||||||
{
|
|
||||||
VHeader() : level(1), lineNumber(-1), index(-1) {}
|
|
||||||
VHeader(int level, const QString &name, const QString &anchor, int lineNumber, int index)
|
|
||||||
: level(level), name(name), anchor(anchor), lineNumber(lineNumber), index(index) {}
|
|
||||||
int level;
|
|
||||||
QString name;
|
|
||||||
QString anchor;
|
|
||||||
int lineNumber;
|
|
||||||
|
|
||||||
// Index in the outline, based on 0.
|
|
||||||
int index;
|
|
||||||
|
|
||||||
// Whether it is an empty (fake) header.
|
|
||||||
bool isEmpty() const
|
|
||||||
{
|
|
||||||
if (anchor.isEmpty()) {
|
|
||||||
return lineNumber == -1;
|
|
||||||
} else {
|
|
||||||
return anchor == "#";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VAnchor
|
|
||||||
{
|
|
||||||
VAnchor() : m_file(NULL), lineNumber(-1), m_outlineIndex(-1) {}
|
|
||||||
|
|
||||||
VAnchor(const VFile *file, const QString &anchor, int lineNumber, int outlineIndex = -1)
|
|
||||||
: m_file(file), anchor(anchor), lineNumber(lineNumber), m_outlineIndex(outlineIndex) {}
|
|
||||||
|
|
||||||
// The file this anchor points to.
|
|
||||||
const VFile *m_file;
|
|
||||||
|
|
||||||
// The string anchor. For Web view.
|
|
||||||
QString anchor;
|
|
||||||
|
|
||||||
// The line number anchor. For edit view.
|
|
||||||
int lineNumber;
|
|
||||||
|
|
||||||
// Index of the header for this anchor in VToc outline.
|
|
||||||
// Used to translate current header between read and edit mode.
|
|
||||||
int m_outlineIndex;
|
|
||||||
|
|
||||||
bool operator==(const VAnchor &p_anchor) const {
|
|
||||||
return (p_anchor.m_file == m_file
|
|
||||||
&& p_anchor.anchor == anchor
|
|
||||||
&& p_anchor.lineNumber == lineNumber
|
|
||||||
&& p_anchor.m_outlineIndex == m_outlineIndex);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VToc
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VToc();
|
|
||||||
|
|
||||||
QVector<VHeader> headers;
|
|
||||||
int type;
|
|
||||||
const VFile *m_file;
|
|
||||||
bool valid;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // VTOC_H
|
|
Loading…
x
Reference in New Issue
Block a user