소스 검색

QuickAccess: add quick note (#2373)

* feature_quick_create_note

* adj name and complete quick create

* del unused

* del unused

* adj name

* adj to const

* adj name 2

* adj

* fix

---------

Co-authored-by: Le Tan <[email protected]>
chendapao 2 년 전
부모
커밋
e8fe0726ff

+ 4 - 0
src/core/notebookconfigmgr/vxnotebookconfigmgr.cpp

@@ -461,6 +461,10 @@ void VXNotebookConfigMgr::addChildNode(Node *p_parent, const QSharedPointer<Node
 QSharedPointer<Node> VXNotebookConfigMgr::loadNodeByPath(const QSharedPointer<Node> &p_root, const QString &p_relativePath)
 {
     auto p = PathUtils::cleanPath(p_relativePath);
+    if (p == ".") {
+        return p_root;
+    }
+
     auto paths = p.split('/', QString::SkipEmptyParts);
     auto node = p_root;
     for (auto &pa : paths) {

+ 59 - 0
src/core/sessionconfig.cpp

@@ -38,6 +38,34 @@ QJsonObject SessionConfig::NotebookItem::toJson() const
     return jobj;
 }
 
+bool SessionConfig::QuickNoteScheme::operator==(const QuickNoteScheme &p_other) const
+{
+    return m_name == p_other.m_name &&
+        m_folderPath == p_other.m_folderPath &&
+        m_noteName == p_other.m_noteName &&
+        m_template == p_other.m_template;
+}
+
+void SessionConfig::QuickNoteScheme::fromJson(const QJsonObject &p_jobj)
+{
+    m_name = p_jobj[QStringLiteral("name")].toString();
+    m_folderPath = p_jobj[QStringLiteral("folder_path")].toString();
+    m_noteName = p_jobj[QStringLiteral("note_name")].toString();
+    m_template = p_jobj[QStringLiteral("template")].toString();
+}
+
+QJsonObject SessionConfig::QuickNoteScheme::toJson() const
+{
+    QJsonObject jobj;
+
+    jobj[QStringLiteral("name")] = m_name;
+    jobj[QStringLiteral("folder_path")] = m_folderPath;
+    jobj[QStringLiteral("note_name")] = m_noteName;
+    jobj[QStringLiteral("template")] = m_template;
+
+    return jobj;
+}
+
 void SessionConfig::ExternalProgram::fromJson(const QJsonObject &p_jobj)
 {
     m_name = p_jobj[QStringLiteral("name")].toString();
@@ -97,6 +125,8 @@ void SessionConfig::init()
 
     loadHistory(sessionJobj);
 
+    loadQuickNoteSchemes(sessionJobj);
+
     if (MainConfig::isVersionChanged()) {
         doVersionSpecificOverride();
     }
@@ -235,6 +265,7 @@ QJsonObject SessionConfig::toJson() const
     writeByteArray(obj, QStringLiteral("notebook_explorer_session"), m_notebookExplorerSession);
     obj[QStringLiteral("external_programs")] = saveExternalPrograms();
     obj[QStringLiteral("history")] = saveHistory();
+    obj[QStringLiteral("quick_note_schemes")] = saveQuickNoteSchemes();
     return obj;
 }
 
@@ -458,6 +489,24 @@ QJsonArray SessionConfig::saveExternalPrograms() const
     return arr;
 }
 
+void SessionConfig::loadQuickNoteSchemes(const QJsonObject &p_session)
+{
+    const auto arr = p_session.value(QStringLiteral("quick_note_schemes")).toArray();
+    m_quickNoteSchemes.resize(arr.size());
+    for (int i = 0; i < arr.size(); ++i) {
+        m_quickNoteSchemes[i].fromJson(arr[i].toObject());
+    }
+}
+
+QJsonArray SessionConfig::saveQuickNoteSchemes() const
+{
+    QJsonArray arr;
+    for (const auto &scheme : m_quickNoteSchemes) {
+        arr.append(scheme.toJson());
+    }
+    return arr;
+}
+
 const QVector<SessionConfig::ExternalProgram> &SessionConfig::getExternalPrograms() const
 {
     return m_externalPrograms;
@@ -541,3 +590,13 @@ QJsonObject SessionConfig::saveExportOption() const
 
     return obj;
 }
+
+const QVector<SessionConfig::QuickNoteScheme> &SessionConfig::getQuickNoteSchemes() const
+{
+    return m_quickNoteSchemes;
+}
+
+void SessionConfig::setQuickNoteSchemes(const QVector<QuickNoteScheme>& p_schemes)
+{
+    updateConfig(m_quickNoteSchemes, p_schemes, this);
+}

+ 29 - 1
src/core/sessionconfig.h

@@ -55,6 +55,25 @@ namespace vnotex
             QByteArray m_locationListState;
         };
 
+        struct QuickNoteScheme
+        {
+            bool operator==(const QuickNoteScheme &p_other) const;
+
+            void fromJson(const QJsonObject &p_jobj);
+
+            QJsonObject toJson() const;
+
+            QString m_name;
+
+            // Where to create the quick note.
+            QString m_folderPath;
+
+            // Name of the quick note. Snippet is supported.
+            QString m_noteName;
+
+            QString m_template;
+        };
+
         enum OpenGL
         {
             None,
@@ -149,6 +168,9 @@ namespace vnotex
         void removeHistory(const QString &p_itemPath);
         void clearHistory();
 
+        const QVector<QuickNoteScheme> &getQuickNoteSchemes() const;
+        void setQuickNoteSchemes(const QVector<QuickNoteScheme>& p_schemes);
+
     private:
         void loadCore(const QJsonObject &p_session);
 
@@ -166,6 +188,10 @@ namespace vnotex
 
         QJsonArray saveExternalPrograms() const;
 
+        void loadQuickNoteSchemes(const QJsonObject &p_session);
+
+        QJsonArray saveQuickNoteSchemes() const;
+
         void doVersionSpecificOverride();
 
         void loadHistory(const QJsonObject &p_session);
@@ -215,7 +241,9 @@ namespace vnotex
         QVector<HistoryItem> m_history;
 
         // Default folder path to open for external media like images and files.
-        QString m_externalMediaDefaultPath;;
+        QString m_externalMediaDefaultPath;
+
+        QVector<QuickNoteScheme> m_quickNoteSchemes;
     };
 } // ns vnotex
 

+ 14 - 0
src/core/templatemgr.cpp

@@ -2,6 +2,8 @@
 
 #include <QDir>
 
