mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support auto heading sequence by config enable_heading_sequence
This commit is contained in:
parent
ed4044061f
commit
2eb6476c3d
@ -14,6 +14,8 @@ Users from China can download the latest release of VNote from [Baidu Netdisk](h
|
|||||||
- [Github releases](https://github.com/tamlok/vnote/releases)
|
- [Github releases](https://github.com/tamlok/vnote/releases)
|
||||||
- Latest builds on master: [  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
- Latest builds on master: [  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||||
|
|
||||||
|
**NOT** supported in XP since QtWebEngineProcess used by VNote could not work in XP.
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
[](https://travis-ci.org/tamlok/vnote)
|
[](https://travis-ci.org/tamlok/vnote)
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
- [Github releases](https://github.com/tamlok/vnote/releases)
|
- [Github releases](https://github.com/tamlok/vnote/releases)
|
||||||
- master分支的最新构建:[  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
- master分支的最新构建:[  ](https://bintray.com/tamlok/vnote/vnote/_latestVersion)
|
||||||
|
|
||||||
|
VNote不支持**XP**,因为QtWebEngineProcess无法在XP上运行。
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
[](https://travis-ci.org/tamlok/vnote)
|
[](https://travis-ci.org/tamlok/vnote)
|
||||||
|
|
||||||
|
@ -10,25 +10,57 @@ extern VConfigManager *g_config;
|
|||||||
VSettingsDialog::VSettingsDialog(QWidget *p_parent)
|
VSettingsDialog::VSettingsDialog(QWidget *p_parent)
|
||||||
: QDialog(p_parent)
|
: QDialog(p_parent)
|
||||||
{
|
{
|
||||||
m_tabs = new QTabWidget;
|
m_tabList = new QListWidget(this);
|
||||||
m_tabs->addTab(new VGeneralTab(), tr("General"));
|
m_tabList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||||
m_tabs->addTab(new VReadEditTab(), tr("Read/Edit"));
|
|
||||||
m_tabs->addTab(new VNoteManagementTab(), tr("Note Management"));
|
m_tabs = new QStackedLayout();
|
||||||
|
|
||||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
connect(m_btnBox, &QDialogButtonBox::accepted, this, &VSettingsDialog::saveConfiguration);
|
connect(m_btnBox, &QDialogButtonBox::accepted, this, &VSettingsDialog::saveConfiguration);
|
||||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QHBoxLayout *tabLayout = new QHBoxLayout();
|
||||||
mainLayout->addWidget(m_tabs);
|
tabLayout->addWidget(m_tabList);
|
||||||
|
tabLayout->addLayout(m_tabs);
|
||||||
|
tabLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
tabLayout->setSpacing(0);
|
||||||
|
tabLayout->setStretch(0, 0);
|
||||||
|
tabLayout->setStretch(1, 5);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||||
|
mainLayout->addLayout(tabLayout);
|
||||||
mainLayout->addWidget(m_btnBox);
|
mainLayout->addWidget(m_btnBox);
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
setWindowTitle(tr("Settings"));
|
setWindowTitle(tr("Settings"));
|
||||||
|
|
||||||
|
// Add tabs.
|
||||||
|
addTab(new VGeneralTab(), tr("General"));
|
||||||
|
addTab(new VReadEditTab(), tr("Read/Edit"));
|
||||||
|
addTab(new VNoteManagementTab(), tr("Note Management"));
|
||||||
|
addTab(new VMarkdownTab(), tr("Markdown"));
|
||||||
|
|
||||||
|
m_tabList->setMaximumWidth(m_tabList->sizeHintForColumn(0) + 5);
|
||||||
|
|
||||||
|
connect(m_tabList, &QListWidget::currentItemChanged,
|
||||||
|
this, [this](QListWidgetItem *p_cur, QListWidgetItem *p_pre) {
|
||||||
|
Q_UNUSED(p_pre);
|
||||||
|
Q_ASSERT(p_cur);
|
||||||
|
int idx = p_cur->data(Qt::UserRole).toInt();
|
||||||
|
Q_ASSERT(idx >= 0);
|
||||||
|
m_tabs->setCurrentWidget(m_tabs->widget(idx));
|
||||||
|
});
|
||||||
|
|
||||||
loadConfiguration();
|
loadConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VSettingsDialog::addTab(QWidget *p_widget, const QString &p_label)
|
||||||
|
{
|
||||||
|
int idx = m_tabs->addWidget(p_widget);
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(p_label, m_tabList);
|
||||||
|
item->setData(Qt::UserRole, idx);
|
||||||
|
}
|
||||||
|
|
||||||
void VSettingsDialog::loadConfiguration()
|
void VSettingsDialog::loadConfiguration()
|
||||||
{
|
{
|
||||||
// General Tab.
|
// General Tab.
|
||||||
@ -58,6 +90,15 @@ void VSettingsDialog::loadConfiguration()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Markdown Tab.
|
||||||
|
{
|
||||||
|
VMarkdownTab *markdownTab = dynamic_cast<VMarkdownTab *>(m_tabs->widget(3));
|
||||||
|
Q_ASSERT(markdownTab);
|
||||||
|
if (!markdownTab->loadConfiguration()) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
||||||
@ -95,6 +136,15 @@ void VSettingsDialog::saveConfiguration()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Markdown Tab.
|
||||||
|
{
|
||||||
|
VMarkdownTab *markdownTab = dynamic_cast<VMarkdownTab *>(m_tabs->widget(3));
|
||||||
|
Q_ASSERT(markdownTab);
|
||||||
|
if (!markdownTab->saveConfiguration()) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
accept();
|
accept();
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
@ -234,25 +284,14 @@ VReadEditTab::VReadEditTab(QWidget *p_parent)
|
|||||||
zoomFactorLayout->addWidget(m_customWebZoom);
|
zoomFactorLayout->addWidget(m_customWebZoom);
|
||||||
zoomFactorLayout->addWidget(m_webZoomFactorSpin);
|
zoomFactorLayout->addWidget(m_webZoomFactorSpin);
|
||||||
|
|
||||||
// Default note open mode.
|
|
||||||
m_openModeCombo = new QComboBox();
|
|
||||||
m_openModeCombo->setToolTip(tr("Default mode to open a note"));
|
|
||||||
m_openModeCombo->addItem(tr("Read Mode"), (int)OpenFileMode::Read);
|
|
||||||
m_openModeCombo->addItem(tr("Edit Mode"), (int)OpenFileMode::Edit);
|
|
||||||
|
|
||||||
QLabel *openModeLabel = new QLabel(tr("Note open mode:"));
|
|
||||||
openModeLabel->setToolTip(m_openModeCombo->toolTip());
|
|
||||||
|
|
||||||
QFormLayout *readLayout = new QFormLayout();
|
QFormLayout *readLayout = new QFormLayout();
|
||||||
readLayout->addRow(zoomFactorLayout);
|
readLayout->addRow(zoomFactorLayout);
|
||||||
readLayout->addRow(openModeLabel, m_openModeCombo);
|
|
||||||
|
|
||||||
m_readBox->setLayout(readLayout);
|
m_readBox->setLayout(readLayout);
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||||
mainLayout->addWidget(m_readBox);
|
mainLayout->addWidget(m_readBox);
|
||||||
mainLayout->addWidget(m_editBox);
|
mainLayout->addWidget(m_editBox);
|
||||||
m_editBox->hide();
|
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,10 +301,6 @@ bool VReadEditTab::loadConfiguration()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loadOpenMode()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,10 +310,6 @@ bool VReadEditTab::saveConfiguration()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!saveOpenMode()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,29 +346,6 @@ void VReadEditTab::customWebZoomChanged(int p_state)
|
|||||||
m_webZoomFactorSpin->setEnabled(p_state == Qt::Checked);
|
m_webZoomFactorSpin->setEnabled(p_state == Qt::Checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VReadEditTab::loadOpenMode()
|
|
||||||
{
|
|
||||||
int mode = (int)g_config->getNoteOpenMode();
|
|
||||||
bool found = false;
|
|
||||||
for (int i = 0; i < m_openModeCombo->count(); ++i) {
|
|
||||||
if (m_openModeCombo->itemData(i).toInt() == mode) {
|
|
||||||
m_openModeCombo->setCurrentIndex(i);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_ASSERT(found);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VReadEditTab::saveOpenMode()
|
|
||||||
{
|
|
||||||
int mode = m_openModeCombo->currentData().toInt();
|
|
||||||
g_config->setNoteOpenMode((OpenFileMode)mode);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
VNoteManagementTab::VNoteManagementTab(QWidget *p_parent)
|
VNoteManagementTab::VNoteManagementTab(QWidget *p_parent)
|
||||||
: QWidget(p_parent)
|
: QWidget(p_parent)
|
||||||
{
|
{
|
||||||
@ -487,3 +495,91 @@ void VNoteManagementTab::customImageFolderExtChanged(int p_state)
|
|||||||
m_imageFolderEditExt->setEnabled(false);
|
m_imageFolderEditExt->setEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VMarkdownTab::VMarkdownTab(QWidget *p_parent)
|
||||||
|
: QWidget(p_parent)
|
||||||
|
{
|
||||||
|
// Default note open mode.
|
||||||
|
m_openModeCombo = new QComboBox();
|
||||||
|
m_openModeCombo->setToolTip(tr("Default mode to open a note"));
|
||||||
|
m_openModeCombo->addItem(tr("Read Mode"), (int)OpenFileMode::Read);
|
||||||
|
m_openModeCombo->addItem(tr("Edit Mode"), (int)OpenFileMode::Edit);
|
||||||
|
|
||||||
|
QLabel *openModeLabel = new QLabel(tr("Note open mode:"));
|
||||||
|
openModeLabel->setToolTip(m_openModeCombo->toolTip());
|
||||||
|
|
||||||
|
// Heading sequence.
|
||||||
|
m_headingSequence = new QCheckBox();
|
||||||
|
m_headingSequence->setToolTip(tr("Enable auto sequence for all headings (in the form like 1.2.3.4.)"));
|
||||||
|
|
||||||
|
QLabel *headingSequenceLabel = new QLabel(tr("Heading sequence:"));
|
||||||
|
headingSequenceLabel->setToolTip(m_headingSequence->toolTip());
|
||||||
|
|
||||||
|
QFormLayout *mainLayout = new QFormLayout();
|
||||||
|
mainLayout->addRow(openModeLabel, m_openModeCombo);
|
||||||
|
mainLayout->addRow(headingSequenceLabel, m_headingSequence);
|
||||||
|
|
||||||
|
setLayout(mainLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMarkdownTab::loadConfiguration()
|
||||||
|
{
|
||||||
|
if (!loadOpenMode()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadHeadingSequence()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMarkdownTab::saveConfiguration()
|
||||||
|
{
|
||||||
|
if (!saveOpenMode()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!saveHeadingSequence()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMarkdownTab::loadOpenMode()
|
||||||
|
{
|
||||||
|
int mode = (int)g_config->getNoteOpenMode();
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < m_openModeCombo->count(); ++i) {
|
||||||
|
if (m_openModeCombo->itemData(i).toInt() == mode) {
|
||||||
|
m_openModeCombo->setCurrentIndex(i);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(found);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMarkdownTab::saveOpenMode()
|
||||||
|
{
|
||||||
|
int mode = m_openModeCombo->currentData().toInt();
|
||||||
|
g_config->setNoteOpenMode((OpenFileMode)mode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMarkdownTab::loadHeadingSequence()
|
||||||
|
{
|
||||||
|
bool enabled = g_config->getEnableHeadingSequence();
|
||||||
|
m_headingSequence->setChecked(enabled);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMarkdownTab::saveHeadingSequence()
|
||||||
|
{
|
||||||
|
g_config->setEnableHeadingSequence(m_headingSequence->isChecked());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -6,12 +6,13 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
class QDialogButtonBox;
|
class QDialogButtonBox;
|
||||||
class QTabWidget;
|
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
class QGroupBox;
|
class QGroupBox;
|
||||||
class QDoubleSpinBox;
|
class QDoubleSpinBox;
|
||||||
class QCheckBox;
|
class QCheckBox;
|
||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
|
class QStackedLayout;
|
||||||
|
class QListWidget;
|
||||||
|
|
||||||
class VGeneralTab : public QWidget
|
class VGeneralTab : public QWidget
|
||||||
{
|
{
|
||||||
@ -52,18 +53,12 @@ public:
|
|||||||
QCheckBox *m_customWebZoom;
|
QCheckBox *m_customWebZoom;
|
||||||
QDoubleSpinBox *m_webZoomFactorSpin;
|
QDoubleSpinBox *m_webZoomFactorSpin;
|
||||||
|
|
||||||
// Default note open mode for markdown.
|
|
||||||
QComboBox *m_openModeCombo;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void customWebZoomChanged(int p_state);
|
void customWebZoomChanged(int p_state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool loadWebZoomFactor();
|
bool loadWebZoomFactor();
|
||||||
bool saveWebZoomFactor();
|
bool saveWebZoomFactor();
|
||||||
|
|
||||||
bool loadOpenMode();
|
|
||||||
bool saveOpenMode();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class VNoteManagementTab : public QWidget
|
class VNoteManagementTab : public QWidget
|
||||||
@ -97,6 +92,28 @@ private:
|
|||||||
bool saveImageFolderExt();
|
bool saveImageFolderExt();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VMarkdownTab : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit VMarkdownTab(QWidget *p_parent = 0);
|
||||||
|
bool loadConfiguration();
|
||||||
|
bool saveConfiguration();
|
||||||
|
|
||||||
|
// Default note open mode for markdown.
|
||||||
|
QComboBox *m_openModeCombo;
|
||||||
|
|
||||||
|
// Whether enable heading sequence.
|
||||||
|
QCheckBox *m_headingSequence;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool loadOpenMode();
|
||||||
|
bool saveOpenMode();
|
||||||
|
|
||||||
|
bool loadHeadingSequence();
|
||||||
|
bool saveHeadingSequence();
|
||||||
|
};
|
||||||
|
|
||||||
class VSettingsDialog : public QDialog
|
class VSettingsDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -109,7 +126,10 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
void loadConfiguration();
|
void loadConfiguration();
|
||||||
|
|
||||||
QTabWidget *m_tabs;
|
void addTab(QWidget *p_widget, const QString &p_label);
|
||||||
|
|
||||||
|
QStackedLayout *m_tabs;
|
||||||
|
QListWidget *m_tabList;
|
||||||
QDialogButtonBox *m_btnBox;
|
QDialogButtonBox *m_btnBox;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -278,9 +278,56 @@ void HGMarkdownHighlighter::initImageRegionsFromResult()
|
|||||||
m_imageRegions.resize(idx);
|
m_imageRegions.resize(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit imageLinksUpdated(m_imageRegions);
|
|
||||||
|
|
||||||
qDebug() << "highlighter: parse" << m_imageRegions.size() << "image regions";
|
qDebug() << "highlighter: parse" << m_imageRegions.size() << "image regions";
|
||||||
|
|
||||||
|
emit imageLinksUpdated(m_imageRegions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HGMarkdownHighlighter::initHeaderRegionsFromResult()
|
||||||
|
{
|
||||||
|
if (!result) {
|
||||||
|
// From Qt5.7, the capacity is preserved.
|
||||||
|
m_headerRegions.clear();
|
||||||
|
emit headersUpdated(m_headerRegions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
int oriSize = m_headerRegions.size();
|
||||||
|
pmh_element_type hx[6] = {pmh_H1, pmh_H2, pmh_H3, pmh_H4, pmh_H5, pmh_H6};
|
||||||
|
for (int i = 0; i < 6; ++i) {
|
||||||
|
pmh_element *elem = result[hx[i]];
|
||||||
|
while (elem != NULL) {
|
||||||
|
if (elem->end <= elem->pos) {
|
||||||
|
elem = elem->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx < oriSize) {
|
||||||
|
// Try to reuse the original element.
|
||||||
|
VElementRegion ® = m_headerRegions[idx];
|
||||||
|
if ((int)elem->pos != reg.m_startPos || (int)elem->end != reg.m_endPos) {
|
||||||
|
reg.m_startPos = (int)elem->pos;
|
||||||
|
reg.m_endPos = (int)elem->end;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_headerRegions.push_back(VElementRegion(elem->pos, elem->end));
|
||||||
|
}
|
||||||
|
|
||||||
|
++idx;
|
||||||
|
elem = elem->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx < oriSize) {
|
||||||
|
m_headerRegions.resize(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(m_headerRegions.begin(), m_headerRegions.end());
|
||||||
|
|
||||||
|
qDebug() << "highlighter: parse" << m_headerRegions.size() << "header regions";
|
||||||
|
|
||||||
|
emit headersUpdated(m_headerRegions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HGMarkdownHighlighter::initBlockHighlihgtOne(unsigned long pos, unsigned long end, int styleIndex)
|
void HGMarkdownHighlighter::initBlockHighlihgtOne(unsigned long pos, unsigned long end, int styleIndex)
|
||||||
@ -399,6 +446,8 @@ void HGMarkdownHighlighter::parse()
|
|||||||
|
|
||||||
initImageRegionsFromResult();
|
initImageRegionsFromResult();
|
||||||
|
|
||||||
|
initHeaderRegionsFromResult();
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
pmh_free_elements(result);
|
pmh_free_elements(result);
|
||||||
result = NULL;
|
result = NULL;
|
||||||
|
@ -106,6 +106,17 @@ struct VElementRegion
|
|||||||
return (m_startPos == p_other.m_startPos
|
return (m_startPos == p_other.m_startPos
|
||||||
&& m_endPos == p_other.m_endPos);
|
&& m_endPos == p_other.m_endPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator<(const VElementRegion &p_other) const
|
||||||
|
{
|
||||||
|
if (m_startPos < p_other.m_startPos) {
|
||||||
|
return true;
|
||||||
|
} else if (m_startPos == p_other.m_startPos) {
|
||||||
|
return m_endPos <= p_other.m_endPos;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class HGMarkdownHighlighter : public QSyntaxHighlighter
|
class HGMarkdownHighlighter : public QSyntaxHighlighter
|
||||||
@ -133,6 +144,9 @@ signals:
|
|||||||
// Emitted when image regions have been fetched from a new parsing result.
|
// Emitted when image regions have been fetched from a new parsing result.
|
||||||
void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions);
|
void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions);
|
||||||
|
|
||||||
|
// Emitted when header regions have been fetched from a new parsing result.
|
||||||
|
void headersUpdated(const QVector<VElementRegion> &p_headerRegions);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
|
void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
@ -174,6 +188,11 @@ private:
|
|||||||
// All image link regions.
|
// All image link regions.
|
||||||
QVector<VElementRegion> m_imageRegions;
|
QVector<VElementRegion> m_imageRegions;
|
||||||
|
|
||||||
|
// All header regions.
|
||||||
|
// May contains illegal elements.
|
||||||
|
// Sorted by start position.
|
||||||
|
QVector<VElementRegion> m_headerRegions;
|
||||||
|
|
||||||
// Timer to signal highlightCompleted().
|
// Timer to signal highlightCompleted().
|
||||||
QTimer *m_completeTimer;
|
QTimer *m_completeTimer;
|
||||||
|
|
||||||
@ -211,6 +230,9 @@ private:
|
|||||||
// Fetch all the image link regions from parsing result.
|
// Fetch all the image link regions from parsing result.
|
||||||
void initImageRegionsFromResult();
|
void initImageRegionsFromResult();
|
||||||
|
|
||||||
|
// Fetch all the header regions from parsing result.
|
||||||
|
void initHeaderRegionsFromResult();
|
||||||
|
|
||||||
// Whether @p_block is totally inside a HTML comment.
|
// Whether @p_block is totally inside a HTML comment.
|
||||||
bool isBlockInsideCommentRegion(const QTextBlock &p_block) const;
|
bool isBlockInsideCommentRegion(const QTextBlock &p_block) const;
|
||||||
|
|
||||||
|
@ -189,4 +189,4 @@ VNote supports following features of Vim:
|
|||||||
|
|
||||||
For now, VNote does **NOT** support the macro and repeat(`.`) features of Vim.
|
For now, VNote does **NOT** support the macro and repeat(`.`) features of Vim.
|
||||||
|
|
||||||
Enjoy Vim on VNote!
|
Enjoy Vim in VNote!
|
||||||
|
@ -83,6 +83,9 @@ insert_title_from_note_name=true
|
|||||||
; 0 - Read, 1 - Edit
|
; 0 - Read, 1 - Edit
|
||||||
note_open_mode=0
|
note_open_mode=0
|
||||||
|
|
||||||
|
; Whether auto generate heading sequence
|
||||||
|
enable_heading_sequence=false
|
||||||
|
|
||||||
[session]
|
[session]
|
||||||
tools_dock_checked=true
|
tools_dock_checked=true
|
||||||
|
|
||||||
|
@ -39,9 +39,9 @@ const QString VUtils::c_fencedCodeBlockEndRegExp = QString("^(\\s*)```$");
|
|||||||
|
|
||||||
const QString VUtils::c_previewImageBlockRegExp = QString("[\\n|^][ |\\t]*\\xfffc[ |\\t]*(?=\\n)");
|
const QString VUtils::c_previewImageBlockRegExp = QString("[\\n|^][ |\\t]*\\xfffc[ |\\t]*(?=\\n)");
|
||||||
|
|
||||||
VUtils::VUtils()
|
const QString VUtils::c_headerRegExp = QString("^(#{1,6})\\s+(((\\d+\\.)+(?=\\s))?\\s?\\S.*)\\s*$");
|
||||||
{
|
|
||||||
}
|
const QString VUtils::c_headerPrefixRegExp = QString("^(#{1,6}\\s+((\\d+\\.)+(?=\\s))?\\s?)\\S.*\\s*$");
|
||||||
|
|
||||||
void VUtils::initAvailableLanguage()
|
void VUtils::initAvailableLanguage()
|
||||||
{
|
{
|
||||||
|
@ -144,8 +144,21 @@ public:
|
|||||||
// Regular expression for preview image block.
|
// Regular expression for preview image block.
|
||||||
static const QString c_previewImageBlockRegExp;
|
static const QString c_previewImageBlockRegExp;
|
||||||
|
|
||||||
|
// Regular expression for header block.
|
||||||
|
// Captured texts:
|
||||||
|
// 1. Header marker (##);
|
||||||
|
// 2. Header Title (need to be trimmed);
|
||||||
|
// 3. Header Sequence (1.1., 1.2., optional);
|
||||||
|
// 4. Unused;
|
||||||
|
static const QString c_headerRegExp;
|
||||||
|
|
||||||
|
// Regular expression for header block.
|
||||||
|
// Captured texts:
|
||||||
|
// 1. prefix till the real header title content;
|
||||||
|
static const QString c_headerPrefixRegExp;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VUtils();
|
VUtils() {}
|
||||||
|
|
||||||
static void initAvailableLanguage();
|
static void initAvailableLanguage();
|
||||||
|
|
||||||
|
@ -174,6 +174,9 @@ void VConfigManager::initialize()
|
|||||||
} else {
|
} else {
|
||||||
m_noteOpenMode = OpenFileMode::Read;
|
m_noteOpenMode = OpenFileMode::Read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_enableHeadingSequence = getConfigFromSettings("global",
|
||||||
|
"enable_heading_sequence").toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VConfigManager::readPredefinedColorsFromSettings()
|
void VConfigManager::readPredefinedColorsFromSettings()
|
||||||
|
@ -232,6 +232,9 @@ public:
|
|||||||
OpenFileMode getNoteOpenMode() const;
|
OpenFileMode getNoteOpenMode() const;
|
||||||
void setNoteOpenMode(OpenFileMode p_mode);
|
void setNoteOpenMode(OpenFileMode p_mode);
|
||||||
|
|
||||||
|
bool getEnableHeadingSequence() const;
|
||||||
|
void setEnableHeadingSequence(bool p_enabled);
|
||||||
|
|
||||||
// Return the configured key sequence of @p_operation.
|
// Return the configured key sequence of @p_operation.
|
||||||
// Return empty if there is no corresponding config.
|
// Return empty if there is no corresponding config.
|
||||||
QString getShortcutKeySequence(const QString &p_operation) const;
|
QString getShortcutKeySequence(const QString &p_operation) const;
|
||||||
@ -469,6 +472,9 @@ private:
|
|||||||
// Default mode when opening a note.
|
// Default mode when opening a note.
|
||||||
OpenFileMode m_noteOpenMode;
|
OpenFileMode m_noteOpenMode;
|
||||||
|
|
||||||
|
// Whether auto genearte heading sequence.
|
||||||
|
bool m_enableHeadingSequence;
|
||||||
|
|
||||||
// The name of the config file in each directory, obsolete.
|
// The name of the config file in each directory, obsolete.
|
||||||
// Use c_dirConfigFile instead.
|
// Use c_dirConfigFile instead.
|
||||||
static const QString c_obsoleteDirConfigFile;
|
static const QString c_obsoleteDirConfigFile;
|
||||||
@ -1204,4 +1210,20 @@ inline void VConfigManager::setNoteOpenMode(OpenFileMode p_mode)
|
|||||||
m_noteOpenMode == OpenFileMode::Read ? 0 : 1);
|
m_noteOpenMode == OpenFileMode::Read ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool VConfigManager::getEnableHeadingSequence() const
|
||||||
|
{
|
||||||
|
return m_enableHeadingSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VConfigManager::setEnableHeadingSequence(bool p_enabled)
|
||||||
|
{
|
||||||
|
if (m_enableHeadingSequence == p_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_enableHeadingSequence = p_enabled;
|
||||||
|
setConfigToSettings("global", "enable_heading_sequence",
|
||||||
|
m_enableHeadingSequence);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // VCONFIGMANAGER_H
|
#endif // VCONFIGMANAGER_H
|
||||||
|
115
src/vmdedit.cpp
115
src/vmdedit.cpp
@ -31,8 +31,9 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
g_config->getCodeBlockStyles(),
|
g_config->getCodeBlockStyles(),
|
||||||
g_config->getMarkdownHighlightInterval(),
|
g_config->getMarkdownHighlightInterval(),
|
||||||
document());
|
document());
|
||||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
|
|
||||||
this, &VMdEdit::generateEditOutline);
|
connect(m_mdHighlighter, &HGMarkdownHighlighter::headersUpdated,
|
||||||
|
this, &VMdEdit::updateOutline);
|
||||||
|
|
||||||
// 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.
|
||||||
@ -99,9 +100,6 @@ void VMdEdit::beginEdit()
|
|||||||
|
|
||||||
setModified(false);
|
setModified(false);
|
||||||
|
|
||||||
// Request update outline.
|
|
||||||
generateEditOutline();
|
|
||||||
|
|
||||||
if (m_freshEdit) {
|
if (m_freshEdit) {
|
||||||
// Will set to false when all async jobs completed.
|
// Will set to false when all async jobs completed.
|
||||||
setReadOnly(true);
|
setReadOnly(true);
|
||||||
@ -110,6 +108,8 @@ void VMdEdit::beginEdit()
|
|||||||
} else {
|
} else {
|
||||||
setReadOnly(false);
|
setReadOnly(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_mdHighlighter->updateHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEdit::endEdit()
|
void VMdEdit::endEdit()
|
||||||
@ -330,24 +330,98 @@ void VMdEdit::updateCurHeader()
|
|||||||
emit curHeaderChanged(VAnchor(m_file, "", m_headers[idx].lineNumber, m_headers[idx].index));
|
emit curHeaderChanged(VAnchor(m_file, "", m_headers[idx].lineNumber, m_headers[idx].index));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEdit::generateEditOutline()
|
static void addHeaderSequence(QVector<int> &p_sequence, int p_level)
|
||||||
|
{
|
||||||
|
Q_ASSERT(p_level >= 1 && p_level < p_sequence.size());
|
||||||
|
++p_sequence[p_level];
|
||||||
|
for (int i = p_level + 1; i < p_sequence.size(); ++i) {
|
||||||
|
p_sequence[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString headerSequenceStr(const QVector<int> &p_sequence)
|
||||||
|
{
|
||||||
|
QString res;
|
||||||
|
for (int i = 1; i < p_sequence.size(); ++i) {
|
||||||
|
if (p_sequence[i] != 0) {
|
||||||
|
res = res + QString::number(p_sequence[i]) + '.';
|
||||||
|
} else if (res.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void insertSequenceToHeader(QTextBlock &p_block,
|
||||||
|
QRegExp &p_reg,
|
||||||
|
QRegExp &p_preReg,
|
||||||
|
const QString &p_seq)
|
||||||
|
{
|
||||||
|
if (!p_block.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text = p_block.text();
|
||||||
|
bool matched = p_reg.exactMatch(text);
|
||||||
|
Q_ASSERT(matched);
|
||||||
|
|
||||||
|
matched = p_preReg.exactMatch(text);
|
||||||
|
Q_ASSERT(matched);
|
||||||
|
|
||||||
|
int start = p_reg.cap(1).length() + 1;
|
||||||
|
int end = p_preReg.cap(1).length();
|
||||||
|
|
||||||
|
Q_ASSERT(start <= end);
|
||||||
|
|
||||||
|
QTextCursor cursor(p_block);
|
||||||
|
cursor.setPosition(p_block.position() + start);
|
||||||
|
if (start != end) {
|
||||||
|
cursor.setPosition(p_block.position() + end, QTextCursor::KeepAnchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.insertText(p_seq + ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEdit::updateOutline(const QVector<VElementRegion> &p_headerRegions)
|
||||||
{
|
{
|
||||||
QTextDocument *doc = document();
|
QTextDocument *doc = document();
|
||||||
|
|
||||||
QVector<VHeader> headers;
|
QVector<VHeader> headers;
|
||||||
|
QVector<int> headerBlockNumbers;
|
||||||
|
QVector<QString> headerSequences;
|
||||||
|
if (!p_headerRegions.isEmpty()) {
|
||||||
|
headers.reserve(p_headerRegions.size());
|
||||||
|
headerBlockNumbers.reserve(p_headerRegions.size());
|
||||||
|
headerSequences.reserve(p_headerRegions.size());
|
||||||
|
}
|
||||||
|
|
||||||
// Assume that each block contains only one line
|
// Assume that each block contains only one line
|
||||||
// Only support # syntax for now
|
// Only support # syntax for now
|
||||||
QRegExp headerReg("(#{1,6})\\s+(\\S.*)"); // Need to trim the spaces
|
QRegExp headerReg(VUtils::c_headerRegExp);
|
||||||
int baseLevel = -1;
|
int baseLevel = -1;
|
||||||
for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
|
for (auto const & reg : p_headerRegions) {
|
||||||
V_ASSERT(block.lineCount() == 1);
|
QTextBlock block = doc->findBlock(reg.m_startPos);
|
||||||
|
if (!block.isValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(block.lineCount() == 1);
|
||||||
|
|
||||||
|
if (!block.contains(reg.m_endPos - 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
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(),
|
VHeader header(level, headerReg.cap(2).trimmed(),
|
||||||
"", block.firstLineNumber(), headers.size());
|
"", block.firstLineNumber(), headers.size());
|
||||||
headers.append(header);
|
headers.append(header);
|
||||||
|
headerBlockNumbers.append(block.blockNumber());
|
||||||
|
headerSequences.append(headerReg.cap(3));
|
||||||
|
|
||||||
if (baseLevel == -1) {
|
if (baseLevel == -1) {
|
||||||
baseLevel = level;
|
baseLevel = level;
|
||||||
@ -359,18 +433,37 @@ void VMdEdit::generateEditOutline()
|
|||||||
|
|
||||||
m_headers.clear();
|
m_headers.clear();
|
||||||
|
|
||||||
|
bool autoSequence = g_config->getEnableHeadingSequence() && !isReadOnly();
|
||||||
|
QVector<int> seqs(7, 0);
|
||||||
|
QRegExp preReg(VUtils::c_headerPrefixRegExp);
|
||||||
int curLevel = baseLevel - 1;
|
int curLevel = baseLevel - 1;
|
||||||
for (auto & item : headers) {
|
for (int i = 0; i < headers.size(); ++i) {
|
||||||
|
VHeader &item = headers[i];
|
||||||
while (item.level > curLevel + 1) {
|
while (item.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(VHeader(curLevel, c_emptyHeaderName, "", -1, m_headers.size()));
|
||||||
|
if (autoSequence) {
|
||||||
|
addHeaderSequence(seqs, curLevel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item.index = m_headers.size();
|
item.index = m_headers.size();
|
||||||
m_headers.append(item);
|
m_headers.append(item);
|
||||||
curLevel = item.level;
|
curLevel = item.level;
|
||||||
|
if (autoSequence) {
|
||||||
|
addHeaderSequence(seqs, item.level);
|
||||||
|
|
||||||
|
QString seqStr = headerSequenceStr(seqs);
|
||||||
|
if (headerSequences[i] != seqStr) {
|
||||||
|
// Insert correct sequence.
|
||||||
|
insertSequenceToHeader(doc->findBlockByNumber(headerBlockNumbers[i]),
|
||||||
|
headerReg,
|
||||||
|
preReg,
|
||||||
|
seqStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit headersChanged(m_headers);
|
emit headersChanged(m_headers);
|
||||||
@ -670,10 +763,10 @@ void VMdEdit::finishOneAsyncJob(int p_idx)
|
|||||||
m_finishedAsyncJobs[p_idx] = true;
|
m_finishedAsyncJobs[p_idx] = true;
|
||||||
if (-1 == m_finishedAsyncJobs.indexOf(false)) {
|
if (-1 == m_finishedAsyncJobs.indexOf(false)) {
|
||||||
// All jobs finished.
|
// All jobs finished.
|
||||||
m_freshEdit = false;
|
|
||||||
setUndoRedoEnabled(true);
|
setUndoRedoEnabled(true);
|
||||||
setReadOnly(false);
|
setReadOnly(false);
|
||||||
setModified(false);
|
setModified(false);
|
||||||
|
m_freshEdit = false;
|
||||||
emit statusChanged();
|
emit statusChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ signals:
|
|||||||
void statusChanged();
|
void statusChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void generateEditOutline();
|
void updateOutline(const QVector<VElementRegion> &p_headerRegions);
|
||||||
|
|
||||||
// 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 updateCurHeader();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user