From 6109737b9d44cbe2a0c85bd27ceb416580b5461d Mon Sep 17 00:00:00 2001 From: Le Tan Date: Tue, 17 Aug 2021 20:58:55 +0800 Subject: [PATCH] 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; --- libs/vtextedit | 2 +- src/core/vnotex.h | 2 + src/core/widgetconfig.cpp | 50 ++++++++++++-- src/core/widgetconfig.h | 10 +++ src/data/core/vnotex.json | 4 ++ src/imagehost/giteeimagehost.cpp | 7 +- src/imagehost/githubimagehost.cpp | 14 +++- src/imagehost/githubimagehost.h | 2 + src/widgets/editors/markdowneditor.cpp | 44 +----------- src/widgets/markdownviewwindow.cpp | 8 +++ src/widgets/notebookexplorer.cpp | 14 +++- src/widgets/notebooknodeexplorer.cpp | 20 ++++++ src/widgets/notebooknodeexplorer.h | 3 + src/widgets/outlineprovider.cpp | 38 ++++++++++ src/widgets/outlineprovider.h | 12 ++++ src/widgets/outlineviewer.cpp | 96 +++++++++++++++++++++----- src/widgets/outlineviewer.h | 16 +++-- src/widgets/viewarea.cpp | 27 +++++++- src/widgets/viewarea.h | 2 + 19 files changed, 292 insertions(+), 79 deletions(-) diff --git a/libs/vtextedit b/libs/vtextedit index 77cf6684..f17e9bf8 160000 --- a/libs/vtextedit +++ b/libs/vtextedit @@ -1 +1 @@ -Subproject commit 77cf66845ac2dee3e49cee440f5a6b40b777e8bd +Subproject commit f17e9bf898d86c2990b5831dea45673c3269030b diff --git a/src/core/vnotex.h b/src/core/vnotex.h index 7f782be8..8ab5cbe5 100644 --- a/src/core/vnotex.h +++ b/src/core/vnotex.h @@ -107,6 +107,8 @@ namespace vnotex void pinToQuickAccessRequested(const QStringList &p_files); + void closeFileRequested(const QString &p_filePath, const QSharedPointer &p_event); + private: explicit VNoteX(QObject *p_parent = nullptr); diff --git a/src/core/widgetconfig.cpp b/src/core/widgetconfig.cpp index 82d8d653..80f65896 100644 --- a/src/core/widgetconfig.cpp +++ b/src/core/widgetconfig.cpp @@ -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(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(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); } + diff --git a/src/core/widgetconfig.h b/src/core/widgetconfig.h index 0a13455d..c9378078 100644 --- a/src/core/widgetconfig.h +++ b/src/core/widgetconfig.h @@ -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. diff --git a/src/data/core/vnotex.json b/src/data/core/vnotex.json index 474278ea..9d56db6d 100644 --- a/src/data/core/vnotex.json +++ b/src/data/core/vnotex.json @@ -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": [], diff --git a/src/imagehost/giteeimagehost.cpp b/src/imagehost/giteeimagehost.cpp index e8a4d341..a0eb9598 100644 --- a/src/imagehost/giteeimagehost.cpp +++ b/src/imagehost/giteeimagehost.cpp @@ -5,7 +5,7 @@ #include #include -#include +#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); diff --git a/src/imagehost/githubimagehost.cpp b/src/imagehost/githubimagehost.cpp index 4a8aff81..06cfed30 100644 --- a/src/imagehost/githubimagehost.cpp +++ b/src/imagehost/githubimagehost.cpp @@ -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); diff --git a/src/imagehost/githubimagehost.h b/src/imagehost/githubimagehost.h index 42400051..fb273cae 100644 --- a/src/imagehost/githubimagehost.h +++ b/src/imagehost/githubimagehost.h @@ -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; diff --git a/src/widgets/editors/markdowneditor.cpp b/src/widgets/editors/markdowneditor.cpp index 86fa8d25..1f99b15d 100644 --- a/src/widgets/editors/markdowneditor.cpp +++ b/src/widgets/editors/markdowneditor.cpp @@ -1185,44 +1185,6 @@ void MarkdownEditor::fetchImagesToLocalAndReplace(QString &p_text) proDlg.setValue(regs.size()); } -static void increaseSectionNumber(QVector &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 &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 &p_headings) { - QVector 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 &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), diff --git a/src/widgets/markdownviewwindow.cpp b/src/widgets/markdownviewwindow.cpp index 56411b4f..d567f905 100644 --- a/src/widgets/markdownviewwindow.cpp +++ b/src/widgets/markdownviewwindow.cpp @@ -828,6 +828,14 @@ QSharedPointer MarkdownViewWindow::headingsToOutline(const QVector & } } + 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; } diff --git a/src/widgets/notebookexplorer.cpp b/src/widgets/notebookexplorer.cpp index 9a2a17d5..c32fd1cc 100644 --- a/src/widgets/notebookexplorer.cpp +++ b/src/widgets/notebookexplorer.cpp @@ -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; } diff --git a/src/widgets/notebooknodeexplorer.cpp b/src/widgets/notebooknodeexplorer.cpp index 758db989..96f43004 100644 --- a/src/widgets/notebooknodeexplorer.cpp +++ b/src/widgets/notebooknodeexplorer.cpp @@ -34,6 +34,7 @@ #include #include #include +#include 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::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::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); diff --git a/src/widgets/notebooknodeexplorer.h b/src/widgets/notebooknodeexplorer.h index d5a28e68..7911dda1 100644 --- a/src/widgets/notebooknodeexplorer.h +++ b/src/widgets/notebooknodeexplorer.h @@ -127,6 +127,9 @@ namespace vnotex void nodeAboutToReload(Node *p_node, const QSharedPointer &p_event); + // @p_filePath is either an external file or a node. + void closeFileRequested(const QString &p_filePath, const QSharedPointer &p_event); + private: enum Column { Name = 0 }; diff --git a/src/widgets/outlineprovider.cpp b/src/widgets/outlineprovider.cpp index 5eea70f2..c4f73ea2 100644 --- a/src/widgets/outlineprovider.cpp +++ b/src/widgets/outlineprovider.cpp @@ -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; + } +} diff --git a/src/widgets/outlineprovider.h b/src/widgets/outlineprovider.h index 2993db06..7719b54e 100644 --- a/src/widgets/outlineprovider.h +++ b/src/widgets/outlineprovider.h @@ -7,6 +7,8 @@ namespace vnotex { + typedef QVector SectionNumber; + // Toc content. struct Outline { @@ -31,6 +33,12 @@ namespace vnotex bool isEmpty() const; QVector 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 static void makePerfectHeadings(const QVector &p_headings, QVector &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(); diff --git a/src/widgets/outlineviewer.cpp b/src/widgets/outlineviewer.cpp index 2b9b59f4..e77bd50e 100644 --- a/src/widgets/outlineviewer.cpp +++ b/src/widgets/outlineviewer.cpp @@ -14,8 +14,8 @@ #include "treewidget.h" #include "titlebar.h" -#include "configmgr.h" -#include "widgetconfig.h" +#include +#include #include "navigationmodemgr.h" using namespace vnotex; @@ -89,7 +89,7 @@ NavigationModeWrapper *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 &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 &p_headings, @@ -228,34 +260,60 @@ void OutlineViewer::renderTreeAtLevel(const QVector &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); } diff --git a/src/widgets/outlineviewer.h b/src/widgets/outlineviewer.h index 58926709..c5fde76c 100644 --- a/src/widgets/outlineviewer.h +++ b/src/widgets/outlineviewer.h @@ -28,8 +28,6 @@ namespace vnotex NavigationModeWrapper *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 &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; diff --git a/src/widgets/viewarea.cpp b/src/widgets/viewarea.cpp index 6f12bc76..a83c13cd 100644 --- a/src/widgets/viewarea.cpp +++ b/src/widgets/viewarea.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -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 &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; +} diff --git a/src/widgets/viewarea.h b/src/widgets/viewarea.h index 2a2d7fbc..faeb37fd 100644 --- a/src/widgets/viewarea.h +++ b/src/widgets/viewarea.h @@ -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 &p_event); + private: enum class SplitType {