+#include <utils/fileutils.h>
+
 #include "configmgr.h"
 
 using namespace vnotex;
@@ -20,5 +22,17 @@ QStringList TemplateMgr::getTemplates() const
 
 QString TemplateMgr::getTemplateFilePath(const QString &p_name) const
 {
+    if (p_name.isEmpty()) {
+        return QString();
+    }
     return QDir(getTemplateFolder()).filePath(p_name);
 }
+
+QString TemplateMgr::getTemplateContent(const QString &p_name) const
+{
+    const auto filePath = getTemplateFilePath(p_name);
+    if (filePath.isEmpty()) {
+        return QString();
+    }
+    return FileUtils::readTextFile(filePath);
+}

+ 2 - 0
src/core/templatemgr.h

@@ -24,6 +24,8 @@ namespace vnotex
 
         QString getTemplateFilePath(const QString &p_name) const;
 
+        QString getTemplateContent(const QString &p_name) const;
+
     private:
         TemplateMgr() = default;
     };

+ 3 - 0
src/core/vnotex.h

@@ -79,6 +79,9 @@ namespace vnotex
         // The handler should determine in which folder this note belongs to.
         void newNoteRequested();
 
+        // Requested to new a quick note (maybe in current folder).
+        void newQuickNoteRequested();
+
         // Requested to new a folder in current notebook.
         void newFolderRequested();
 

+ 40 - 92
src/widgets/dialogs/newnotedialog.cpp

@@ -14,6 +14,7 @@
 #include <utils/fileutils.h>
 #include "exception.h"
 #include "nodeinfowidget.h"
+#include "notetemplateselector.h"
 #include <utils/widgetutils.h>
 #include <core/templatemgr.h>
 #include <core/configmgr.h>
@@ -43,27 +44,8 @@ void NewNoteDialog::setupUI(const Node *p_node)
 
     auto infoLayout = m_infoWidget->getMainLayout();
 
-    {
-        auto templateLayout = new QHBoxLayout();
-        templateLayout->setContentsMargins(0, 0, 0, 0);
-        infoLayout->addRow(tr("Template:"), templateLayout);
-
-        setupTemplateComboBox(m_infoWidget);
-        templateLayout->addWidget(m_templateComboBox);
-
-        templateLayout->addStretch();
-
-        auto manageBtn = new QPushButton(tr("Manage"), m_infoWidget);
-        templateLayout->addWidget(manageBtn);
-        connect(manageBtn, &QPushButton::clicked,
-                this, []() {
-                    WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(TemplateMgr::getInst().getTemplateFolder()));
-                });
-
-        m_templateTextEdit = WidgetsFactory::createPlainTextConsole(m_infoWidget);
-        infoLayout->addRow("", m_templateTextEdit);
-        m_templateTextEdit->hide();
-    }
+    m_templateSelector = new NoteTemplateSelector(m_infoWidget);
+    infoLayout->addRow(tr("Template:"), m_templateSelector);
 
     setDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
 
