mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
Fixes
1. OutlineViewer: support section number; 2. Allow to close the file before opening with external program; 3. Skip end marker within a block marker;
This commit is contained in:
parent
7cc31a5d6e
commit
6109737b9d
@ -1 +1 @@
|
||||
Subproject commit 77cf66845ac2dee3e49cee440f5a6b40b777e8bd
|
||||
Subproject commit f17e9bf898d86c2990b5831dea45673c3269030b
|
@ -107,6 +107,8 @@ namespace vnotex
|
||||
|
||||
void pinToQuickAccessRequested(const QStringList &p_files);
|
||||
|
||||
void closeFileRequested(const QString &p_filePath, const QSharedPointer<Event> &p_event);
|
||||
|
||||
private:
|
||||
explicit VNoteX(QObject *p_parent = nullptr);
|
||||
|
||||
|
@ -18,19 +18,29 @@ void WidgetConfig::init(const QJsonObject &p_app,
|
||||
const auto appObj = p_app.value(m_sessionName).toObject();
|
||||
const auto userObj = p_user.value(m_sessionName).toObject();
|
||||
|
||||
m_outlineAutoExpandedLevel = READINT(QStringLiteral("outline_auto_expanded_level"));
|
||||
if (m_outlineAutoExpandedLevel < 0 || m_outlineAutoExpandedLevel > 6) {
|
||||
m_outlineAutoExpandedLevel = 6;
|
||||
{
|
||||
m_outlineAutoExpandedLevel = READINT(QStringLiteral("outline_auto_expanded_level"));
|
||||
if (m_outlineAutoExpandedLevel < 0 || m_outlineAutoExpandedLevel > 6) {
|
||||
m_outlineAutoExpandedLevel = 6;
|
||||
}
|
||||
|
||||
m_outlineSectionNumberEnabled = READBOOL(QStringLiteral("outline_section_number_enabled"));
|
||||
}
|
||||
|
||||
m_findAndReplaceOptions = static_cast<FindOptions>(READINT(QStringLiteral("find_and_replace_options")));
|
||||
|
||||
m_nodeExplorerViewOrder = READINT(QStringLiteral("node_explorer_view_order"));
|
||||
m_nodeExplorerRecycleBinNodeVisible = READBOOL(QStringLiteral("node_explorer_recycle_bin_node_visible"));
|
||||
m_nodeExplorerExternalFilesVisible = READBOOL(QStringLiteral("node_explorer_external_files_visible"));
|
||||
m_nodeExplorerAutoImportExternalFilesEnabled = READBOOL(QStringLiteral("node_explorer_auto_import_external_files_enabled"));
|
||||
{
|
||||
m_nodeExplorerViewOrder = READINT(QStringLiteral("node_explorer_view_order"));
|
||||
m_nodeExplorerRecycleBinNodeVisible = READBOOL(QStringLiteral("node_explorer_recycle_bin_node_visible"));
|
||||
m_nodeExplorerExternalFilesVisible = READBOOL(QStringLiteral("node_explorer_external_files_visible"));
|
||||
m_nodeExplorerAutoImportExternalFilesEnabled = READBOOL(QStringLiteral("node_explorer_auto_import_external_files_enabled"));
|
||||
m_nodeExplorerCloseBeforeOpenWithEnabled = READBOOL(QStringLiteral("node_explorer_close_before_open_with_enabled"));
|
||||
}
|
||||
|
||||
m_searchPanelAdvancedSettingsVisible = READBOOL(QStringLiteral("search_panel_advanced_settings_visible"));
|
||||
|
||||
m_mainWindowKeepDocksExpandingContentArea = READSTRLIST(QStringLiteral("main_window_keep_docks_expanding_content_area"));
|
||||
|
||||
m_snippetPanelBuiltInSnippetsVisible = READBOOL(QStringLiteral("snippet_panel_builtin_snippets_visible"));
|
||||
}
|
||||
|
||||
@ -38,11 +48,16 @@ QJsonObject WidgetConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("outline_auto_expanded_level")] = m_outlineAutoExpandedLevel;
|
||||
obj[QStringLiteral("outline_section_number_enabled")] = m_outlineSectionNumberEnabled;
|
||||
|
||||
obj[QStringLiteral("find_and_replace_options")] = static_cast<int>(m_findAndReplaceOptions);
|
||||
|
||||
obj[QStringLiteral("node_explorer_view_order")] = m_nodeExplorerViewOrder;
|
||||
obj[QStringLiteral("node_explorer_recycle_bin_node_visible")] = m_nodeExplorerRecycleBinNodeVisible;
|
||||
obj[QStringLiteral("node_explorer_external_files_visible")] = m_nodeExplorerExternalFilesVisible;
|
||||
obj[QStringLiteral("node_explorer_auto_import_external_files_enabled")] = m_nodeExplorerAutoImportExternalFilesEnabled;
|
||||
obj[QStringLiteral("node_explorer_close_before_open_with_enabled")] = m_nodeExplorerCloseBeforeOpenWithEnabled;
|
||||
|
||||
obj[QStringLiteral("search_panel_advanced_settings_visible")] = m_searchPanelAdvancedSettingsVisible;
|
||||
obj[QStringLiteral("snippet_panel_builtin_snippets_visible")] = m_snippetPanelBuiltInSnippetsVisible;
|
||||
writeStringList(obj,
|
||||
@ -61,6 +76,16 @@ void WidgetConfig::setOutlineAutoExpandedLevel(int p_level)
|
||||
updateConfig(m_outlineAutoExpandedLevel, p_level, this);
|
||||
}
|
||||
|
||||
bool WidgetConfig::getOutlineSectionNumberEnabled() const
|
||||
{
|
||||
return m_outlineSectionNumberEnabled;
|
||||
}
|
||||
|
||||
void WidgetConfig::setOutlineSectionNumberEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_outlineSectionNumberEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
FindOptions WidgetConfig::getFindAndReplaceOptions() const
|
||||
{
|
||||
return m_findAndReplaceOptions;
|
||||
@ -111,6 +136,16 @@ void WidgetConfig::setNodeExplorerAutoImportExternalFilesEnabled(bool p_enabled)
|
||||
updateConfig(m_nodeExplorerAutoImportExternalFilesEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
bool WidgetConfig::getNodeExplorerCloseBeforeOpenWithEnabled() const
|
||||
{
|
||||
return m_nodeExplorerCloseBeforeOpenWithEnabled;
|
||||
}
|
||||
|
||||
void WidgetConfig::setNodeExplorerCloseBeforeOpenWithEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_nodeExplorerCloseBeforeOpenWithEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
bool WidgetConfig::isSearchPanelAdvancedSettingsVisible() const
|
||||
{
|
||||
return m_searchPanelAdvancedSettingsVisible;
|
||||
@ -140,3 +175,4 @@ void WidgetConfig::setSnippetPanelBuiltInSnippetsVisible(bool p_visible)
|
||||
{
|
||||
updateConfig(m_snippetPanelBuiltInSnippetsVisible, p_visible, this);
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,9 @@ namespace vnotex
|
||||
int getOutlineAutoExpandedLevel() const;
|
||||
void setOutlineAutoExpandedLevel(int p_level);
|
||||
|
||||
bool getOutlineSectionNumberEnabled() const;
|
||||
void setOutlineSectionNumberEnabled(bool p_enabled);
|
||||
|
||||
FindOptions getFindAndReplaceOptions() const;
|
||||
void setFindAndReplaceOptions(FindOptions p_options);
|
||||
|
||||
@ -36,6 +39,9 @@ namespace vnotex
|
||||
bool getNodeExplorerAutoImportExternalFilesEnabled() const;
|
||||
void setNodeExplorerAutoImportExternalFilesEnabled(bool p_enabled);
|
||||
|
||||
bool getNodeExplorerCloseBeforeOpenWithEnabled() const;
|
||||
void setNodeExplorerCloseBeforeOpenWithEnabled(bool p_enabled);
|
||||
|
||||
bool isSearchPanelAdvancedSettingsVisible() const;
|
||||
void setSearchPanelAdvancedSettingsVisible(bool p_visible);
|
||||
|
||||
@ -48,6 +54,8 @@ namespace vnotex
|
||||
private:
|
||||
int m_outlineAutoExpandedLevel = 6;
|
||||
|
||||
bool m_outlineSectionNumberEnabled = false;
|
||||
|
||||
FindOptions m_findAndReplaceOptions = FindOption::FindNone;
|
||||
|
||||
int m_nodeExplorerViewOrder = 0;
|
||||
@ -58,6 +66,8 @@ namespace vnotex
|
||||
|
||||
bool m_nodeExplorerAutoImportExternalFilesEnabled = true;
|
||||
|
||||
bool m_nodeExplorerCloseBeforeOpenWithEnabled = true;
|
||||
|
||||
bool m_searchPanelAdvancedSettingsVisible = true;
|
||||
|
||||
// Object name of those docks that should be kept when expanding content area.
|
||||
|
@ -358,6 +358,8 @@
|
||||
"widget" : {
|
||||
"//comment" : "Level of the heading in outline that should expand to automatically (1-6)",
|
||||
"outline_auto_expanded_level" : 6,
|
||||
"//comment" : "Add section number in the outline viewer",
|
||||
"outline_section_number_enabled" : false,
|
||||
"//comment" : "Default find options in FindAndReplace",
|
||||
"find_and_replace_options" : 16,
|
||||
"//comment" : "View order of the node explorer",
|
||||
@ -365,6 +367,8 @@
|
||||
"node_explorer_recycle_bin_node_visible" : false,
|
||||
"node_explorer_external_files_visible" : true,
|
||||
"node_explorer_auto_import_external_files_enabled" : true,
|
||||
"//comment" : "Whether close the file before opening it with external program",
|
||||
"node_explorer_close_before_open_with_enabled" : true,
|
||||
"search_panel_advanced_settings_visible" : true,
|
||||
"//comment" : "Docks to ignore when expanding content area of main window",
|
||||
"main_window_keep_docks_expanding_content_area": [],
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <QByteArray>
|
||||
|
||||
#include <utils/utils.h>
|
||||
#include <utils/webutils.h>
|
||||
#include "githubimagehost.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -39,7 +39,8 @@ void GiteeImageHost::setConfig(const QJsonObject &p_jobj)
|
||||
{
|
||||
parseConfig(p_jobj, m_personalAccessToken, m_userName, m_repoName);
|
||||
|
||||
m_imageUrlPrefix = QString("https://gitee.com/%1/%2/raw/master/").arg(m_userName, m_repoName);
|
||||
// Do not assume the default branch.
|
||||
m_imageUrlPrefix = QString("https://gitee.com/%1/%2/raw/").arg(m_userName, m_repoName);
|
||||
}
|
||||
|
||||
bool GiteeImageHost::testConfig(const QJsonObject &p_jobj, QString &p_msg)
|
||||
@ -165,7 +166,7 @@ bool GiteeImageHost::remove(const QString &p_url, QString &p_msg)
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString resourcePath = WebUtils::purifyUrl(p_url.mid(m_imageUrlPrefix.size()));
|
||||
const auto resourcePath = GitHubImageHost::fetchResourcePath(m_imageUrlPrefix, p_url);
|
||||
|
||||
auto rawHeader = prepareCommonHeaders();
|
||||
const auto urlStr = QString("%1/repos/%2/%3/contents/%4").arg(c_apiUrl, m_userName, m_repoName, resourcePath);
|
||||
|
@ -39,7 +39,8 @@ void GitHubImageHost::setConfig(const QJsonObject &p_jobj)
|
||||
{
|
||||
parseConfig(p_jobj, m_personalAccessToken, m_userName, m_repoName);
|
||||
|
||||
m_imageUrlPrefix = QString("https://raw.githubusercontent.com/%1/%2/master/").arg(m_userName, m_repoName);
|
||||
// Do not assume the default branch.
|
||||
m_imageUrlPrefix = QString("https://raw.githubusercontent.com/%1/%2/").arg(m_userName, m_repoName);
|
||||
}
|
||||
|
||||
bool GitHubImageHost::testConfig(const QJsonObject &p_jobj, QString &p_msg)
|
||||
@ -150,6 +151,15 @@ bool GitHubImageHost::ownsUrl(const QString &p_url) const
|
||||
return p_url.startsWith(m_imageUrlPrefix);
|
||||
}
|
||||
|
||||
QString GitHubImageHost::fetchResourcePath(const QString &p_prefix, const QString &p_url)
|
||||
{
|
||||
auto resourcePath = p_url.mid(p_prefix.size());
|
||||
// Skip the branch name.
|
||||
resourcePath = resourcePath.mid(resourcePath.indexOf(QLatin1Char('/')) + 1);
|
||||
resourcePath = WebUtils::purifyUrl(resourcePath);
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
bool GitHubImageHost::remove(const QString &p_url, QString &p_msg)
|
||||
{
|
||||
Q_ASSERT(ownsUrl(p_url));
|
||||
@ -159,7 +169,7 @@ bool GitHubImageHost::remove(const QString &p_url, QString &p_msg)
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString resourcePath = WebUtils::purifyUrl(p_url.mid(m_imageUrlPrefix.size()));
|
||||
const auto resourcePath = fetchResourcePath(m_imageUrlPrefix, p_url);
|
||||
|
||||
auto rawHeader = prepareCommonHeaders(m_personalAccessToken);
|
||||
const auto urlStr = QString("%1/repos/%2/%3/contents/%4").arg(c_apiUrl, m_userName, m_repoName, resourcePath);
|
||||
|
@ -29,6 +29,8 @@ namespace vnotex
|
||||
|
||||
bool ownsUrl(const QString &p_url) const Q_DECL_OVERRIDE;
|
||||
|
||||
static QString fetchResourcePath(const QString &p_prefix, const QString &p_url);
|
||||
|
||||
protected:
|
||||
QString m_personalAccessToken;
|
||||
|
||||
|
@ -1185,44 +1185,6 @@ void MarkdownEditor::fetchImagesToLocalAndReplace(QString &p_text)
|
||||
proDlg.setValue(regs.size());
|
||||
}
|
||||
|
||||
static void increaseSectionNumber(QVector<int> &p_sectionNumber, int p_level, int p_baseLevel)
|
||||
{
|
||||
Q_ASSERT(p_level >= 1 && p_level < p_sectionNumber.size());
|
||||
if (p_level < p_baseLevel) {
|
||||
p_sectionNumber.fill(0);
|
||||
return;
|
||||
}
|
||||
|
||||
++p_sectionNumber[p_level];
|
||||
for (int i = p_level + 1; i < p_sectionNumber.size(); ++i) {
|
||||
p_sectionNumber[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static QString joinSectionNumberStr(const QVector<int> &p_sectionNumber, bool p_endingDot)
|
||||
{
|
||||
QString res;
|
||||
for (auto sec : p_sectionNumber) {
|
||||
if (sec != 0) {
|
||||
if (res.isEmpty()) {
|
||||
res = QString::number(sec);
|
||||
} else {
|
||||
res += '.' + QString::number(sec);
|
||||
}
|
||||
} else if (res.isEmpty()) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_endingDot && !res.isEmpty()) {
|
||||
return res + '.';
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static bool updateHeadingSectionNumber(QTextCursor &p_cursor,
|
||||
const QTextBlock &p_block,
|
||||
const QString &p_sectionNumber,
|
||||
@ -1269,7 +1231,7 @@ static bool updateHeadingSectionNumber(QTextCursor &p_cursor,
|
||||
|
||||
bool MarkdownEditor::updateSectionNumber(const QVector<Heading> &p_headings)
|
||||
{
|
||||
QVector<int> sectionNumber(7, 0);
|
||||
SectionNumber sectionNumber(7, 0);
|
||||
int baseLevel = m_config.getSectionNumberBaseLevel();
|
||||
if (baseLevel < 1 || baseLevel > 6) {
|
||||
baseLevel = 1;
|
||||
@ -1281,8 +1243,8 @@ bool MarkdownEditor::updateSectionNumber(const QVector<Heading> &p_headings)
|
||||
QTextCursor cursor(doc);
|
||||
cursor.beginEditBlock();
|
||||
for (const auto &heading : p_headings) {
|
||||
increaseSectionNumber(sectionNumber, heading.m_level, baseLevel);
|
||||
auto sectionStr = m_sectionNumberEnabled ? joinSectionNumberStr(sectionNumber, endingDot) : QString();
|
||||
OutlineProvider::increaseSectionNumber(sectionNumber, heading.m_level, baseLevel);
|
||||
auto sectionStr = m_sectionNumberEnabled ? OutlineProvider::joinSectionNumber(sectionNumber, endingDot) : QString();
|
||||
if (heading.m_blockNumber > -1 && sectionStr != heading.m_sectionNumber) {
|
||||
if (updateHeadingSectionNumber(cursor,
|
||||
doc->findBlockByNumber(heading.m_blockNumber),
|
||||
|
@ -828,6 +828,14 @@ QSharedPointer<Outline> MarkdownViewWindow::headingsToOutline(const QVector<T> &
|
||||
}
|
||||
}
|
||||
|
||||
const auto &markdownConfig = ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig();
|
||||
if (markdownConfig.getSectionNumberMode() == MarkdownEditorConfig::SectionNumberMode::Edit) {
|
||||
outline->m_sectionNumberBaseLevel = -1;
|
||||
} else {
|
||||
outline->m_sectionNumberBaseLevel = markdownConfig.getSectionNumberBaseLevel();
|
||||
outline->m_sectionNumberEndingDot = markdownConfig.getSectionNumberStyle() == MarkdownEditorConfig::SectionNumberStyle::DigDotDigDot;
|
||||
}
|
||||
|
||||
return outline;
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,8 @@ void NotebookExplorer::setupUI()
|
||||
&VNoteX::getInst(), &VNoteX::nodeAboutToRemove);
|
||||
connect(m_nodeExplorer, &NotebookNodeExplorer::nodeAboutToReload,
|
||||
&VNoteX::getInst(), &VNoteX::nodeAboutToReload);
|
||||
connect(m_nodeExplorer, &NotebookNodeExplorer::closeFileRequested,
|
||||
&VNoteX::getInst(), &VNoteX::closeFileRequested);
|
||||
mainLayout->addWidget(m_nodeExplorer);
|
||||
|
||||
setFocusProxy(m_nodeExplorer);
|
||||
@ -159,13 +161,23 @@ TitleBar *NotebookExplorer::setupTitleBar(QWidget *p_parent)
|
||||
subMenu,
|
||||
tr("Import External Files When Activated"),
|
||||
titleBar,
|
||||
[this](bool p_checked) {
|
||||
[](bool p_checked) {
|
||||
ConfigMgr::getInst().getWidgetConfig().setNodeExplorerAutoImportExternalFilesEnabled(p_checked);
|
||||
});
|
||||
importAct->setCheckable(true);
|
||||
importAct->setChecked(widgetConfig.getNodeExplorerAutoImportExternalFilesEnabled());
|
||||
}
|
||||
|
||||
{
|
||||
auto act = titleBar->addMenuAction(tr("Close File Before Open With External Program"),
|
||||
titleBar,
|
||||
[](bool p_checked) {
|
||||
ConfigMgr::getInst().getWidgetConfig().setNodeExplorerCloseBeforeOpenWithEnabled(p_checked);
|
||||
});
|
||||
act->setCheckable(true);
|
||||
act->setChecked(widgetConfig.getNodeExplorerCloseBeforeOpenWithEnabled());
|
||||
}
|
||||
|
||||
return titleBar;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <core/configmgr.h>
|
||||
#include <core/coreconfig.h>
|
||||
#include <core/sessionconfig.h>
|
||||
#include <core/widgetconfig.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
@ -2027,23 +2028,42 @@ void NotebookNodeExplorer::setupShortcuts()
|
||||
|
||||
void NotebookNodeExplorer::openSelectedNodesWithDefaultProgram()
|
||||
{
|
||||
const bool closeBefore = ConfigMgr::getInst().getWidgetConfig().getNodeExplorerCloseBeforeOpenWithEnabled();
|
||||
const auto files = getSelectedNodesPath();
|
||||
for (const auto &file : files) {
|
||||
if (file.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (closeBefore) {
|
||||
auto event = QSharedPointer<Event>::create();
|
||||
emit closeFileRequested(file, event);
|
||||
if (!event->m_response.toBool()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(file));
|
||||
}
|
||||
}
|
||||
|
||||
void NotebookNodeExplorer::openSelectedNodesWithExternalProgram(const QString &p_command)
|
||||
{
|
||||
const bool closeBefore = ConfigMgr::getInst().getWidgetConfig().getNodeExplorerCloseBeforeOpenWithEnabled();
|
||||
const auto files = getSelectedNodesPath();
|
||||
for (const auto &file : files) {
|
||||
if (file.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (closeBefore) {
|
||||
auto event = QSharedPointer<Event>::create();
|
||||
emit closeFileRequested(file, event);
|
||||
if (!event->m_response.toBool()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto command = p_command;
|
||||
command.replace(QStringLiteral("%1"), QString("\"%1\"").arg(file));
|
||||
ProcessUtils::startDetached(command);
|
||||
|
@ -127,6 +127,9 @@ namespace vnotex
|
||||
|
||||
void nodeAboutToReload(Node *p_node, const QSharedPointer<Event> &p_event);
|
||||
|
||||
// @p_filePath is either an external file or a node.
|
||||
void closeFileRequested(const QString &p_filePath, const QSharedPointer<Event> &p_event);
|
||||
|
||||
private:
|
||||
enum Column { Name = 0 };
|
||||
|
||||
|
@ -63,3 +63,41 @@ void OutlineProvider::setCurrentHeadingIndex(int p_idx)
|
||||
m_currentHeadingIndex = p_idx;
|
||||
emit currentHeadingChanged();
|
||||
}
|
||||
|
||||
void OutlineProvider::increaseSectionNumber(SectionNumber &p_sectionNumber, int p_level, int p_baseLevel)
|
||||
{
|
||||
Q_ASSERT(p_level >= 1 && p_level < p_sectionNumber.size());
|
||||
if (p_level < p_baseLevel) {
|
||||
p_sectionNumber.fill(0);
|
||||
return;
|
||||
}
|
||||
|
||||
++p_sectionNumber[p_level];
|
||||
for (int i = p_level + 1; i < p_sectionNumber.size(); ++i) {
|
||||
p_sectionNumber[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QString OutlineProvider::joinSectionNumber(const SectionNumber &p_sectionNumber, bool p_endingDot)
|
||||
{
|
||||
QString res;
|
||||
for (auto sec : p_sectionNumber) {
|
||||
if (sec != 0) {
|
||||
if (res.isEmpty()) {
|
||||
res = QString::number(sec);
|
||||
} else {
|
||||
res += '.' + QString::number(sec);
|
||||
}
|
||||
} else if (res.isEmpty()) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_endingDot && !res.isEmpty()) {
|
||||
return res + '.';
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
typedef QVector<int> SectionNumber;
|
||||
|
||||
// Toc content.
|
||||
struct Outline
|
||||
{
|
||||
@ -31,6 +33,12 @@ namespace vnotex
|
||||
bool isEmpty() const;
|
||||
|
||||
QVector<Heading> m_headings;
|
||||
|
||||
// 1-based.
|
||||
// -1 to disable section number by force.
|
||||
int m_sectionNumberBaseLevel = 1;
|
||||
|
||||
bool m_sectionNumberEndingDot = true;
|
||||
};
|
||||
|
||||
// Used to hold toc-related data of one ViewWindow.
|
||||
@ -53,6 +61,10 @@ namespace vnotex
|
||||
template <class T>
|
||||
static void makePerfectHeadings(const QVector<T> &p_headings, QVector<T> &p_perfectHeadings);
|
||||
|
||||
static void increaseSectionNumber(SectionNumber &p_sectionNumber, int p_level, int p_baseLevel);
|
||||
|
||||
static QString joinSectionNumber(const SectionNumber &p_sectionNumber, bool p_endingDot);
|
||||
|
||||
signals:
|
||||
void outlineChanged();
|
||||
|
||||
|
@ -14,8 +14,8 @@
|
||||
#include "treewidget.h"
|
||||
#include "titlebar.h"
|
||||
|
||||
#include "configmgr.h"
|
||||
#include "widgetconfig.h"
|
||||
#include <core/configmgr.h>
|
||||
#include <core/widgetconfig.h>
|
||||
#include "navigationmodemgr.h"
|
||||
|
||||
using namespace vnotex;
|
||||
@ -89,7 +89,7 @@ NavigationModeWrapper<QTreeWidget, QTreeWidgetItem> *OutlineViewer::getNavigatio
|
||||
|
||||
TitleBar *OutlineViewer::setupTitleBar(const QString &p_title, QWidget *p_parent)
|
||||
{
|
||||
auto titleBar = new TitleBar(p_title, false, TitleBar::Action::None, p_parent);
|
||||
auto titleBar = new TitleBar(p_title, false, TitleBar::Action::Menu, p_parent);
|
||||
titleBar->setActionButtonsAlwaysShown(true);
|
||||
|
||||
auto decreaseBtn = titleBar->addActionButton(QStringLiteral("decrease_outline_level.svg"), tr("Decrease Expansion Level"));
|
||||
@ -122,6 +122,17 @@ TitleBar *OutlineViewer::setupTitleBar(const QString &p_title, QWidget *p_parent
|
||||
showLevel();
|
||||
});
|
||||
|
||||
{
|
||||
auto act = titleBar->addMenuAction(tr("Section Number"),
|
||||
titleBar,
|
||||
[this](bool p_checked) {
|
||||
ConfigMgr::getInst().getWidgetConfig().setOutlineSectionNumberEnabled(p_checked);
|
||||
updateTree(true);
|
||||
});
|
||||
act->setCheckable(true);
|
||||
act->setChecked(ConfigMgr::getInst().getWidgetConfig().getOutlineSectionNumberEnabled());
|
||||
}
|
||||
|
||||
return titleBar;
|
||||
}
|
||||
|
||||
@ -148,27 +159,32 @@ void OutlineViewer::setOutlineProvider(const QSharedPointer<OutlineProvider> &p_
|
||||
this, [this]() {
|
||||
updateCurrentHeading(m_provider->getCurrentHeadingIndex());
|
||||
});
|
||||
if (isVisible()) {
|
||||
updateOutline(m_provider->getOutline());
|
||||
updateCurrentHeading(m_provider->getCurrentHeadingIndex());
|
||||
}
|
||||
} else {
|
||||
updateOutline(nullptr);
|
||||
updateCurrentHeading(-1);
|
||||
}
|
||||
|
||||
if (isVisible()) {
|
||||
updateTree();
|
||||
}
|
||||
}
|
||||
|
||||
void OutlineViewer::showEvent(QShowEvent *p_event)
|
||||
{
|
||||
QFrame::showEvent(p_event);
|
||||
|
||||
// Update the tree.
|
||||
updateTree();
|
||||
}
|
||||
|
||||
void OutlineViewer::updateTree(bool p_force)
|
||||
{
|
||||
if (p_force) {
|
||||
m_outline.clear();
|
||||
}
|
||||
|
||||
if (m_provider) {
|
||||
updateOutline(m_provider->getOutline());
|
||||
updateCurrentHeading(m_provider->getCurrentHeadingIndex());
|
||||
} else {
|
||||
updateOutline(nullptr);
|
||||
updateCurrentHeading(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,8 +235,24 @@ void OutlineViewer::updateTreeToOutline(QTreeWidget *p_tree, const Outline &p_ou
|
||||
return;
|
||||
}
|
||||
|
||||
int sectionNumberBaseLevel = -1;
|
||||
const auto &widgetConfig = ConfigMgr::getInst().getWidgetConfig();
|
||||
if (widgetConfig.getOutlineSectionNumberEnabled()) {
|
||||
sectionNumberBaseLevel = p_outline.m_sectionNumberBaseLevel;
|
||||
}
|
||||
|
||||
SectionNumber sectionNumber(7, 0);
|
||||
|
||||
int idx = 0;
|
||||
renderTreeAtLevel(p_outline.m_headings, idx, 1, p_tree, nullptr, nullptr);
|
||||
renderTreeAtLevel(p_outline.m_headings,
|
||||
idx,
|
||||
1,
|
||||
p_tree,
|
||||
nullptr,
|
||||
nullptr,
|
||||
sectionNumberBaseLevel,
|
||||
sectionNumber,
|
||||
p_outline.m_sectionNumberEndingDot);
|
||||
}
|
||||
|
||||
void OutlineViewer::renderTreeAtLevel(const QVector<Outline::Heading> &p_headings,
|
||||
@ -228,34 +260,60 @@ void OutlineViewer::renderTreeAtLevel(const QVector<Outline::Heading> &p_heading
|
||||
int p_level,
|
||||
QTreeWidget *p_tree,
|
||||
QTreeWidgetItem *p_parentItem,
|
||||
QTreeWidgetItem *p_lastItem)
|
||||
QTreeWidgetItem *p_lastItem,
|
||||
int p_sectionNumberBaseLevel,
|
||||
SectionNumber &p_sectionNumber,
|
||||
bool p_sectionNumberEndingDot)
|
||||
{
|
||||
while (p_index < p_headings.size()) {
|
||||
const auto &heading = p_headings[p_index];
|
||||
if (heading.m_level < p_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTreeWidgetItem *item = nullptr;
|
||||
if (heading.m_level == p_level) {
|
||||
QString sectionStr;
|
||||
if (p_sectionNumberBaseLevel > 0) {
|
||||
OutlineProvider::increaseSectionNumber(p_sectionNumber, heading.m_level, p_sectionNumberBaseLevel);
|
||||
sectionStr = OutlineProvider::joinSectionNumber(p_sectionNumber, p_sectionNumberEndingDot);
|
||||
}
|
||||
|
||||
if (p_parentItem) {
|
||||
item = new QTreeWidgetItem(p_parentItem);
|
||||
} else {
|
||||
item = new QTreeWidgetItem(p_tree);
|
||||
}
|
||||
|
||||
fillTreeItem(item, heading, p_index);
|
||||
fillTreeItem(item, heading, p_index, sectionStr);
|
||||
|
||||
p_lastItem = item;
|
||||
++p_index;
|
||||
} else if (heading.m_level < p_level) {
|
||||
return;
|
||||
} else {
|
||||
renderTreeAtLevel(p_headings, p_index, p_level + 1, p_tree, p_lastItem, nullptr);
|
||||
renderTreeAtLevel(p_headings,
|
||||
p_index,
|
||||
p_level + 1,
|
||||
p_tree,
|
||||
p_lastItem,
|
||||
nullptr,
|
||||
p_sectionNumberBaseLevel,
|
||||
p_sectionNumber,
|
||||
p_sectionNumberEndingDot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OutlineViewer::fillTreeItem(QTreeWidgetItem *p_item, const Outline::Heading &p_heading, int p_index)
|
||||
void OutlineViewer::fillTreeItem(QTreeWidgetItem *p_item,
|
||||
const Outline::Heading &p_heading,
|
||||
int p_index,
|
||||
const QString &p_sectionStr)
|
||||
{
|
||||
p_item->setData(Column::Name, Qt::UserRole, p_index);
|
||||
p_item->setText(Column::Name, p_heading.m_name);
|
||||
if (p_sectionStr.isEmpty()) {
|
||||
p_item->setText(Column::Name, p_heading.m_name);
|
||||
} else {
|
||||
p_item->setText(Column::Name, tr("%1 %2").arg(p_sectionStr, p_heading.m_name));
|
||||
}
|
||||
p_item->setToolTip(Column::Name, p_heading.m_name);
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,6 @@ namespace vnotex
|
||||
|
||||
NavigationModeWrapper<QTreeWidget, QTreeWidgetItem> *getNavigationModeWrapper();
|
||||
|
||||
static void updateTreeToOutline(QTreeWidget *p_tree, const Outline &p_outline);
|
||||
|
||||
signals:
|
||||
void focusViewArea();
|
||||
|
||||
@ -43,6 +41,8 @@ namespace vnotex
|
||||
|
||||
TitleBar *setupTitleBar(const QString &p_title, QWidget *p_parent = nullptr);
|
||||
|
||||
void updateTree(bool p_force = false);
|
||||
|
||||
void updateOutline(const QSharedPointer<Outline> &p_outline);
|
||||
|
||||
void updateCurrentHeading(int p_idx);
|
||||
@ -61,9 +61,17 @@ namespace vnotex
|
||||
int p_level,
|
||||
QTreeWidget *p_tree,
|
||||
QTreeWidgetItem *p_parentItem,
|
||||
QTreeWidgetItem *p_lastItem);
|
||||
QTreeWidgetItem *p_lastItem,
|
||||
int p_sectionNumberBaseLevel,
|
||||
SectionNumber &p_sectionNumber,
|
||||
bool p_sectionNumberEndingDot);
|
||||
|
||||
static void fillTreeItem(QTreeWidgetItem *p_item, const Outline::Heading &p_heading, int p_index);
|
||||
static void fillTreeItem(QTreeWidgetItem *p_item,
|
||||
const Outline::Heading &p_heading,
|
||||
int p_index,
|
||||
const QString &p_sectionStr);
|
||||
|
||||
static void updateTreeToOutline(QTreeWidget *p_tree, const Outline &p_outline);
|
||||
|
||||
bool m_muted = false;
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <core/events.h>
|
||||
#include <core/vnotex.h>
|
||||
#include <core/configmgr.h>
|
||||
#include <core/notebookmgr.h>
|
||||
#include <core/coreconfig.h>
|
||||
#include <core/editorconfig.h>
|
||||
#include <core/markdowneditorconfig.h>
|
||||
@ -87,6 +88,9 @@ ViewArea::ViewArea(QWidget *p_parent)
|
||||
connect(&VNoteX::getInst(), &VNoteX::nodeAboutToReload,
|
||||
this, &ViewArea::handleNodeChange);
|
||||
|
||||
connect(&VNoteX::getInst(), &VNoteX::closeFileRequested,
|
||||
this, &ViewArea::closeFile);
|
||||
|
||||
auto &configMgr = ConfigMgr::getInst();
|
||||
connect(&configMgr, &ConfigMgr::editorConfigChanged,
|
||||
this, [this]() {
|
||||
@ -931,7 +935,7 @@ bool ViewArea::close(Node *p_node, bool p_force)
|
||||
return closeIf(p_force, [p_node](ViewWindow *p_win) {
|
||||
auto buffer = p_win->getBuffer();
|
||||
return buffer->match(p_node) || buffer->isChildOf(p_node);
|
||||
}, false);
|
||||
}, true);
|
||||
}
|
||||
|
||||
bool ViewArea::close(const Notebook *p_notebook, bool p_force)
|
||||
@ -1484,3 +1488,24 @@ void ViewArea::updateHistory(const ViewWindowSession &p_session, Notebook *p_not
|
||||
p_session.m_readOnly,
|
||||
p_notebook);
|
||||
}
|
||||
|
||||
void ViewArea::closeFile(const QString &p_filePath, const QSharedPointer<Event> &p_event)
|
||||
{
|
||||
if (p_event->m_handled) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto node = VNoteX::getInst().getNotebookMgr().loadNodeByPath(p_filePath);
|
||||
bool done = false;
|
||||
if (node) {
|
||||
done = close(node.data(), false);
|
||||
} else {
|
||||
done = closeIf(false, [p_filePath](ViewWindow *p_win) {
|
||||
auto buffer = p_win->getBuffer();
|
||||
return buffer->match(p_filePath);
|
||||
}, true);
|
||||
}
|
||||
|
||||
p_event->m_response = done;
|
||||
p_event->m_handled = !done;
|
||||
}
|
||||
|
@ -139,6 +139,8 @@ namespace vnotex
|
||||
|
||||
void moveViewWindowOneSplit(ViewSplit *p_split, ViewWindow *p_win, Direction p_direction);
|
||||
|
||||
void closeFile(const QString &p_filePath, const QSharedPointer<Event> &p_event);
|
||||
|
||||
private:
|
||||
enum class SplitType
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user