@@ -106,41 +88,56 @@ bool NewNoteDialog::validateNameInput(QString &p_msg)
 
 void NewNoteDialog::acceptedButtonClicked()
 {
-    s_lastTemplate = m_templateComboBox->currentData().toString();
+    s_lastTemplate = m_templateSelector->getCurrentTemplate();
 
     {
         auto fileType = FileTypeHelper::getInst().getFileTypeByName(m_infoWidget->getFileType()).m_type;
         ConfigMgr::getInst().getWidgetConfig().setNewNoteDefaultFileType(static_cast<int>(fileType));
     }
 
-    if (validateInputs() && newNote()) {
+    if (validateInputs()) {
+        Notebook *notebook = const_cast<Notebook *>(m_infoWidget->getNotebook());
+        Node *parentNode = const_cast<Node *>(m_infoWidget->getParentNode());
+        QString errMsg;
+        m_newNode = newNote(notebook,
+                            parentNode,
+                            m_infoWidget->getName(),
+                            m_templateSelector->getTemplateContent(),
+                            errMsg);
+        if (!m_newNode) {
+            setInformationText(errMsg, ScrollDialog::InformationLevel::Error);
+            return;
+        }
         accept();
     }
 }
 
-bool NewNoteDialog::newNote()
+QSharedPointer<Node> NewNoteDialog::newNote(Notebook *p_notebook,
+                                            Node *p_parentNode,
+                                            const QString &p_name,
+                                            const QString &p_templateContent,
+                                            QString &p_errMsg)
 {
-    m_newNode.clear();
+    Q_ASSERT(p_notebook && p_parentNode);
+
+    QSharedPointer<Node> newNode;
+    p_errMsg.clear();
 
-    Notebook *notebook = const_cast<Notebook *>(m_infoWidget->getNotebook());
-    Node *parentNode = const_cast<Node *>(m_infoWidget->getParentNode());
     try {
-        m_newNode = notebook->newNode(parentNode,
+        newNode = p_notebook->newNode(p_parentNode,
                                       Node::Flag::Content,
-                                      m_infoWidget->getName(),
-                                      getTemplateContent());
+                                      p_name,
+                                      evaluateTemplateContent(p_templateContent, p_name));
     } catch (Exception &p_e) {
-        QString msg = tr("Failed to create note under (%1) in (%2) (%3).").arg(parentNode->getName(),
-                                                                               notebook->getName(),
-                                                                               p_e.what());
-        qCritical() << msg;
-        setInformationText(msg, ScrollDialog::InformationLevel::Error);
-        return false;
+        p_errMsg = tr("Failed to create note under (%1) in (%2) (%3).").arg(p_parentNode->getName(),
+                                                                            p_notebook->getName(),
+                                                                            p_e.what());
+        qCritical() << p_errMsg;
+        return nullptr;
     }
 
-    emit notebook->nodeUpdated(m_newNode.data());
-
-    return true;
+    emit p_notebook->nodeUpdated(newNode.data());
+    return newNode;
 }
 
 const QSharedPointer<Node> &NewNoteDialog::getNewNode() const
@@ -166,66 +163,17 @@ void NewNoteDialog::initDefaultValues(const Node *p_node)
 
     if (!s_lastTemplate.isEmpty()) {
         // Restore.
-        int idx = m_templateComboBox->findData(s_lastTemplate);
-        if (idx != -1) {
-            m_templateComboBox->setCurrentIndex(idx);
-        } else {
+        if (!m_templateSelector->setCurrentTemplate(s_lastTemplate)) {
             s_lastTemplate.clear();
         }
     }
 }
 
-void NewNoteDialog::setupTemplateComboBox(QWidget *p_parent)
-{
-    m_templateComboBox = WidgetsFactory::createComboBox(p_parent);
-
-    // None.
-    m_templateComboBox->addItem(tr("None"), "");
-
-    int idx = 1;
-    auto templates = TemplateMgr::getInst().getTemplates();
-    for (const auto &temp : templates) {
-        m_templateComboBox->addItem(temp, temp);
-        m_templateComboBox->setItemData(idx++, temp, Qt::ToolTipRole);
-    }
-
-    m_templateComboBox->setCurrentIndex(0);
-
-    connect(m_templateComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
-            this, &NewNoteDialog::updateCurrentTemplate);
-}
-
-QString NewNoteDialog::getTemplateContent() const
+QString NewNoteDialog::evaluateTemplateContent(const QString &p_content, const QString &p_name)
 {
     int cursorOffset = 0;
-    return SnippetMgr::getInst().applySnippetBySymbol(m_templateContent,
+    return SnippetMgr::getInst().applySnippetBySymbol(p_content,
                                                       QString(),
                                                       cursorOffset,
-                                                      SnippetMgr::generateOverrides(m_infoWidget->getName()));
-}
-
-void NewNoteDialog::updateCurrentTemplate()
-{
-    m_templateContent.clear();
-    m_templateTextEdit->clear();
-
-    auto temp = m_templateComboBox->currentData().toString();
-    if (temp.isEmpty()) {
-        m_templateTextEdit->hide();
-        return;
-    }
-
-    const auto filePath = TemplateMgr::getInst().getTemplateFilePath(temp);
-    try {
-        m_templateContent = FileUtils::readTextFile(filePath);
-        m_templateTextEdit->setPlainText(m_templateContent);
-        m_templateTextEdit->show();
-    } catch (Exception &p_e) {
-        m_templateTextEdit->hide();
-
-        QString msg = tr("Failed to load template (%1) (%2).")
-            .arg(filePath, p_e.what());
-        qCritical() << msg;
-        setInformationText(msg, ScrollDialog::InformationLevel::Error);
-    }
+                                                      SnippetMgr::generateOverrides(p_name));
 }

+ 9 - 15
src/widgets/dialogs/newnotedialog.h

@@ -3,14 +3,12 @@
 
 #include "scrolldialog.h"
 
-class QComboBox;
-class QPlainTextEdit;
-
 namespace vnotex
 {
     class Notebook;
     class Node;
     class NodeInfoWidget;
+    class NoteTemplateSelector;
 
     class NewNoteDialog : public ScrollDialog
     {
@@ -21,6 +19,12 @@ namespace vnotex
 
         const QSharedPointer<Node> &getNewNode() const;
 
+        static QSharedPointer<Node> newNote(Notebook *p_notebook,
+                                            Node *p_parentNode,
+                                            const QString &p_name,
+                                            const QString &p_templateContent,
+                                            QString &p_errMsg);
+
     protected:
         void acceptedButtonClicked() Q_DECL_OVERRIDE;
 
@@ -29,27 +33,17 @@ namespace vnotex
 
         void setupNodeInfoWidget(const Node *p_node, QWidget *p_parent);
 
-        void setupTemplateComboBox(QWidget *p_parent);
-
         bool validateInputs();
 
         bool validateNameInput(QString &p_msg);
 
-        bool newNote();
-
         void initDefaultValues(const Node *p_node);
 
-        QString getTemplateContent() const;
-
-        void updateCurrentTemplate();
+        static QString evaluateTemplateContent(const QString &p_content, const QString &p_name);
 
         NodeInfoWidget *m_infoWidget = nullptr;
 
-        QComboBox *m_templateComboBox = nullptr;
-
-        QPlainTextEdit *m_templateTextEdit = nullptr;
-
-        QString m_templateContent;
+        NoteTemplateSelector *m_templateSelector = nullptr;
 
         QSharedPointer<Node> m_newNode;
 

+ 112 - 0
src/widgets/dialogs/notetemplateselector.cpp

@@ -0,0 +1,112 @@
+#include "notetemplateselector.h"
+
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QComboBox>
+#include <QPlainTextEdit>
+#include <QPushButton>
+
+#include <core/templatemgr.h>
+#include <core/exception.h>
+#include <utils/fileutils.h>
+#include <utils/widgetutils.h>
+
+#include <widgets/widgetsfactory.h>
+
+using namespace vnotex;
+
+NoteTemplateSelector::NoteTemplateSelector(QWidget *p_parent)
+    : QWidget(p_parent)
+{
+    setupUI();
+}
+
+void NoteTemplateSelector::setupUI()
+{
+    auto mainLayout = new QVBoxLayout(this);
+    mainLayout->setContentsMargins(0, 0, 0, 0);
+
+    auto selectorLayout = new QHBoxLayout();
+    mainLayout->addLayout(selectorLayout);
+
+    setupTemplateComboBox(this);
+    selectorLayout->addWidget(m_templateComboBox, 1);
+
+    auto manageBtn = new QPushButton(tr("Manage"), this);
+    selectorLayout->addWidget(manageBtn);
+    connect(manageBtn, &QPushButton::clicked,
+            this, []() {
+                WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(TemplateMgr::getInst().getTemplateFolder()));
+            });
+
+    m_templateTextEdit = WidgetsFactory::createPlainTextConsole(this);
+    mainLayout->addWidget(m_templateTextEdit);
+    m_templateTextEdit->hide();
+}
+
+void NoteTemplateSelector::setupTemplateComboBox(QWidget *p_parent)
+{
+    m_templateComboBox = WidgetsFactory::createComboBox(p_parent);
+
+    // None.
+    m_templateComboBox->addItem(tr("None"), "");
+
+    int idx = 1;
+    auto templates = TemplateMgr::getInst().getTemplates();
+    for (const auto &temp : templates) {
+        m_templateComboBox->addItem(temp, temp);
+        m_templateComboBox->setItemData(idx++, temp, Qt::ToolTipRole);
+    }
+
+    m_templateComboBox->setCurrentIndex(0);
+
+    connect(m_templateComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
+            this, &NoteTemplateSelector::updateCurrentTemplate);
+}
+
+void NoteTemplateSelector::updateCurrentTemplate()
+{
+    m_templateContent.clear();
+    m_templateTextEdit->clear();
+
+    auto templateName = m_templateComboBox->currentData().toString();
+    if (templateName.isEmpty()) {
+        m_templateTextEdit->hide();
+        emit templateChanged();
+        return;
+    }
+
+    const auto filePath = TemplateMgr::getInst().getTemplateFilePath(templateName);
+    try {
+        m_templateContent = FileUtils::readTextFile(filePath);
+        m_templateTextEdit->setPlainText(m_templateContent);
+    } catch (Exception &p_e) {
+        QString msg = tr("Failed to load template (%1) (%2).")
+            .arg(filePath, p_e.what());
+        qCritical() << msg;
+        m_templateTextEdit->setPlainText(msg);
+    }
+    m_templateTextEdit->show();
+    emit templateChanged();
+}
+
+QString NoteTemplateSelector::getCurrentTemplate() const
+{
+    return m_templateComboBox->currentData().toString();
+}
+
+bool NoteTemplateSelector::setCurrentTemplate(const QString &p_template)
+{
+    int idx = m_templateComboBox->findData(p_template);
+    if (idx != -1) {
+        m_templateComboBox->setCurrentIndex(idx);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+const QString& NoteTemplateSelector::getTemplateContent() const
+{
+    return m_templateContent;
+}

+ 40 - 0
src/widgets/dialogs/notetemplateselector.h

@@ -0,0 +1,40 @@
+#ifndef NOTETEMPLATESELECTOR_H
+#define NOTETEMPLATESELECTOR_H
+
+#include <QWidget>
+
+class QComboBox;
+class QPlainTextEdit;
+
+namespace vnotex
+{
+    class NoteTemplateSelector : public QWidget
+    {
+        Q_OBJECT
+    public:
+        explicit NoteTemplateSelector(QWidget *p_parent = nullptr);
+
+        QString getCurrentTemplate() const;
+        bool setCurrentTemplate(const QString &p_template);
+
+        const QString& getTemplateContent() const;
+
+    signals:
+        void templateChanged();
+
+    private:
+        void setupUI();
+
+        void setupTemplateComboBox(QWidget *p_parent);
+
+        void updateCurrentTemplate();
+
+        QComboBox *m_templateComboBox = nullptr;
+
+        QPlainTextEdit *m_templateTextEdit = nullptr;
+
+        QString m_templateContent;
+    };
+}
+
+#endif // NOTETEMPLATESELECTOR_H

+ 2 - 0
src/widgets/dialogs/settings/fileassociationpage.cpp

@@ -39,6 +39,8 @@ void FileAssociationPage::setupUI()
     m_externalProgramsBox = new QGroupBox(tr("External Programs"), this);
     WidgetsFactory::createFormLayout(m_externalProgramsBox);
     mainLayout->addWidget(m_externalProgramsBox);
+
+    mainLayout->addStretch();
 }
 
 void FileAssociationPage::loadInternal()

+ 20 - 2
src/widgets/dialogs/settings/imagehostpage.cpp

@@ -46,6 +46,8 @@ void ImageHostPage::setupUI()
 
     auto box = setupGeneralBox(this);
     m_mainLayout->addWidget(box);
+
+    m_mainLayout->addStretch();
 }
 
 QGroupBox *ImageHostPage::setupGeneralBox(QWidget *p_parent)
@@ -54,9 +56,8 @@ QGroupBox *ImageHostPage::setupGeneralBox(QWidget *p_parent)
     auto layout = WidgetsFactory::createFormLayout(box);
 
     {
-        m_defaultImageHostComboBox = WidgetsFactory::createComboBox(box);
-
         // Add items in loadInternal().
+        m_defaultImageHostComboBox = WidgetsFactory::createComboBox(box);
 
         const QString label(tr("Default image host:"));
         layout->addRow(label, m_defaultImageHostComboBox);
@@ -121,11 +122,15 @@ void ImageHostPage::loadInternal()
         }
     }
 
+    removeLastStretch();
+
     // Setup boxes.
     for (const auto &host : hosts) {
         auto box = setupGroupBoxForImageHost(host, this);
         addWidgetToLayout(box);
     }
+
+    m_mainLayout->addStretch();
 }
 
 bool ImageHostPage::saveInternal()
@@ -185,8 +190,12 @@ void ImageHostPage::newImageHost()
 {
     NewImageHostDialog dialog(this);
     if (dialog.exec()) {
+        removeLastStretch();
+
         auto box = setupGroupBoxForImageHost(dialog.getNewImageHost(), this);
         addWidgetToLayout(box);
+
+        m_mainLayout->addStretch();
     }
 }
 
@@ -293,3 +302,12 @@ void ImageHostPage::testImageHost(const QString &p_hostName)
                              QString(),
                              msg);
 }
+
+void ImageHostPage::removeLastStretch()
+{
+    auto item = m_mainLayout->itemAt(m_mainLayout->count() - 1);
+    if (item) {
+        m_mainLayout->removeItem(item);
+        delete item;
+    }
+}

+ 2 - 0
src/widgets/dialogs/settings/imagehostpage.h

@@ -46,6 +46,8 @@ namespace vnotex
 
         QGroupBox *setupGeneralBox(QWidget *p_parent);
 
+        void removeLastStretch();
+
         QVBoxLayout *m_mainLayout = nullptr;
 
         // [host] -> list of related fields.

+ 2 - 0
src/widgets/dialogs/settings/markdowneditorpage.cpp

@@ -46,6 +46,8 @@ void MarkdownEditorPage::setupUI()
 
     auto editBox = setupEditGroup();
     mainLayout->addWidget(editBox);
+
+    mainLayout->addStretch();
 }
 
 void MarkdownEditorPage::loadInternal()

+ 226 - 1
src/widgets/dialogs/settings/quickaccesspage.cpp

@@ -6,13 +6,24 @@
 #include <QPlainTextEdit>
 #include <QDebug>
 #include <QFileDialog>
+#include <QCheckBox>
+#include <QPushButton>
+#include <QComboBox>
+#include <QLineEdit>
+#include <QInputDialog>
 
-#include <widgets/widgetsfactory.h>
 #include <core/sessionconfig.h>
 #include <core/coreconfig.h>
 #include <core/configmgr.h>
+#include <core/notebookmgr.h>
+#include <core/vnotex.h>
 #include <utils/widgetutils.h>
 #include <widgets/locationinputwithbrowsebutton.h>
+#include <widgets/lineeditwithsnippet.h>
+#include <widgets/widgetsfactory.h>
+#include <widgets/messageboxhelper.h>
+
+#include "../notetemplateselector.h"
 
 using namespace vnotex;
 
@@ -31,6 +42,11 @@ void QuickAccessPage::setupUI()
 
     auto quickAccessBox = setupQuickAccessGroup();
     mainLayout->addWidget(quickAccessBox);
+
+    auto quickNoteBox = setupQuickNoteGroup();
+    mainLayout->addWidget(quickNoteBox);
+
+    mainLayout->addStretch();
 }
 
 void QuickAccessPage::loadInternal()
@@ -45,6 +61,26 @@ void QuickAccessPage::loadInternal()
             m_quickAccessTextEdit->setPlainText(quickAccess.join(QChar('\n')));
         }
     }
+
+    loadQuickNoteSchemes();
+}
+
+void QuickAccessPage::loadQuickNoteSchemes()
+{
+    const auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
+
+    m_quickNoteSchemes = sessionConfig.getQuickNoteSchemes();
+    m_quickNoteCurrentIndex = -1;
+
+    m_quickNoteSchemeComboBox->clear();
+    for (const auto &scheme : m_quickNoteSchemes) {
+        m_quickNoteSchemeComboBox->addItem(scheme.m_name);
+    }
+    if (m_quickNoteSchemeComboBox->count() > 0) {
+        m_quickNoteSchemeComboBox->setCurrentIndex(0);
+        // Manually call the handler.
+        setCurrentQuickNote(0);
+    }
 }
 
 bool QuickAccessPage::saveInternal()
@@ -60,9 +96,20 @@ bool QuickAccessPage::saveInternal()
         }
     }
 
+    saveQuickNoteSchemes();
+
     return true;
 }
 
+void QuickAccessPage::saveQuickNoteSchemes()
+{
+    // Save current quick note scheme from inputs.
+    saveCurrentQuickNote();
+
+    auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
+    sessionConfig.setQuickNoteSchemes(m_quickNoteSchemes);
+}
+
 QString QuickAccessPage::title() const
 {
     return tr("Quick Access");
@@ -104,6 +151,7 @@ QGroupBox *QuickAccessPage::setupQuickAccessGroup()
     {
         m_quickAccessTextEdit = WidgetsFactory::createPlainTextEdit(box);
         m_quickAccessTextEdit->setToolTip(tr("Edit the files pinned to Quick Access (one file per line)"));
+        m_quickAccessTextEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
 
         const QString label(tr("Quick Access:"));
         layout->addRow(label, m_quickAccessTextEdit);
@@ -114,3 +162,180 @@ QGroupBox *QuickAccessPage::setupQuickAccessGroup()
 
     return box;
 }
+
+QString QuickAccessPage::getDefaultQuickNoteFolderPath()
+{
+    auto defaultPath = QDir::homePath();
+    auto currentNotebook = VNoteX::getInst().getNotebookMgr().getCurrentNotebook();
+    if (currentNotebook) {
+        defaultPath = currentNotebook->getRootFolderAbsolutePath();
+    }
+    return defaultPath;
+}
+
+QGroupBox *QuickAccessPage::setupQuickNoteGroup()
+{
+    auto box = new QGroupBox(tr("Quick Note"), this);
+    auto mainLayout = WidgetsFactory::createFormLayout(box);
+
+    {
+        auto selectorLayout = new QHBoxLayout();
+
+        // Add items in loadInternal().
+        m_quickNoteSchemeComboBox = WidgetsFactory::createComboBox(box);
+        selectorLayout->addWidget(m_quickNoteSchemeComboBox, 1);
+        m_quickNoteSchemeComboBox->setPlaceholderText(tr("No scheme to show"));
+
+        auto newBtn = new QPushButton(tr("New"), box);
+        connect(newBtn, &QPushButton::clicked,
+                this, &QuickAccessPage::newQuickNoteScheme);
+        selectorLayout->addWidget(newBtn);
+
+        auto deleteBtn = new QPushButton(tr("Delete"), box);
+        deleteBtn->setEnabled(false);
+        connect(deleteBtn, &QPushButton::clicked,
+                this, &QuickAccessPage::removeQuickNoteScheme);
+        selectorLayout->addWidget(deleteBtn);
+
+        const QString label(tr("Scheme:"));
+        mainLayout->addRow(label, selectorLayout);
+        addSearchItem(label, m_quickNoteSchemeComboBox);
+
+        connect(m_quickNoteSchemeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
+                this, &QuickAccessPage::pageIsChanged);
+        connect(m_quickNoteSchemeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
+                this, [deleteBtn](int idx) {
+                    deleteBtn->setEnabled(idx > -1);
+                });
+    }
+
+    m_quickNoteInfoGroupBox = new QGroupBox(box);
+    mainLayout->addRow(m_quickNoteInfoGroupBox);
+    auto infoLayout = WidgetsFactory::createFormLayout(m_quickNoteInfoGroupBox);
+
+    {
+        const QString label(tr("Folder path:"));
+        m_quickNoteFolderPathInput = new LocationInputWithBrowseButton(m_quickNoteInfoGroupBox);
+        m_quickNoteFolderPathInput->setPlaceholderText(tr("Empty to use current explored folder dynamically"));
+        infoLayout->addRow(label, m_quickNoteFolderPathInput);
+        addSearchItem(label, m_quickNoteFolderPathInput);
+        connect(m_quickNoteFolderPathInput, &LocationInputWithBrowseButton::textChanged,
+                this, &QuickAccessPage::pageIsChanged);
+        connect(m_quickNoteFolderPathInput, &LocationInputWithBrowseButton::clicked,
+                this, [this]() {
+                    auto folderPath = QFileDialog::getExistingDirectory(this,
+                                                                        tr("Select Quick Note Folder"),
+                                                                        getDefaultQuickNoteFolderPath());
+                    if (!folderPath.isEmpty()) {
+                        m_quickNoteFolderPathInput->setText(folderPath);
+                    }
+                });
+    }
+
+    {
+        const QString label(tr("Note name:"));
+        m_quickNoteNoteNameLineEdit = WidgetsFactory::createLineEditWithSnippet(m_quickNoteInfoGroupBox);
+        infoLayout->addRow(label, m_quickNoteNoteNameLineEdit);
+        connect(m_quickNoteNoteNameLineEdit, &QLineEdit::textChanged,
+                this, &QuickAccessPage::pageIsChanged);
+    }
+
+    {
+        const QString label(tr("Note template:"));
+        m_quickNoteTemplateSelector = new NoteTemplateSelector(m_quickNoteInfoGroupBox);
+        infoLayout->addRow(label, m_quickNoteTemplateSelector);
+        connect(m_quickNoteTemplateSelector, &NoteTemplateSelector::templateChanged,
+                this, &QuickAccessPage::pageIsChanged);
+    }
+
+    m_quickNoteInfoGroupBox->setVisible(false);
+    connect(m_quickNoteSchemeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
+            this, [this](int idx) {
+                if (isLoading()) {
+                    return;
+                }
+                setCurrentQuickNote(idx);
+            });
+    return box;
+}
+
+void QuickAccessPage::newQuickNoteScheme()
+{
+    bool isDuplicated = false;
+    QString schemeName;
+    do {
+        schemeName = QInputDialog::getText(this, tr("Quick Note Scheme"),
+                isDuplicated ? tr("Scheme name already exists! Try again:") : tr("Scheme name:"));
+        if (schemeName.isEmpty()) {
+            return;
+        }
+        isDuplicated = m_quickNoteSchemeComboBox->findText(schemeName) != -1;
+    } while (isDuplicated);
+
+    SessionConfig::QuickNoteScheme scheme;
+    scheme.m_name = schemeName;
+    scheme.m_folderPath = getDefaultQuickNoteFolderPath();
+    scheme.m_noteName = tr("quick_note_%da%.md");
+    m_quickNoteSchemes.push_back(scheme);
+
+    m_quickNoteSchemeComboBox->addItem(schemeName);
+    m_quickNoteSchemeComboBox->setCurrentText(schemeName);
+
+    emit pageIsChanged();
+}
+
+void QuickAccessPage::removeQuickNoteScheme()
+{
+    int idx = m_quickNoteSchemeComboBox->currentIndex();
+    Q_ASSERT(idx > -1);
+
+    auto& scheme = m_quickNoteSchemes[idx];
+    int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Type::Question,
+                                                 tr("Delete quick note scheme (%1)?").arg(scheme.m_name));
+    if (ret != QMessageBox::Ok) {
+        return;
+    }
+
+    m_quickNoteCurrentIndex = -1;
+    m_quickNoteSchemes.removeAt(idx);
+    m_quickNoteSchemeComboBox->removeItem(idx);
+    emit pageIsChanged();
+}
+
+void QuickAccessPage::saveCurrentQuickNote()
+{
+    if (m_quickNoteCurrentIndex < 0) {
+        return;
+    }
+    Q_ASSERT(m_quickNoteCurrentIndex < m_quickNoteSchemes.size());
+    auto& scheme = m_quickNoteSchemes[m_quickNoteCurrentIndex];
+    scheme.m_folderPath = m_quickNoteFolderPathInput->text();
+    // No need to apply the snippet for now.
+    scheme.m_noteName = m_quickNoteNoteNameLineEdit->text();
+    scheme.m_template = m_quickNoteTemplateSelector->getCurrentTemplate();
+}
+
+void QuickAccessPage::loadCurrentQuickNote()
+{
+    if (m_quickNoteCurrentIndex < 0) {
+        m_quickNoteFolderPathInput->setText(QString());
+        m_quickNoteNoteNameLineEdit->setText(QString());
+        m_quickNoteTemplateSelector->setCurrentTemplate(QString());
+        return;
+    }
+
+    Q_ASSERT(m_quickNoteCurrentIndex < m_quickNoteSchemes.size());
+    const auto& scheme = m_quickNoteSchemes[m_quickNoteCurrentIndex];
+    m_quickNoteFolderPathInput->setText(scheme.m_folderPath);
+    m_quickNoteNoteNameLineEdit->setText(scheme.m_noteName);
+    m_quickNoteTemplateSelector->setCurrentTemplate(scheme.m_template);
+}
+
+void QuickAccessPage::setCurrentQuickNote(int idx)
+{
+    saveCurrentQuickNote();
+    m_quickNoteCurrentIndex = idx;
+    loadCurrentQuickNote();
+
+    m_quickNoteInfoGroupBox->setVisible(idx > -1);
+}

+ 37 - 0
src/widgets/dialogs/settings/quickaccesspage.h

@@ -3,12 +3,17 @@
 
 #include "settingspage.h"
 
+#include <core/sessionconfig.h>
+
 class QGroupBox;
 class QPlainTextEdit;
+class QComboBox;
 
 namespace vnotex
 {
     class LocationInputWithBrowseButton;
+    class LineEditWithSnippet;
+    class NoteTemplateSelector;
 
     class QuickAccessPage : public SettingsPage
     {
@@ -30,9 +35,41 @@ namespace vnotex
 
         QGroupBox *setupQuickAccessGroup();
 
+        QGroupBox *setupQuickNoteGroup();
+
+        void newQuickNoteScheme();
+
+        void removeQuickNoteScheme();
+
+        void saveCurrentQuickNote();
+
+        void loadCurrentQuickNote();
+
+        void loadQuickNoteSchemes();
+
+        void saveQuickNoteSchemes();
+
+        void setCurrentQuickNote(int idx);
+
+        static QString getDefaultQuickNoteFolderPath();
+
         LocationInputWithBrowseButton *m_flashPageInput = nullptr;
 
         QPlainTextEdit *m_quickAccessTextEdit = nullptr;
+
+        QComboBox *m_quickNoteSchemeComboBox = nullptr;
+
+        LocationInputWithBrowseButton *m_quickNoteFolderPathInput = nullptr;
+
+        LineEditWithSnippet *m_quickNoteNoteNameLineEdit = nullptr;
+
+        NoteTemplateSelector *m_quickNoteTemplateSelector = nullptr;
+
+        QGroupBox *m_quickNoteInfoGroupBox = nullptr;
+
+        QVector<SessionConfig::QuickNoteScheme> m_quickNoteSchemes;
+
+        int m_quickNoteCurrentIndex = -1;
     };
 }
 

+ 7 - 0
src/widgets/dialogs/settings/settingspage.cpp

@@ -73,7 +73,9 @@ void SettingsPage::pageIsChangedWithRestartNeeded()
 
 void SettingsPage::load()
 {
+    m_loading = true;
     loadInternal();
+    m_loading = false;
     m_changed = false;
     m_restartNeeded = false;
 }
@@ -115,3 +117,8 @@ bool SettingsPage::isRestartNeeded() const
 {
     return m_restartNeeded;
 }
+
+bool SettingsPage::isLoading() const
+{
+    return m_loading;
+}

+ 4 - 0
src/widgets/dialogs/settings/settingspage.h

@@ -44,6 +44,8 @@ namespace vnotex
 
         void setError(const QString &p_err);
 
+        bool isLoading() const;
+
     protected slots:
         void pageIsChanged();
 
@@ -70,6 +72,8 @@ namespace vnotex
 
         bool m_restartNeeded = false;
 
+        bool m_loading = false;
+
         QString m_error;
     };
 }

+ 1 - 5
src/widgets/dialogs/settings/themepage.cpp

@@ -86,11 +86,7 @@ void ThemePage::setupUI()
         layout->addWidget(scrollArea, 0, 2, 5, 1);
     }
 
-    // Override.
-    {
-        auto box = new QGroupBox(tr("Style Override"), this);
-        mainLayout->addWidget(box);
-    }
+    mainLayout->addStretch();
 }
 
 void ThemePage::loadInternal()

+ 6 - 6
src/widgets/editors/markdowneditor.cpp

@@ -374,7 +374,7 @@ bool MarkdownEditor::insertImageToBufferFromLocalFile(const QString &p_title,
             ba = FileUtils::readFile(p_srcImagePath);
         } catch (Exception &e) {
             MessageBoxHelper::notify(MessageBoxHelper::Warning,
-                                     QString("Failed to read local image file (%1) (%2).").arg(p_srcImagePath, e.what()),
+                                     tr("Failed to read local image file (%1) (%2).").arg(p_srcImagePath, e.what()),
                                      this);
             return false;
         }
@@ -387,7 +387,7 @@ bool MarkdownEditor::insertImageToBufferFromLocalFile(const QString &p_title,
             destFilePath = m_buffer->insertImage(p_srcImagePath, destFileName);
         } catch (Exception &e) {
             MessageBoxHelper::notify(MessageBoxHelper::Warning,
-                                     QString("Failed to insert image from local file (%1) (%2).").arg(p_srcImagePath, e.what()),
+                                     tr("Failed to insert image from local file (%1) (%2).").arg(p_srcImagePath, e.what()),
                                      this);
             return false;
         }
@@ -430,7 +430,7 @@ bool MarkdownEditor::insertImageToBufferFromData(const QString &p_title,
             destFilePath = m_buffer->insertImage(p_image, destFileName);
         } catch (Exception &e) {
             MessageBoxHelper::notify(MessageBoxHelper::Warning,
-                                     QString("Failed to insert image from data (%1).").arg(e.what()),
+                                     tr("Failed to insert image from data (%1).").arg(e.what()),
                                      this);
             return false;
         }
@@ -1470,7 +1470,7 @@ QString MarkdownEditor::saveToImageHost(const QByteArray &p_imageData, const QSt
 
     if (targetUrl.isEmpty()) {
         MessageBoxHelper::notify(MessageBoxHelper::Warning,
-                                 QString("Failed to upload image to image host (%1) as (%2).").arg(m_imageHost->getName(), destPath),
+                                 tr("Failed to upload image to image host (%1) as (%2).").arg(m_imageHost->getName(), destPath),
                                  QString(),
                                  errMsg,
                                  this);
@@ -1551,7 +1551,7 @@ void MarkdownEditor::uploadImagesToImageHost()
             ba = FileUtils::readFile(link.m_path);
         } catch (Exception &e) {
             MessageBoxHelper::notify(MessageBoxHelper::Warning,
-                                     QString("Failed to read local image file (%1) (%2).").arg(link.m_path, e.what()),
+                                     tr("Failed to read local image file (%1) (%2).").arg(link.m_path, e.what()),
                                      this);
             continue;
         }
@@ -1569,7 +1569,7 @@ void MarkdownEditor::uploadImagesToImageHost()
 
         if (targetUrl.isEmpty()) {
             MessageBoxHelper::notify(MessageBoxHelper::Warning,
-                                     QString("Failed to upload image to image host (%1) as (%2).").arg(host->getName(), destPath),
+                                     tr("Failed to upload image to image host (%1) as (%2).").arg(host->getName(), destPath),
                                      QString(),
                                      errMsg,
                                      this);

+ 5 - 0
src/widgets/locationinputwithbrowsebutton.cpp

@@ -44,3 +44,8 @@ void LocationInputWithBrowseButton::setToolTip(const QString &p_tip)
 {
     m_lineEdit->setToolTip(p_tip);
 }
+
+void LocationInputWithBrowseButton::setPlaceholderText(const QString &p_text)
+{
+    m_lineEdit->setPlaceholderText(p_text);
+}

+ 2 - 0
src/widgets/locationinputwithbrowsebutton.h

@@ -22,6 +22,8 @@ namespace vnotex
 
         void setToolTip(const QString &p_tip);
 
+        void setPlaceholderText(const QString &p_text);
+
     signals:
         void clicked();
 

+ 2 - 0
src/widgets/mainwindow.cpp

@@ -332,6 +332,8 @@ void MainWindow::setupNotebookExplorer()
             m_notebookExplorer, &NotebookExplorer::newFolder);
     connect(&VNoteX::getInst(), &VNoteX::newNoteRequested,
             m_notebookExplorer, &NotebookExplorer::newNote);
+    connect(&VNoteX::getInst(), &VNoteX::newQuickNoteRequested,
+            m_notebookExplorer, &NotebookExplorer::newQuickNote);
     connect(&VNoteX::getInst(), &VNoteX::importFileRequested,
             m_notebookExplorer, &NotebookExplorer::importFile);
     connect(&VNoteX::getInst(), &VNoteX::importFolderRequested,

+ 71 - 3
src/widgets/notebookexplorer.cpp

@@ -8,6 +8,7 @@
 #include <QProgressDialog>
 
 #include "titlebar.h"
+#include "dialogs/selectdialog.h"
 #include "dialogs/newnotebookdialog.h"
 #include "dialogs/newnotebookfromfolderdialog.h"
 #include "dialogs/newfolderdialog.h"
@@ -22,6 +23,7 @@
 #include <utils/iconutils.h>
 #include <utils/widgetutils.h>
 #include <utils/pathutils.h>
+#include <utils/fileutils.h>
 #include "notebookselector.h"
 #include "notebooknodeexplorer.h"
 #include "messageboxhelper.h"
@@ -32,6 +34,10 @@
 #include <core/events.h>
 #include <core/exception.h>
 #include <core/fileopenparameters.h>
+#include <core/notebookmgr.h>
+#include <core/templatemgr.h>
+#include <snippet/snippetmgr.h>
+
 #include "navigationmodemgr.h"
 #include "widgetsfactory.h"
 
@@ -269,12 +275,11 @@ void NotebookExplorer::newNote()
     auto node = checkNotebookAndGetCurrentExploredFolderNode();
     if (!node) {
         return;
-    }
+	}
 
-    NewNoteDialog dialog(node, VNoteX::getInst().getMainWindow());
+	NewNoteDialog dialog(node, VNoteX::getInst().getMainWindow());
     if (dialog.exec() == QDialog::Accepted) {
         m_nodeExplorer->setCurrentNode(dialog.getNewNode().data());
-
         // Open it right now.
         auto paras = QSharedPointer<FileOpenParameters>::create();
         paras->m_mode = ViewWindowMode::Edit;
@@ -283,6 +288,69 @@ void NotebookExplorer::newNote()
     }
 }
 
+void NotebookExplorer::newQuickNote()
+{
+    auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
+    const auto& schemes = sessionConfig.getQuickNoteSchemes();
+    if (schemes.isEmpty()) {
+        MessageBoxHelper::notify(MessageBoxHelper::Information,
+                                 tr("Please set up quick note schemes in the Settings dialog first."),
+                                 VNoteX::getInst().getMainWindow());
+        return;
+    }
+
+    SelectDialog dialog(tr("New Quick Note"), VNoteX::getInst().getMainWindow());
+    for (int i = 0; i < schemes.size(); ++i) {
+        dialog.addSelection(schemes[i].m_name, i);
+    }
+
+    if (dialog.exec() != QDialog::Accepted) {
+        return;
+    }
+
+    int selection = dialog.getSelection();
+    const auto &scheme = schemes[selection];
+
+    Notebook *notebook = m_currentNotebook.data();
+    Node *parentNode = currentExploredFolderNode();
+    if (!scheme.m_folderPath.isEmpty()) {
+        auto node = VNoteX::getInst().getNotebookMgr().loadNodeByPath(scheme.m_folderPath);
+        if (node) {
+            notebook = node->getNotebook();
+            parentNode = node.data();
+        }
+    }
+
+    if (!parentNode) {
+        MessageBoxHelper::notify(MessageBoxHelper::Information,
+                                 tr("The quick note should be created within a notebook."),
+                                 VNoteX::getInst().getMainWindow());
+        return;
+    }
+
+    QFileInfo finfo(SnippetMgr::getInst().applySnippetBySymbol(scheme.m_noteName));
+    QString newName = FileUtils::generateFileNameWithSequence(parentNode->fetchAbsolutePath(),
+            finfo.completeBaseName(), finfo.suffix());
+
+    QString errMsg;
+    auto newNode = NewNoteDialog::newNote(notebook, parentNode, newName,
+            TemplateMgr::getInst().getTemplateContent(scheme.m_template),
+            errMsg);
+    if (!newNode) {
+        MessageBoxHelper::notify(MessageBoxHelper::Information,
+                tr("Failed to create quick note from scheme (%1) (%2)").arg(scheme.m_name, errMsg),
+                VNoteX::getInst().getMainWindow());
+        return;
+    }
+
+    m_nodeExplorer->setCurrentNode(newNode.data());
+    // Open it right now.
+    auto paras = QSharedPointer<FileOpenParameters>::create();
+    paras->m_mode = ViewWindowMode::Edit;
+    paras->m_newFile = true;
+    emit VNoteX::getInst().openNodeRequested(newNode.data(), paras);
+}
+
 Node *NotebookExplorer::currentExploredFolderNode() const
 {
     return m_nodeExplorer->currentExploredFolderNode();

+ 2 - 0
src/widgets/notebookexplorer.h

@@ -51,6 +51,8 @@ namespace vnotex
 
         void newNote();
 
+        void newQuickNote();
+
         void importFile();
 
         void importFolder();

+ 6 - 3
src/widgets/viewarea.cpp

@@ -14,9 +14,6 @@
 #include <QHash>
 #include <QTabBar>
 
-#include "viewwindow.h"
-#include "mainwindow.h"
-#include "propertydefs.h"
 #include <utils/widgetutils.h>
 #include <utils/docsutils.h>
 #include <utils/urldragdroputils.h>
@@ -34,6 +31,12 @@
 #include "editors/plantumlhelper.h"
 #include "editors/graphvizhelper.h"
 #include <core/historymgr.h>
+#include <widgets/dialogs/selectdialog.h>
+
+#include "viewwindow.h"
+#include "mainwindow.h"
+#include "propertydefs.h"
+#include "messageboxhelper.h"
 
 using namespace vnotex;
 

+ 8 - 1
src/widgets/viewsplit.cpp

@@ -25,6 +25,7 @@
 #include <core/coreconfig.h>
 #include "propertydefs.h"
 #include "fileopenparameters.h"
+#include "sessionconfig.h"
 
 using namespace vnotex;
 
@@ -80,7 +81,13 @@ void ViewSplit::setupUI()
                 closeTab(p_idx);
             });
     connect(this, &QTabWidget::tabBarDoubleClicked,
-            this, &ViewSplit::closeTab);
+            this, [this](int p_idx) {
+                if (p_idx == -1) {
+                    emit VNoteX::getInst().newQuickNoteRequested();
+                } else {
+                    closeTab(p_idx);
+                }
+            });
     connect(this, &QTabWidget::tabBarClicked,
             this, [this](int p_idx) {
                 Q_UNUSED(p_idx);

+ 2 - 0
src/widgets/viewsplit.h

@@ -92,6 +92,8 @@ namespace vnotex
 
         void distributeSplitsRequested();
 
+        void newQuickNoteRequested();
+
         void removeSplitRequested(ViewSplit *p_split);
 
         void removeSplitAndWorkspaceRequested(ViewSplit *p_split);

+ 2 - 0
src/widgets/widgets.pri

@@ -42,6 +42,7 @@ SOURCES += \
     $$PWD/dialogs/tableinsertdialog.cpp \
     $$PWD/dialogs/updater.cpp \
     $$PWD/dialogs/viewtagsdialog.cpp \
+    $$PWD/dialogs/notetemplateselector.cpp \
     $$PWD/dockwidgethelper.cpp \
     $$PWD/dragdropareaindicator.cpp \
     $$PWD/editors/graphhelper.cpp \
@@ -181,6 +182,7 @@ HEADERS += \
     $$PWD/dialogs/tableinsertdialog.h \
     $$PWD/dialogs/updater.h \
     $$PWD/dialogs/viewtagsdialog.h \
+    $$PWD/dialogs/notetemplateselector.h \
     $$PWD/dockwidgethelper.h \
     $$PWD/dragdropareaindicator.h \
     $$PWD/editors/graphhelper.h \