From 787c61a5af5f2f8fd84ab81922ea52875cefda04 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Tue, 17 Oct 2017 19:47:57 +0800 Subject: [PATCH] support meta word - Add VLineEdit as a QLineEdit with meta data support; - support custom magic words through [magic_words]; - add %help% for all magic words information; --- src/dialog/vdirinfodialog.cpp | 42 +- src/dialog/vdirinfodialog.h | 4 +- src/dialog/vfileinfodialog.cpp | 51 ++- src/dialog/vfileinfodialog.h | 4 +- src/dialog/vinsertimagedialog.cpp | 39 +- src/dialog/vinsertimagedialog.h | 3 +- src/dialog/vnewdirdialog.cpp | 45 +- src/dialog/vnewdirdialog.h | 4 +- src/dialog/vnewfiledialog.cpp | 45 +- src/dialog/vnewfiledialog.h | 4 +- src/dialog/vnewnotebookdialog.cpp | 55 ++- src/dialog/vnewnotebookdialog.h | 3 +- src/dialog/vnotebookinfodialog.cpp | 36 +- src/dialog/vnotebookinfodialog.h | 3 +- src/dialog/vorphanfileinfodialog.cpp | 4 +- src/src.pro | 8 +- src/utils/vmetawordmanager.cpp | 593 +++++++++++++++++++++++++++ src/utils/vmetawordmanager.h | 202 +++++++++ src/utils/vutils.cpp | 12 + src/utils/vutils.h | 6 + src/vattachmentlist.cpp | 27 +- src/vconfigmanager.cpp | 18 + src/vconfigmanager.h | 4 + src/veditarea.cpp | 9 +- src/veditarea.h | 4 +- src/veditwindow.cpp | 19 +- src/veditwindow.h | 4 +- src/vlineedit.cpp | 46 +++ src/vlineedit.h | 29 ++ src/vmainwindow.cpp | 4 +- src/vnote.cpp | 11 +- src/vnote.h | 12 +- src/vpreviewpage.cpp | 5 +- 33 files changed, 1201 insertions(+), 154 deletions(-) create mode 100644 src/utils/vmetawordmanager.cpp create mode 100644 src/utils/vmetawordmanager.h create mode 100644 src/vlineedit.cpp create mode 100644 src/vlineedit.h diff --git a/src/dialog/vdirinfodialog.cpp b/src/dialog/vdirinfodialog.cpp index 63a9d477..e67c98a3 100644 --- a/src/dialog/vdirinfodialog.cpp +++ b/src/dialog/vdirinfodialog.cpp @@ -2,6 +2,7 @@ #include "vdirinfodialog.h" #include "vdirectory.h" #include "vconfigmanager.h" +#include "vlineedit.h" #include "utils/vutils.h" extern VConfigManager *g_config; @@ -16,7 +17,7 @@ VDirInfoDialog::VDirInfoDialog(const QString &title, { setupUI(); - connect(nameEdit, &QLineEdit::textChanged, this, &VDirInfoDialog::handleInputChanged); + connect(m_nameEdit, &QLineEdit::textChanged, this, &VDirInfoDialog::handleInputChanged); handleInputChanged(); } @@ -28,15 +29,18 @@ void VDirInfoDialog::setupUI() infoLabel = new QLabel(info); } - nameEdit = new QLineEdit(m_directory->getName()); - nameEdit->selectAll(); + m_nameEdit = new VLineEdit(m_directory->getName()); + QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), + m_nameEdit); + m_nameEdit->setValidator(validator); + m_nameEdit->selectAll(); // Created time. QString createdTimeStr = VUtils::displayDateTime(m_directory->getCreatedTimeUtc().toLocalTime()); QLabel *createdTimeLabel = new QLabel(createdTimeStr); QFormLayout *topLayout = new QFormLayout(); - topLayout->addRow(tr("Folder &name:"), nameEdit); + topLayout->addRow(tr("Folder &name:"), m_nameEdit); topLayout->addRow(tr("Created time:"), createdTimeLabel); m_warnLabel = new QLabel(); @@ -49,7 +53,7 @@ void VDirInfoDialog::setupUI() connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok); - nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); + m_nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); QVBoxLayout *mainLayout = new QVBoxLayout(); if (infoLabel) { @@ -67,19 +71,35 @@ void VDirInfoDialog::setupUI() void VDirInfoDialog::handleInputChanged() { bool showWarnLabel = false; - QString name = nameEdit->text(); + QString name = m_nameEdit->getEvaluatedText(); bool nameOk = !name.isEmpty(); if (nameOk && name != m_directory->getName()) { // Check if the name conflicts with existing directory name. // Case-insensitive when creating note. const VDirectory *directory = m_parentDirectory->findSubDirectory(name, false); + QString warnText; if (directory && directory != m_directory) { nameOk = false; + warnText = tr("WARNING: " + "Name (case-insensitive) %3 already exists. " + "Please choose another name.") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } else if (!VUtils::checkFileNameLegal(name)) { + // Check if evaluated name contains illegal characters. + nameOk = false; + warnText = tr("WARNING: " + "Name %3 contains illegal characters " + "(after magic word evaluation).") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } + + if (!nameOk) { showWarnLabel = true; - QString nameConflictText = tr("WARNING: Name (case-insensitive) already exists. " - "Please choose another name.") - .arg(g_config->c_warningTextStyle); - m_warnLabel->setText(nameConflictText); + m_warnLabel->setText(warnText); } } @@ -91,5 +111,5 @@ void VDirInfoDialog::handleInputChanged() QString VDirInfoDialog::getNameInput() const { - return nameEdit->text(); + return m_nameEdit->getEvaluatedText(); } diff --git a/src/dialog/vdirinfodialog.h b/src/dialog/vdirinfodialog.h index 558334df..d23a0211 100644 --- a/src/dialog/vdirinfodialog.h +++ b/src/dialog/vdirinfodialog.h @@ -4,7 +4,7 @@ #include class QLabel; -class QLineEdit; +class VLineEdit; class QDialogButtonBox; class QString; class VDirectory; @@ -27,7 +27,7 @@ private slots: private: void setupUI(); - QLineEdit *nameEdit; + VLineEdit *m_nameEdit; QLabel *m_warnLabel; QDialogButtonBox *m_btnBox; diff --git a/src/dialog/vfileinfodialog.cpp b/src/dialog/vfileinfodialog.cpp index 40373a87..4fd4a8a8 100644 --- a/src/dialog/vfileinfodialog.cpp +++ b/src/dialog/vfileinfodialog.cpp @@ -4,6 +4,7 @@ #include "vnotefile.h" #include "vconfigmanager.h" #include "utils/vutils.h" +#include "vlineedit.h" extern VConfigManager *g_config; @@ -16,7 +17,7 @@ VFileInfoDialog::VFileInfoDialog(const QString &title, { setupUI(title, info); - connect(nameEdit, &QLineEdit::textChanged, this, &VFileInfoDialog::handleInputChanged); + connect(m_nameEdit, &QLineEdit::textChanged, this, &VFileInfoDialog::handleInputChanged); handleInputChanged(); } @@ -30,7 +31,10 @@ void VFileInfoDialog::setupUI(const QString &p_title, const QString &p_info) // File name. QString name = m_file->getName(); - nameEdit = new QLineEdit(name); + m_nameEdit = new VLineEdit(name); + QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), + m_nameEdit); + m_nameEdit->setValidator(validator); int baseStart = 0, baseLength = name.size(); int dotIdx = name.lastIndexOf('.'); if (dotIdx != -1) { @@ -38,7 +42,7 @@ void VFileInfoDialog::setupUI(const QString &p_title, const QString &p_info) } // Select without suffix. - nameEdit->setSelection(baseStart, baseLength); + m_nameEdit->setSelection(baseStart, baseLength); // Attachment folder. QLineEdit *attachmentFolderEdit = new QLineEdit(m_file->getAttachmentFolder()); @@ -56,7 +60,7 @@ void VFileInfoDialog::setupUI(const QString &p_title, const QString &p_info) modifiedTimeLabel->setToolTip(tr("Last modified time within VNote")); QFormLayout *topLayout = new QFormLayout(); - topLayout->addRow(tr("Note &name:"), nameEdit); + topLayout->addRow(tr("Note &name:"), m_nameEdit); topLayout->addRow(tr("Attachment folder:"), attachmentFolderEdit); topLayout->addRow(tr("Created time:"), createdTimeLabel); topLayout->addRow(tr("Modified time:"), modifiedTimeLabel); @@ -71,7 +75,7 @@ void VFileInfoDialog::setupUI(const QString &p_title, const QString &p_info) connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok); - nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); + m_nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); QVBoxLayout *mainLayout = new QVBoxLayout(); if (infoLabel) { @@ -90,28 +94,43 @@ void VFileInfoDialog::setupUI(const QString &p_title, const QString &p_info) void VFileInfoDialog::handleInputChanged() { bool showWarnLabel = false; - QString name = nameEdit->text(); + QString name = m_nameEdit->getEvaluatedText(); bool nameOk = !name.isEmpty(); if (nameOk && name != m_file->getName()) { // Check if the name conflicts with existing note name. // Case-insensitive when creating note. const VNoteFile *file = m_directory->findFile(name, false); + QString warnText; if (file && file != m_file) { nameOk = false; - showWarnLabel = true; - QString nameConflictText = tr("WARNING: Name (case-insensitive) already exists. " - "Please choose another name.") - .arg(g_config->c_warningTextStyle); - m_warnLabel->setText(nameConflictText); + warnText = tr("WARNING: " + "Name (case-insensitive) %3 already exists. " + "Please choose another name.") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); } else if (m_file->getDocType() != DocType::Unknown && VUtils::docTypeFromName(name) != m_file->getDocType()) { // Check if the name change the doc type. nameOk = false; + warnText = tr("WARNING: " + "Changing type of the note is not supported. " + "Please use the same suffix as the old one.") + .arg(g_config->c_warningTextStyle); + } else if (!VUtils::checkFileNameLegal(name)) { + // Check if evaluated name contains illegal characters. + nameOk = false; + warnText = tr("WARNING: " + "Name %3 contains illegal characters " + "(after magic word evaluation).") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } + + if (!nameOk) { showWarnLabel = true; - QString nameConflictText = tr("WARNING: Changing type of the note is not supported. " - "Please use the same suffix as the old one.") - .arg(g_config->c_warningTextStyle); - m_warnLabel->setText(nameConflictText); + m_warnLabel->setText(warnText); } } @@ -123,5 +142,5 @@ void VFileInfoDialog::handleInputChanged() QString VFileInfoDialog::getNameInput() const { - return nameEdit->text(); + return m_nameEdit->getEvaluatedText(); } diff --git a/src/dialog/vfileinfodialog.h b/src/dialog/vfileinfodialog.h index 939cfcdf..015705f0 100644 --- a/src/dialog/vfileinfodialog.h +++ b/src/dialog/vfileinfodialog.h @@ -4,7 +4,7 @@ #include class QLabel; -class QLineEdit; +class VLineEdit; class QDialogButtonBox; class QString; class VDirectory; @@ -29,7 +29,7 @@ private slots: private: void setupUI(const QString &p_title, const QString &p_info); - QLineEdit *nameEdit; + VLineEdit *m_nameEdit; QLabel *m_warnLabel; QDialogButtonBox *m_btnBox; diff --git a/src/dialog/vinsertimagedialog.cpp b/src/dialog/vinsertimagedialog.cpp index 9b0b090e..ff0a724f 100644 --- a/src/dialog/vinsertimagedialog.cpp +++ b/src/dialog/vinsertimagedialog.cpp @@ -3,6 +3,7 @@ #include #include "vinsertimagedialog.h" #include "utils/vutils.h" +#include "vlineedit.h" VInsertImageDialog::VInsertImageDialog(const QString &title, const QString &defaultImageTitle, const QString &defaultPath, QWidget *parent) @@ -11,9 +12,12 @@ VInsertImageDialog::VInsertImageDialog(const QString &title, const QString &defa { setupUI(); - connect(imageTitleEdit, &QLineEdit::textChanged, this, &VInsertImageDialog::enableOkButton); - connect(pathEdit, &QLineEdit::textChanged, this, &VInsertImageDialog::enableOkButton); - connect(browseBtn, &QPushButton::clicked, this, &VInsertImageDialog::handleBrowseBtnClicked); + connect(m_imageTitleEdit, &QLineEdit::textChanged, + this, &VInsertImageDialog::enableOkButton); + connect(pathEdit, &QLineEdit::textChanged, + this, &VInsertImageDialog::enableOkButton); + connect(browseBtn, &QPushButton::clicked, + this, &VInsertImageDialog::handleBrowseBtnClicked); enableOkButton(); } @@ -34,19 +38,19 @@ void VInsertImageDialog::setupUI() browseBtn = new QPushButton(tr("&Browse")); imageTitleLabel = new QLabel(tr("&Image title:")); - imageTitleEdit = new QLineEdit(defaultImageTitle); - imageTitleEdit->selectAll(); - imageTitleLabel->setBuddy(imageTitleEdit); - QRegExp regExp("[\\w\\(\\)@#%\\*\\-\\+=\\?<>\\,\\.\\s]+"); - QValidator *validator = new QRegExpValidator(regExp, this); - imageTitleEdit->setValidator(validator); + m_imageTitleEdit = new VLineEdit(defaultImageTitle); + m_imageTitleEdit->selectAll(); + imageTitleLabel->setBuddy(m_imageTitleEdit); + QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_imageTitleRegExp), + m_imageTitleEdit); + m_imageTitleEdit->setValidator(validator); QGridLayout *topLayout = new QGridLayout(); topLayout->addWidget(pathLabel, 0, 0); topLayout->addWidget(pathEdit, 0, 1); topLayout->addWidget(browseBtn, 0, 2); topLayout->addWidget(imageTitleLabel, 1, 0); - topLayout->addWidget(imageTitleEdit, 1, 1, 1, 2); + topLayout->addWidget(m_imageTitleEdit, 1, 1, 1, 2); topLayout->setColumnStretch(0, 0); topLayout->setColumnStretch(1, 1); topLayout->setColumnStretch(2, 0); @@ -67,22 +71,25 @@ void VInsertImageDialog::setupUI() mainLayout->setSizeConstraint(QLayout::SetFixedSize); setWindowTitle(title); - imageTitleEdit->setFocus(); + m_imageTitleEdit->setFocus(); } void VInsertImageDialog::enableOkButton() { - bool enabled = true; - if (imageTitleEdit->text().isEmpty() || !image) { - enabled = false; + QString title = m_imageTitleEdit->getEvaluatedText(); + bool titleOk = !title.isEmpty(); + if (titleOk) { + QRegExp reg(VUtils::c_imageTitleRegExp); + titleOk = reg.exactMatch(title); } + QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok); - okBtn->setEnabled(enabled); + okBtn->setEnabled(titleOk); } QString VInsertImageDialog::getImageTitleInput() const { - return imageTitleEdit->text(); + return m_imageTitleEdit->getEvaluatedText(); } QString VInsertImageDialog::getPathInput() const diff --git a/src/dialog/vinsertimagedialog.h b/src/dialog/vinsertimagedialog.h index b16b7cb8..670f50d1 100644 --- a/src/dialog/vinsertimagedialog.h +++ b/src/dialog/vinsertimagedialog.h @@ -8,6 +8,7 @@ class QLabel; class QLineEdit; +class VLineEdit; class QPushButton; class QDialogButtonBox; @@ -37,7 +38,7 @@ private: void setupUI(); QLabel *imageTitleLabel; - QLineEdit *imageTitleEdit; + VLineEdit *m_imageTitleEdit; QLabel *pathLabel; QLineEdit *pathEdit; QPushButton *browseBtn; diff --git a/src/dialog/vnewdirdialog.cpp b/src/dialog/vnewdirdialog.cpp index 7a8bff59..24ad8ccc 100644 --- a/src/dialog/vnewdirdialog.cpp +++ b/src/dialog/vnewdirdialog.cpp @@ -2,6 +2,8 @@ #include "vnewdirdialog.h" #include "vdirectory.h" #include "vconfigmanager.h" +#include "vlineedit.h" +#include "utils/vutils.h" extern VConfigManager *g_config; @@ -15,7 +17,7 @@ VNewDirDialog::VNewDirDialog(const QString &title, { setupUI(); - connect(nameEdit, &QLineEdit::textChanged, this, &VNewDirDialog::handleInputChanged); + connect(m_nameEdit, &QLineEdit::textChanged, this, &VNewDirDialog::handleInputChanged); handleInputChanged(); } @@ -29,9 +31,12 @@ void VNewDirDialog::setupUI() } QLabel *nameLabel = new QLabel(tr("Folder &name:")); - nameEdit = new QLineEdit(defaultName); - nameEdit->selectAll(); - nameLabel->setBuddy(nameEdit); + m_nameEdit = new VLineEdit(defaultName); + QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), + m_nameEdit); + m_nameEdit->setValidator(validator); + m_nameEdit->selectAll(); + nameLabel->setBuddy(m_nameEdit); m_warnLabel = new QLabel(); m_warnLabel->setWordWrap(true); @@ -44,10 +49,10 @@ void VNewDirDialog::setupUI() QHBoxLayout *topLayout = new QHBoxLayout(); topLayout->addWidget(nameLabel); - topLayout->addWidget(nameEdit); + topLayout->addWidget(m_nameEdit); QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok); - nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); + m_nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); QVBoxLayout *mainLayout = new QVBoxLayout(); if (infoLabel) { @@ -64,18 +69,34 @@ void VNewDirDialog::setupUI() void VNewDirDialog::handleInputChanged() { bool showWarnLabel = false; - QString name = nameEdit->text(); + QString name = m_nameEdit->getEvaluatedText(); bool nameOk = !name.isEmpty(); if (nameOk) { // Check if the name conflicts with existing directory name. // Case-insensitive when creating folder. + QString warnText; if (m_directory->findSubDirectory(name, false)) { nameOk = false; + warnText = tr("WARNING: " + "Name (case-insensitive) %3 already exists. " + "Please choose another name.") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } else if (!VUtils::checkFileNameLegal(name)) { + // Check if evaluated name contains illegal characters. + nameOk = false; + warnText = tr("WARNING: " + "Name %3 contains illegal characters " + "(after magic word evaluation).") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } + + if (!nameOk) { showWarnLabel = true; - QString nameConflictText = tr("WARNING: Name (case-insensitive) already exists. " - "Please choose another name.") - .arg(g_config->c_warningTextStyle); - m_warnLabel->setText(nameConflictText); + m_warnLabel->setText(warnText); } } @@ -87,5 +108,5 @@ void VNewDirDialog::handleInputChanged() QString VNewDirDialog::getNameInput() const { - return nameEdit->text(); + return m_nameEdit->getEvaluatedText(); } diff --git a/src/dialog/vnewdirdialog.h b/src/dialog/vnewdirdialog.h index 30ca9727..8594c594 100644 --- a/src/dialog/vnewdirdialog.h +++ b/src/dialog/vnewdirdialog.h @@ -4,7 +4,7 @@ #include class QLabel; -class QLineEdit; +class VLineEdit; class QDialogButtonBox; class QString; class VDirectory; @@ -27,7 +27,7 @@ private slots: private: void setupUI(); - QLineEdit *nameEdit; + VLineEdit *m_nameEdit; QDialogButtonBox *m_btnBox; QLabel *m_warnLabel; diff --git a/src/dialog/vnewfiledialog.cpp b/src/dialog/vnewfiledialog.cpp index 6ef321ff..730728cc 100644 --- a/src/dialog/vnewfiledialog.cpp +++ b/src/dialog/vnewfiledialog.cpp @@ -2,6 +2,8 @@ #include "vnewfiledialog.h" #include "vconfigmanager.h" #include "vdirectory.h" +#include "vlineedit.h" +#include "utils/vutils.h" extern VConfigManager *g_config; @@ -13,7 +15,7 @@ VNewFileDialog::VNewFileDialog(const QString &title, const QString &info, { setupUI(); - connect(nameEdit, &QLineEdit::textChanged, this, &VNewFileDialog::handleInputChanged); + connect(m_nameEdit, &VLineEdit::textChanged, this, &VNewFileDialog::handleInputChanged); handleInputChanged(); } @@ -27,10 +29,13 @@ void VNewFileDialog::setupUI() // Name. QLabel *nameLabel = new QLabel(tr("Note &name:")); - nameEdit = new QLineEdit(defaultName); + m_nameEdit = new VLineEdit(defaultName); + QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), + m_nameEdit); + m_nameEdit->setValidator(validator); int dotIndex = defaultName.lastIndexOf('.'); - nameEdit->setSelection(0, (dotIndex == -1) ? defaultName.size() : dotIndex); - nameLabel->setBuddy(nameEdit); + m_nameEdit->setSelection(0, (dotIndex == -1) ? defaultName.size() : dotIndex); + nameLabel->setBuddy(m_nameEdit); // InsertTitle. m_insertTitleCB = new QCheckBox(tr("Insert note name as title (for Markdown only)")); @@ -42,10 +47,10 @@ void VNewFileDialog::setupUI() }); QFormLayout *topLayout = new QFormLayout(); - topLayout->addRow(nameLabel, nameEdit); + topLayout->addRow(nameLabel, m_nameEdit); topLayout->addWidget(m_insertTitleCB); - nameEdit->setMinimumWidth(m_insertTitleCB->sizeHint().width()); + m_nameEdit->setMinimumWidth(m_insertTitleCB->sizeHint().width()); m_warnLabel = new QLabel(); m_warnLabel->setWordWrap(true); @@ -73,18 +78,34 @@ void VNewFileDialog::setupUI() void VNewFileDialog::handleInputChanged() { bool showWarnLabel = false; - QString name = nameEdit->text(); + QString name = m_nameEdit->getEvaluatedText(); bool nameOk = !name.isEmpty(); if (nameOk) { // Check if the name conflicts with existing note name. // Case-insensitive when creating note. + QString warnText; if (m_directory->findFile(name, false)) { nameOk = false; + warnText = tr("WARNING: " + "Name (case-insensitive) %3 already exists. " + "Please choose another name.") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } else if (!VUtils::checkFileNameLegal(name)) { + // Check if evaluated name contains illegal characters. + nameOk = false; + warnText = tr("WARNING: " + "Name %3 contains illegal characters " + "(after magic word evaluation).") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } + + if (!nameOk) { showWarnLabel = true; - QString nameConflictText = tr("WARNING: Name (case-insensitive) already exists. " - "Please choose another name.") - .arg(g_config->c_warningTextStyle); - m_warnLabel->setText(nameConflictText); + m_warnLabel->setText(warnText); } } @@ -96,7 +117,7 @@ void VNewFileDialog::handleInputChanged() QString VNewFileDialog::getNameInput() const { - return nameEdit->text(); + return m_nameEdit->getEvaluatedText(); } bool VNewFileDialog::getInsertTitleInput() const diff --git a/src/dialog/vnewfiledialog.h b/src/dialog/vnewfiledialog.h index 2eeb8370..496e90df 100644 --- a/src/dialog/vnewfiledialog.h +++ b/src/dialog/vnewfiledialog.h @@ -4,7 +4,7 @@ #include class QLabel; -class QLineEdit; +class VLineEdit; class QDialogButtonBox; class QCheckBox; class VDirectory; @@ -27,7 +27,7 @@ private slots: private: void setupUI(); - QLineEdit *nameEdit; + VLineEdit *m_nameEdit; QCheckBox *m_insertTitleCB; QPushButton *okBtn; diff --git a/src/dialog/vnewnotebookdialog.cpp b/src/dialog/vnewnotebookdialog.cpp index b9856c11..f211bdc0 100644 --- a/src/dialog/vnewnotebookdialog.cpp +++ b/src/dialog/vnewnotebookdialog.cpp @@ -4,6 +4,7 @@ #include "vconfigmanager.h" #include "utils/vutils.h" #include "vnotebook.h" +#include "vlineedit.h" extern VConfigManager *g_config; @@ -18,7 +19,7 @@ VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info { setupUI(title, info); - connect(nameEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged); + connect(m_nameEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged); connect(pathEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged); connect(browseBtn, &QPushButton::clicked, this, &VNewNotebookDialog::handleBrowseBtnClicked); @@ -34,8 +35,11 @@ void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info) } QLabel *nameLabel = new QLabel(tr("Notebook &name:")); - nameEdit = new QLineEdit(defaultName); - nameLabel->setBuddy(nameEdit); + m_nameEdit = new VLineEdit(defaultName); + QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), + m_nameEdit); + m_nameEdit->setValidator(validator); + nameLabel->setBuddy(m_nameEdit); QLabel *pathLabel = new QLabel(tr("Notebook &root folder:")); pathEdit = new QLineEdit(defaultPath); @@ -50,7 +54,7 @@ void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info) m_imageFolderEdit->setToolTip(tr("Set the name of the folder to hold images of all the notes in this notebook " "(empty to use global configuration)")); imageFolderLabel->setToolTip(m_imageFolderEdit->toolTip()); - QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit); + validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit); m_imageFolderEdit->setValidator(validator); QLabel *attachmentFolderLabel = new QLabel(tr("&Attachment folder:")); @@ -66,7 +70,7 @@ void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info) QGridLayout *topLayout = new QGridLayout(); topLayout->addWidget(nameLabel, 0, 0); - topLayout->addWidget(nameEdit, 0, 1, 1, 2); + topLayout->addWidget(m_nameEdit, 0, 1, 1, 2); topLayout->addWidget(pathLabel, 1, 0); topLayout->addWidget(pathEdit, 1, 1); topLayout->addWidget(browseBtn, 1, 2); @@ -104,7 +108,7 @@ void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info) QString VNewNotebookDialog::getNameInput() const { - return nameEdit->text(); + return m_nameEdit->getEvaluatedText(); } QString VNewNotebookDialog::getPathInput() const @@ -161,7 +165,7 @@ bool VNewNotebookDialog::isImportExistingNotebook() const void VNewNotebookDialog::showEvent(QShowEvent *event) { - nameEdit->setFocus(); + m_nameEdit->setFocus(); QDialog::showEvent(event); } @@ -183,7 +187,7 @@ void VNewNotebookDialog::handleInputChanged() m_manualPath = true; } - if (nameEdit->isModified()) { + if (m_nameEdit->isModified()) { m_manualName = true; } @@ -248,7 +252,7 @@ void VNewNotebookDialog::handleInputChanged() } } - QString name = nameEdit->text(); + QString name = m_nameEdit->getEvaluatedText(); bool nameOk = !name.isEmpty(); if (pathOk && nameOk) { // Check if the name conflicts with existing notebook name. @@ -260,13 +264,29 @@ void VNewNotebookDialog::handleInputChanged() } } + QString warnText; if (idx < m_notebooks.size()) { nameOk = false; + warnText = tr("WARNING: " + "Name (case-insensitive) %3 already exists. " + "Please choose another name.") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } else if (!VUtils::checkFileNameLegal(name)) { + // Check if evaluated name contains illegal characters. + nameOk = false; + warnText = tr("WARNING: " + "Name %3 contains illegal characters " + "(after magic word evaluation).") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } + + if (!nameOk) { showWarnLabel = true; - QString nameConflictText = tr("WARNING: Name (case-insensitive) already exists. " - "Please choose another name.") - .arg(g_config->c_warningTextStyle); - m_warnLabel->setText(nameConflictText); + m_warnLabel->setText(warnText); } } @@ -281,6 +301,8 @@ void VNewNotebookDialog::handleInputChanged() bool VNewNotebookDialog::autoComplete() { + QString nameText = m_nameEdit->getEvaluatedText(); + if (m_manualPath) { if (m_manualName) { return false; @@ -290,8 +312,8 @@ bool VNewNotebookDialog::autoComplete() QString pathText = pathEdit->text(); if (!pathText.isEmpty()) { QString autoName = VUtils::directoryNameFromPath(pathText); - if (autoName != nameEdit->text()) { - nameEdit->setText(autoName); + if (autoName != nameText) { + m_nameEdit->setText(autoName); return true; } } @@ -307,7 +329,6 @@ bool VNewNotebookDialog::autoComplete() } bool ret = false; - QString nameText = nameEdit->text(); if (nameText.isEmpty()) { if (m_manualName) { return false; @@ -316,7 +337,7 @@ bool VNewNotebookDialog::autoComplete() // Get a folder name under vnoteFolder and set it as the name of the notebook. QString name = "vnotebook"; name = VUtils::getDirNameWithSequence(vnoteFolder, name); - nameEdit->setText(name); + m_nameEdit->setText(name); ret = true; } else { // Use the name as the folder name under vnoteFolder. diff --git a/src/dialog/vnewnotebookdialog.h b/src/dialog/vnewnotebookdialog.h index d1f38105..87d0d9b8 100644 --- a/src/dialog/vnewnotebookdialog.h +++ b/src/dialog/vnewnotebookdialog.h @@ -6,6 +6,7 @@ class QLabel; class QLineEdit; +class VLineEdit; class QPushButton; class QDialogButtonBox; class VNotebook; @@ -52,7 +53,7 @@ private: // Returns true if name or path is modified. bool autoComplete(); - QLineEdit *nameEdit; + VLineEdit *m_nameEdit; QLineEdit *pathEdit; QPushButton *browseBtn; QLabel *m_warnLabel; diff --git a/src/dialog/vnotebookinfodialog.cpp b/src/dialog/vnotebookinfodialog.cpp index d52eec1d..f7e41eb2 100644 --- a/src/dialog/vnotebookinfodialog.cpp +++ b/src/dialog/vnotebookinfodialog.cpp @@ -3,6 +3,7 @@ #include "vnotebook.h" #include "utils/vutils.h" #include "vconfigmanager.h" +#include "vlineedit.h" extern VConfigManager *g_config; @@ -29,7 +30,10 @@ void VNotebookInfoDialog::setupUI(const QString &p_title, const QString &p_info) infoLabel = new QLabel(p_info); } - m_nameEdit = new QLineEdit(m_notebook->getName()); + m_nameEdit = new VLineEdit(m_notebook->getName()); + QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), + m_nameEdit); + m_nameEdit->setValidator(validator); m_nameEdit->selectAll(); m_pathEdit = new QLineEdit(m_notebook->getPath()); @@ -41,7 +45,7 @@ void VNotebookInfoDialog::setupUI(const QString &p_title, const QString &p_info) .arg(g_config->getImageFolder())); m_imageFolderEdit->setToolTip(tr("Set the name of the folder to hold images of all the notes in this notebook " "(empty to use global configuration)")); - QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit); + validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit); m_imageFolderEdit->setValidator(validator); // Attachment folder. @@ -98,7 +102,7 @@ void VNotebookInfoDialog::setupUI(const QString &p_title, const QString &p_info) void VNotebookInfoDialog::handleInputChanged() { - QString name = m_nameEdit->text(); + QString name = m_nameEdit->getEvaluatedText(); bool nameOk = !name.isEmpty(); bool showWarnLabel = false; @@ -112,13 +116,29 @@ void VNotebookInfoDialog::handleInputChanged() } } + QString warnText; if (idx < m_notebooks.size() && m_notebooks[idx] != m_notebook) { nameOk = false; + warnText = tr("WARNING: " + "Name (case-insensitive) %3 already exists. " + "Please choose another name.") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } else if (!VUtils::checkFileNameLegal(name)) { + // Check if evaluated name contains illegal characters. + nameOk = false; + warnText = tr("WARNING: " + "Name %3 contains illegal characters " + "(after magic word evaluation).") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(name); + } + + if (!nameOk) { showWarnLabel = true; - QString nameConflictText = tr("WARNING: Name (case-insensitive) already exists. " - "Please choose another name.") - .arg(g_config->c_warningTextStyle); - m_warnLabel->setText(nameConflictText); + m_warnLabel->setText(warnText); } } @@ -130,7 +150,7 @@ void VNotebookInfoDialog::handleInputChanged() QString VNotebookInfoDialog::getName() const { - return m_nameEdit->text(); + return m_nameEdit->getEvaluatedText(); } QString VNotebookInfoDialog::getImageFolder() const diff --git a/src/dialog/vnotebookinfodialog.h b/src/dialog/vnotebookinfodialog.h index 68d02fbd..d1b4c234 100644 --- a/src/dialog/vnotebookinfodialog.h +++ b/src/dialog/vnotebookinfodialog.h @@ -6,6 +6,7 @@ class QLabel; class QLineEdit; +class VLineEdit; class QDialogButtonBox; class QString; class VNotebook; @@ -38,7 +39,7 @@ private: const VNotebook *m_notebook; - QLineEdit *m_nameEdit; + VLineEdit *m_nameEdit; QLineEdit *m_pathEdit; QLineEdit *m_imageFolderEdit; // Read-only. diff --git a/src/dialog/vorphanfileinfodialog.cpp b/src/dialog/vorphanfileinfodialog.cpp index 579865d6..aa9a48af 100644 --- a/src/dialog/vorphanfileinfodialog.cpp +++ b/src/dialog/vorphanfileinfodialog.cpp @@ -25,7 +25,6 @@ void VOrphanFileInfoDialog::setupUI() QLabel *fileLabel = new QLabel(m_file->fetchPath()); topLayout->addRow(tr("File:"), fileLabel); - QLabel *imageFolderLabel = new QLabel(tr("Image folder:")); m_imageFolderEdit = new QLineEdit(m_file->getImageFolder()); m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)") .arg(g_config->getImageFolderExt())); @@ -33,9 +32,8 @@ void VOrphanFileInfoDialog::setupUI() "of this file.\nIf absolute path is used, " "VNote will not manage those images." "(empty to use global configuration)"); - imageFolderLabel->setToolTip(imgFolderTip); m_imageFolderEdit->setToolTip(imgFolderTip); - topLayout->addRow(imageFolderLabel, m_imageFolderEdit); + topLayout->addRow(tr("&Image folder:"), m_imageFolderEdit); // Ok is the default button. m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); diff --git a/src/src.pro b/src/src.pro index f575f9d1..8831b063 100644 --- a/src/src.pro +++ b/src/src.pro @@ -78,7 +78,9 @@ SOURCES += main.cpp\ vattachmentlist.cpp \ dialog/vsortdialog.cpp \ vfilesessioninfo.cpp \ - vtableofcontent.cpp + vtableofcontent.cpp \ + utils/vmetawordmanager.cpp \ + vlineedit.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -144,7 +146,9 @@ HEADERS += vmainwindow.h \ vattachmentlist.h \ dialog/vsortdialog.h \ vfilesessioninfo.h \ - vtableofcontent.h + vtableofcontent.h \ + utils/vmetawordmanager.h \ + vlineedit.h RESOURCES += \ vnote.qrc \ diff --git a/src/utils/vmetawordmanager.cpp b/src/utils/vmetawordmanager.cpp new file mode 100644 index 00000000..d32efb2f --- /dev/null +++ b/src/utils/vmetawordmanager.cpp @@ -0,0 +1,593 @@ +#include "vmetawordmanager.h" + +#include +#include +#include +#include + +#include "vconfigmanager.h" + +extern VConfigManager *g_config; + + +// Used as the function template for some date/time related meta words. +static QString formattedDateTime(const VMetaWord *p_metaWord, + const QString &p_format) +{ + return p_metaWord->getManager()->getDateTime().toString(p_format); +} + +static QString allMetaWordsInfo(const VMetaWord *p_metaWord) +{ + QString msg = QObject::tr("All magic words:"); + + const VMetaWordManager *mgr = p_metaWord->getManager(); + QList keys = mgr->getAllMetaWords().keys(); + keys.sort(Qt::CaseInsensitive); + + for (auto const & key : keys) { + const VMetaWord *word = mgr->findMetaWord(key); + Q_ASSERT(word); + msg += QString("\n%1:\t%2").arg(word->getWord()).arg(word->getDefinition()); + } + + QWidget *focusWid = QApplication::focusWidget(); + if (focusWid) { + QPoint pos = focusWid->mapToGlobal(QPoint(0, focusWid->height())); + QToolTip::showText(pos, msg, focusWid); + } + + // Just return the same word. + return QString("%1help%1").arg(VMetaWordManager::c_delimiter); +} + +const QChar VMetaWordManager::c_delimiter = '%'; + +VMetaWordManager::VMetaWordManager(QObject *p_parent) + : QObject(p_parent) +{ +} + +void VMetaWordManager::init() +{ + using namespace std::placeholders; + + // %d%. + addMetaWord(MetaWordType::FunctionBased, + "d", + tr("the day as number without a leading zero (`1` to `31`)"), + std::bind(formattedDateTime, _1, "d")); + + // %dd%. + addMetaWord(MetaWordType::FunctionBased, + "dd", + tr("the day as number with a leading zero (`01` to `31`)"), + std::bind(formattedDateTime, _1, "dd")); + + // %ddd%. + addMetaWord(MetaWordType::FunctionBased, + "ddd", + tr("the abbreviated localized day name (e.g. `Mon` to `Sun`)"), + std::bind(formattedDateTime, _1, "ddd")); + + // %dddd%. + addMetaWord(MetaWordType::FunctionBased, + "dddd", + tr("the long localized day name (e.g. `Monday` to `Sunday`)"), + std::bind(formattedDateTime, _1, "dddd")); + + // %M%. + addMetaWord(MetaWordType::FunctionBased, + "M", + tr("the month as number without a leading zero (`1` to `12`)"), + std::bind(formattedDateTime, _1, "M")); + + // %MM%. + addMetaWord(MetaWordType::FunctionBased, + "MM", + tr("the month as number with a leading zero (`01` to `12`)"), + std::bind(formattedDateTime, _1, "MM")); + + // %MMM%. + addMetaWord(MetaWordType::FunctionBased, + "MMM", + tr("the abbreviated localized month name (e.g. `Jan` to `Dec`)"), + std::bind(formattedDateTime, _1, "MMM")); + + // %MMMM%. + addMetaWord(MetaWordType::FunctionBased, + "MMMM", + tr("the long localized month name (e.g. `January` to `December`"), + std::bind(formattedDateTime, _1, "MMMM")); + + // %yy%. + addMetaWord(MetaWordType::FunctionBased, + "yy", + tr("the year as two digit number (`00` to `99`)"), + std::bind(formattedDateTime, _1, "yy")); + + // %yyyy%. + addMetaWord(MetaWordType::FunctionBased, + "yyyy", + tr("the year as four digit number"), + std::bind(formattedDateTime, _1, "yyyy")); + + // %h%. + addMetaWord(MetaWordType::FunctionBased, + "h", + tr("the hour without a leading zero (`0` to `23` or `1` to `12` if AM/PM display"), + std::bind(formattedDateTime, _1, "h")); + + // %hh%. + addMetaWord(MetaWordType::FunctionBased, + "hh", + tr("the hour with a leading zero (`00` to `23` or `01` to `12` if AM/PM display"), + std::bind(formattedDateTime, _1, "hh")); + + // %H%. + addMetaWord(MetaWordType::FunctionBased, + "H", + tr("the hour without a leading zero (`0` to `23` even with AM/PM display"), + std::bind(formattedDateTime, _1, "H")); + + // %HH%. + addMetaWord(MetaWordType::FunctionBased, + "HH", + tr("the hour with a leading zero (`00` to `23` even with AM/PM display"), + std::bind(formattedDateTime, _1, "HH")); + + // %m%. + addMetaWord(MetaWordType::FunctionBased, + "m", + tr("the minute without a leading zero (`0` to `59`)"), + std::bind(formattedDateTime, _1, "m")); + + // %mm%. + addMetaWord(MetaWordType::FunctionBased, + "mm", + tr("the minute with a leading zero (`00` to `59`)"), + std::bind(formattedDateTime, _1, "mm")); + + // %s%. + addMetaWord(MetaWordType::FunctionBased, + "s", + tr("the second without a leading zero (`0` to `59`)"), + std::bind(formattedDateTime, _1, "s")); + + // %ss%. + addMetaWord(MetaWordType::FunctionBased, + "ss", + tr("the second with a leading zero (`00` to `59`)"), + std::bind(formattedDateTime, _1, "ss")); + + // %z%. + addMetaWord(MetaWordType::FunctionBased, + "z", + tr("the milliseconds without leading zeroes (`0` to `999`)"), + std::bind(formattedDateTime, _1, "z")); + + // %zzz%. + addMetaWord(MetaWordType::FunctionBased, + "zzz", + tr("the milliseconds with leading zeroes (`000` to `999`)"), + std::bind(formattedDateTime, _1, "zzz")); + + // %AP%. + addMetaWord(MetaWordType::FunctionBased, + "AP", + tr("use AM/PM display (`AM` or `PM`)"), + std::bind(formattedDateTime, _1, "AP")); + + // %A%. + addMetaWord(MetaWordType::FunctionBased, + "A", + tr("use AM/PM display (`AM` or `PM`)"), + std::bind(formattedDateTime, _1, "A")); + + // %ap%. + addMetaWord(MetaWordType::FunctionBased, + "ap", + tr("use am/pm display (`am` or `pm`)"), + std::bind(formattedDateTime, _1, "ap")); + + // %a%. + addMetaWord(MetaWordType::FunctionBased, + "a", + tr("use am/pm display (`am` or `pm`)"), + std::bind(formattedDateTime, _1, "a")); + + // %t%. + addMetaWord(MetaWordType::FunctionBased, + "t", + tr("the timezone (e.g. `CEST`)"), + std::bind(formattedDateTime, _1, "t")); + + // %random%. + addMetaWord(MetaWordType::FunctionBased, + "random", + tr("a random number"), + [](const VMetaWord *) { + return QString::number(qrand()); + }); + + // %random_d%. + addMetaWord(MetaWordType::Dynamic, + "random_d", + tr("dynamic version of `random`"), + [](const VMetaWord *) { + return QString::number(qrand()); + }); + + // %date%. + addMetaWord(MetaWordType::Compound, + "date", + QString("%1yyyy%1-%1MM%1-%1dd%1").arg(c_delimiter)); + + // %da%. + addMetaWord(MetaWordType::Compound, + "da", + QString("%1yyyy%1%1MM%1%1dd%1").arg(c_delimiter)); + + // %time%. + addMetaWord(MetaWordType::Compound, + "time", + QString("%1hh%1:%1mm%1:%1ss%1").arg(c_delimiter)); + + // %datetime%. + addMetaWord(MetaWordType::Compound, + "datetime", + QString("%1date%1 %1time%1").arg(c_delimiter)); + + // Custom meta words. + initCustomMetaWords(); + + // %help% to print all metaword info. + addMetaWord(MetaWordType::FunctionBased, + "help", + tr("information about all defined magic words"), + allMetaWordsInfo); +} + +void VMetaWordManager::initCustomMetaWords() +{ + QVector words = g_config->getCustomMagicWords(); + for (auto const & item : words) { + addMetaWord(MetaWordType::Compound, + item.m_name, + item.m_definition); + } +} + +QString VMetaWordManager::evaluate(const QString &p_text) const +{ + if (p_text.isEmpty()) { + return p_text; + } + + // Update datetime for later parse. + const_cast(this)->m_dateTime = QDateTime::currentDateTime(); + + // Treat the text as a Compound meta word. + const QString tmpWord("vnote_tmp_metaword"); + Q_ASSERT(!contains(tmpWord)); + VMetaWord metaWord(this, + MetaWordType::Compound, + tmpWord, + p_text); + if (metaWord.isValid()) { + return metaWord.evaluate(); + } else { + return p_text; + } +} + +bool VMetaWordManager::contains(const QString &p_word) const +{ + return m_metaWords.contains(p_word); +} + +const VMetaWord *VMetaWordManager::findMetaWord(const QString &p_word) const +{ + auto it = m_metaWords.find(p_word); + if (it != m_metaWords.end()) { + return &it.value(); + } + + return NULL; +} + +void VMetaWordManager::addMetaWord(MetaWordType p_type, + const QString &p_word, + const QString &p_definition, + MetaWordFunc p_function) +{ + if (p_word.isEmpty() || contains(p_word)) { + return; + } + + VMetaWord metaWord(this, + p_type, + p_word, + p_definition, + p_function); + + if (metaWord.isValid()) { + m_metaWords.insert(p_word, metaWord); + qDebug() << QString("MetaWord %1%2%1[%3] added") + .arg(c_delimiter).arg(p_word).arg(p_definition); + } +} + +VMetaWord::VMetaWord(const VMetaWordManager *p_manager, + MetaWordType p_type, + const QString &p_word, + const QString &p_definition, + MetaWordFunc p_function) + : m_manager(p_manager), + m_type(p_type), + m_word(p_word), + m_definition(p_definition), + m_valid(false) +{ + m_function = p_function; + + if (checkType(MetaWordType::Simple) + || checkType(MetaWordType::Compound)) { + Q_ASSERT(!m_function); + } else { + Q_ASSERT(m_function); + } + + checkAndParseDefinition(); +} + +bool VMetaWord::checkType(MetaWordType p_type) +{ + return m_type == p_type; +} + +void VMetaWord::checkAndParseDefinition() +{ + if (m_word.contains(VMetaWordManager::c_delimiter)) { + m_valid = false; + return; + } + + m_valid = true; + m_tokens.clear(); + + // We do not accept \n and \t in the definition. + QRegExp defReg("[\\S ]*"); + if (!defReg.exactMatch(m_definition)) { + m_valid = false; + return; + } + + if (checkType(MetaWordType::FunctionBased) + || checkType(MetaWordType::Dynamic)) { + if (!m_function) { + m_valid = false; + } + } else if (checkType(MetaWordType::Compound)) { + m_tokens = parseToTokens(m_definition); + if (m_tokens.isEmpty()) { + m_valid = false; + return; + } + + for (auto const & to : m_tokens) { + if (to.isMetaWord()) { + if (!m_manager->contains(to.m_value)) { + // Dependency not defined. + m_valid = false; + break; + } + } + } + } +} + +bool VMetaWord::isValid() const +{ + return m_valid; +} + +QString VMetaWord::evaluate() const +{ + Q_ASSERT(m_valid); + qDebug() << "evaluate meta word" << m_word; + switch (m_type) { + case MetaWordType::Simple: + return m_definition; + + case MetaWordType::FunctionBased: + case MetaWordType::Dynamic: + return m_function(this); + + case MetaWordType::Compound: + { + QHash cache; + return evaluateTokens(m_tokens, cache); + } + + default: + return ""; + } +} + +MetaWordType VMetaWord::getType() const +{ + return m_type; +} + +const QString &VMetaWord::getWord() const +{ + return m_word; +} + +const QString &VMetaWord::getDefinition() const +{ + return m_definition; +} + +const VMetaWordManager *VMetaWord::getManager() const +{ + return m_manager; +} + + +QString VMetaWord::toString() const +{ + QChar typeChar('U'); + switch (m_type) { + case MetaWordType::Simple: + typeChar = 'S'; + break; + + case MetaWordType::FunctionBased: + typeChar = 'F'; + break; + + case MetaWordType::Dynamic: + typeChar = 'D'; + break; + + case MetaWordType::Compound: + typeChar = 'C'; + break; + + default: + break; + } + + return QString("%1%2%1[%3]: %4").arg(VMetaWordManager::c_delimiter) + .arg(m_word) + .arg(typeChar) + .arg(m_definition); +} + +QVector VMetaWord::parseToTokens(const QString &p_text) +{ + QVector tokens; + + TokenType type = TokenType::Raw; + QString value; + value.reserve(p_text.size()); + for (int idx = 0; idx < p_text.size(); ++idx) { + const QChar &ch = p_text[idx]; + if (ch == VMetaWordManager::c_delimiter) { + // Check if it is single or double. + int next = idx + 1; + if (next == p_text.size() + || p_text[next] != VMetaWordManager::c_delimiter) { + // Single delimiter. + if (type == TokenType::Raw) { + // End of a raw token, begin of a MetaWord token. + if (!value.isEmpty()) { + tokens.push_back(Token(type, value)); + } + + type = TokenType::MetaWord; + } else { + // End of a MetaWord token, begin of a Raw token. + Q_ASSERT(!value.isEmpty()); + + tokens.push_back(Token(type, value)); + type = TokenType::Raw; + } + + value.clear(); + } else { + // Double delimiter. + // If now is parsing a MetaWord token, treat the first delimiter + // as the end of a token. + // Otherwise, store one single delimiter in value and skip next char. + if (type == TokenType::MetaWord) { + Q_ASSERT(!value.isEmpty()); + tokens.push_back(Token(type, value)); + type = TokenType::Raw; + value.clear(); + } else { + value.push_back(ch); + ++idx; + } + } + } else { + // Push ch in value. + value.push_back(ch); + } + } + + if (!value.isEmpty()) { + if (type == TokenType::Raw) { + tokens.push_back(Token(type, value)); + } else { + // An imcomplete metaword token. + // Treat it as raw. + tokens.push_back(Token(TokenType::Raw, "%" + value)); + } + + value.clear(); + } + + return tokens; +} + +QString VMetaWord::evaluateTokens(const QVector &p_tokens, + QHash &p_cache) const +{ + QString val; + + for (auto const & to : p_tokens) { + switch (to.m_type) { + case TokenType::Raw: + val += to.m_value; + break; + + case TokenType::MetaWord: + { + const VMetaWord *metaWord = m_manager->findMetaWord(to.m_value); + if (!metaWord) { + // Invalid meta word. Treat it as literal value. + val += VMetaWordManager::c_delimiter + to.m_value + VMetaWordManager::c_delimiter; + break; + } + + QString wordVal; + switch (metaWord->getType()) { + case MetaWordType::FunctionBased: + { + auto it = p_cache.find(metaWord->getWord()); + if (it != p_cache.end()) { + // Find it in the cache. + wordVal = it.value(); + } else { + // First evaluate this meta word. + wordVal = metaWord->evaluate(); + p_cache.insert(metaWord->getWord(), wordVal); + } + + break; + } + + case MetaWordType::Compound: + wordVal = evaluateTokens(metaWord->m_tokens, p_cache); + break; + + default: + wordVal = metaWord->evaluate(); + break; + } + + val += wordVal; + break; + } + + default: + Q_ASSERT(false); + break; + } + } + + return val; +} diff --git a/src/utils/vmetawordmanager.h b/src/utils/vmetawordmanager.h new file mode 100644 index 00000000..abfb642a --- /dev/null +++ b/src/utils/vmetawordmanager.h @@ -0,0 +1,202 @@ +#ifndef VMETAWORDMANAGER_H +#define VMETAWORDMANAGER_H + +#include + +#include +#include +#include +#include +#include + + +enum class MetaWordType +{ + // Definition is plain text. + Simple = 0, + + // Definition is a function call to get the value. + FunctionBased, + + // Like FunctionBased, but should re-evaluate the value for each occurence. + Dynamic, + + // Consists of other meta words. + Compound, + + Invalid +}; + +// We call meta word "magic word" in user interaction. +struct VMagicWord +{ + QString m_name; + QString m_definition; +}; + +class VMetaWordManager; +class VMetaWord; + +typedef std::function MetaWordFunc; + +// A Meta Word is surrounded by %. +// - Use %% for an escaped %; +// - Built-in or user-defined; +// - A meta word could contain other meta words as definition. +class VMetaWord +{ +public: + VMetaWord(const VMetaWordManager *p_manager, + MetaWordType p_type, + const QString &p_word, + const QString &p_definition, + MetaWordFunc p_function = nullptr); + + bool isValid() const; + + QString evaluate() const; + + MetaWordType getType() const; + + const QString &getWord() const; + + const QString &getDefinition() const; + + const VMetaWordManager *getManager() const; + + QString toString() const; + + enum class TokenType + { + Raw = 0, + MetaWord + }; + + struct Token + { + Token() + : m_type(TokenType::Raw) + { + } + + Token(VMetaWord::TokenType p_type, const QString &p_value) + : m_type(p_type), + m_value(p_value) + { + } + + QString toString() const + { + return QString("token %1[%2]").arg(m_type == TokenType::Raw + ? "Raw" : "MetaWord") + .arg(m_value); + } + + bool isRaw() const + { + return m_type == TokenType::Raw; + } + + bool isMetaWord() const + { + return m_type == TokenType::MetaWord; + } + + TokenType m_type; + + // For Raw type, m_value is the raw string of this token; + // For MetaWord type, m_value is the word of the meta word pointed to by + // this token. + QString m_value; + }; + +private: + // Check if m_type is @p_type. + bool checkType(MetaWordType p_type); + + // Parse children word from definition. + // Children word MUST be defined in m_manager already. + void checkAndParseDefinition(); + + // Parse @p_text to a list of tokens. + static QVector parseToTokens(const QString &p_text); + + // Used for Compound meta word with cache @p_cache. + // @p_cache: value cache for FunctionBased Token. + // For a meta word occuring more than once, we should evaluate it once for + // FunctionBased meta word. + QString evaluateTokens(const QVector &p_tokens, + QHash &p_cache) const; + + const VMetaWordManager *m_manager; + + MetaWordType m_type; + + // Word could contains spaces but no %. + QString m_word; + + // For Simple/Compound meta word, this contains the definition; + // For FunctionBased/Dynamic meta word, this makes no sense and is used + // for description. + QString m_definition; + + // For FunctionBased and Dynamic meta word. + MetaWordFunc m_function; + + bool m_valid; + + // Tokens used for Compound meta word. + QVector m_tokens; +}; + + +// Manager of meta word. +class VMetaWordManager : public QObject +{ + Q_OBJECT +public: + explicit VMetaWordManager(QObject *p_parent = nullptr); + + void init(); + + // Expand meta words in @p_text and return the expanded text. + QString evaluate(const QString &p_text) const; + + const VMetaWord *findMetaWord(const QString &p_word) const; + + bool contains(const QString &p_word) const; + + const QDateTime &getDateTime() const; + + const QHash &getAllMetaWords() const; + + // % by default. + static const QChar c_delimiter; + +private: + void addMetaWord(MetaWordType p_type, + const QString &p_word, + const QString &p_definition, + MetaWordFunc p_function = nullptr); + + void initCustomMetaWords(); + + // Map using word as key. + QHash m_metaWords; + + // Used for data/time related evaluate. + // Will be updated before each evaluation. + QDateTime m_dateTime; +}; + +inline const QDateTime &VMetaWordManager::getDateTime() const +{ + return m_dateTime; +} + +inline const QHash &VMetaWordManager::getAllMetaWords() const +{ + return m_metaWords; +} + +#endif // VMETAWORDMANAGER_H diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index 047bff19..95f11136 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -33,6 +33,8 @@ QVector> VUtils::s_availableLanguages; const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]\\(([^\\)\"]+)\\s*(\"(\\\\.|[^\"\\)])*\")?\\s*\\)"); +const QString VUtils::c_imageTitleRegExp = QString("[\\w\\(\\)@#%\\*\\-\\+=\\?<>\\,\\.\\s]+"); + const QString VUtils::c_fileNameRegExp = QString("[^\\\\/:\\*\\?\"<>\\|]*"); const QString VUtils::c_fencedCodeBlockStartRegExp = QString("^(\\s*)```([^`\\s]*)\\s*[^`]*$"); @@ -725,6 +727,16 @@ bool VUtils::checkPathLegal(const QString &p_path) return ret; } +bool VUtils::checkFileNameLegal(const QString &p_name) +{ + if (p_name.isEmpty()) { + return false; + } + + QRegExp exp(c_fileNameRegExp); + return exp.exactMatch(p_name); +} + bool VUtils::equalPath(const QString &p_patha, const QString &p_pathb) { QString a = QDir::cleanPath(p_patha); diff --git a/src/utils/vutils.h b/src/utils/vutils.h index de748473..e21a580f 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -177,6 +177,9 @@ public: // Try to check if @p_path is legal. static bool checkPathLegal(const QString &p_path); + // Check if file/folder name is legal. + static bool checkFileNameLegal(const QString &p_name); + // Returns true if @p_patha and @p_pathb points to the same file/directory. static bool equalPath(const QString &p_patha, const QString &p_pathb); @@ -248,6 +251,9 @@ public: // 4. Unused; static const QString c_imageLinkRegExp; + // Regular expression for image title. + static const QString c_imageTitleRegExp; + // Regular expression for file/directory name. // Forbidden char: \/:*?"<>| static const QString c_fileNameRegExp; diff --git a/src/vattachmentlist.cpp b/src/vattachmentlist.cpp index 240fa899..8c44044b 100644 --- a/src/vattachmentlist.cpp +++ b/src/vattachmentlist.cpp @@ -5,13 +5,12 @@ #include "vconfigmanager.h" #include "utils/vutils.h" #include "vbuttonwithwidget.h" -#include "vnote.h" #include "vmainwindow.h" #include "dialog/vconfirmdeletiondialog.h" #include "dialog/vsortdialog.h" extern VConfigManager *g_config; -extern VNote *g_vnote; +extern VMainWindow *g_mainWin; VAttachmentList::VAttachmentList(QWidget *p_parent) : QWidget(p_parent), m_file(NULL) @@ -53,7 +52,7 @@ void VAttachmentList::setupUI() .arg(m_file->fetchAttachmentFolderPath()), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok, - g_vnote->getMainWindow(), + g_mainWin, MessageBoxType::Danger); if (ret == QMessageBox::Ok) { if (!m_file->deleteAttachments()) { @@ -66,7 +65,7 @@ void VAttachmentList::setupUI() "maintain the configuration file manually."), QMessageBox::Ok, QMessageBox::Ok, - g_vnote->getMainWindow()); + g_mainWin); } m_attachmentList->clear(); @@ -204,7 +203,7 @@ void VAttachmentList::addAttachment() } static QString lastPath = QDir::homePath(); - QStringList files = QFileDialog::getOpenFileNames(g_vnote->getMainWindow(), + QStringList files = QFileDialog::getOpenFileNames(g_mainWin, tr("Select Files As Attachments"), lastPath); if (files.isEmpty()) { @@ -236,16 +235,16 @@ void VAttachmentList::addAttachments(const QStringList &p_files) "", QMessageBox::Ok, QMessageBox::Ok, - g_vnote->getMainWindow()); + g_mainWin); } else { ++addedFiles; } } if (addedFiles > 0) { - g_vnote->getMainWindow()->showStatusMessage(tr("%1 %2 added as attachments") - .arg(addedFiles) - .arg(addedFiles > 1 ? tr("files") : tr("file"))); + g_mainWin->showStatusMessage(tr("%1 %2 added as attachments") + .arg(addedFiles) + .arg(addedFiles > 1 ? tr("files") : tr("file"))); } } @@ -330,7 +329,7 @@ void VAttachmentList::deleteSelectedItems() false, false, false, - g_vnote->getMainWindow()); + g_mainWin); if (dialog.exec()) { items = dialog.getConfirmedItems(); @@ -349,7 +348,7 @@ void VAttachmentList::deleteSelectedItems() "maintain the configuration file manually."), QMessageBox::Ok, QMessageBox::Ok, - g_vnote->getMainWindow()); + g_mainWin); } updateButtonState(); @@ -370,7 +369,7 @@ void VAttachmentList::sortItems() "in the configuration file.") .arg(g_config->c_dataTextStyle) .arg(m_file->getName()), - g_vnote->getMainWindow()); + g_mainWin); QTreeWidget *tree = dialog.getTreeWidget(); tree->clear(); tree->setColumnCount(1); @@ -624,7 +623,7 @@ void VAttachmentList::checkAttachments() false, false, false, - g_vnote->getMainWindow()); + g_mainWin); if (dialog.exec()) { items = dialog.getConfirmedItems(); @@ -643,7 +642,7 @@ void VAttachmentList::checkAttachments() "maintain the configuration file manually."), QMessageBox::Ok, QMessageBox::Ok, - g_vnote->getMainWindow()); + g_mainWin); } updateButtonState(); diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index 256e6f2c..66b39882 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -1152,3 +1152,21 @@ void VConfigManager::setLastOpenedFiles(const QVector &p_files << "items in [last_opened_files] section"; } + +QVector VConfigManager::getCustomMagicWords() +{ + QVector words; + int size = userSettings->beginReadArray("magic_words"); + for (int i = 0; i < size; ++i) { + userSettings->setArrayIndex(i); + + VMagicWord word; + word.m_name = userSettings->value("name").toString(); + word.m_definition = userSettings->value("definition").toString(); + words.push_back(word); + } + + userSettings->endArray(); + + return words; +} diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index 1d14d43f..7bef0fa7 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -12,6 +12,7 @@ #include "vmarkdownconverter.h" #include "vconstants.h" #include "vfilesessioninfo.h" +#include "utils/vmetawordmanager.h" class QJsonObject; @@ -326,6 +327,9 @@ public: // Write last opened files to [last_opened_files] of session.ini. void setLastOpenedFiles(const QVector &p_files); + // Read custom magic words from [magic_words] section. + QVector getCustomMagicWords(); + // Return the configured key sequence of @p_operation. // Return empty if there is no corresponding config. QString getShortcutKeySequence(const QString &p_operation) const; diff --git a/src/veditarea.cpp b/src/veditarea.cpp index 87ee6255..6100ec14 100644 --- a/src/veditarea.cpp +++ b/src/veditarea.cpp @@ -12,9 +12,10 @@ extern VConfigManager *g_config; extern VNote *g_vnote; -VEditArea::VEditArea(VNote *vnote, QWidget *parent) - : QWidget(parent), VNavigationMode(), - vnote(vnote), curWindowIndex(-1) +VEditArea::VEditArea(QWidget *parent) + : QWidget(parent), + VNavigationMode(), + curWindowIndex(-1) { setupUI(); @@ -66,7 +67,7 @@ void VEditArea::setupUI() void VEditArea::insertSplitWindow(int idx) { - VEditWindow *win = new VEditWindow(vnote, this); + VEditWindow *win = new VEditWindow(this); splitter->insertWidget(idx, win); connect(win, &VEditWindow::tabStatusUpdated, this, &VEditArea::handleWindowTabStatusUpdated); diff --git a/src/veditarea.h b/src/veditarea.h index dbeb9d8f..6f3f95fa 100644 --- a/src/veditarea.h +++ b/src/veditarea.h @@ -14,7 +14,6 @@ #include "veditwindow.h" #include "vnavigationmode.h" -class VNote; class VFile; class VDirectory; class VFindReplaceDialog; @@ -25,7 +24,7 @@ class VEditArea : public QWidget, public VNavigationMode { Q_OBJECT public: - explicit VEditArea(VNote *vnote, QWidget *parent = 0); + explicit VEditArea(QWidget *parent = 0); // Whether @p_file has been opened in edit area. bool isFileOpened(const VFile *p_file); @@ -161,7 +160,6 @@ private: // Update status of current window. void updateWindowStatus(); - VNote *vnote; int curWindowIndex; // Splitter holding multiple split windows diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp index 519c589f..3cd0a281 100644 --- a/src/veditwindow.cpp +++ b/src/veditwindow.cpp @@ -2,7 +2,6 @@ #include #include "veditwindow.h" #include "vedittab.h" -#include "vnote.h" #include "utils/vutils.h" #include "vorphanfile.h" #include "vmainwindow.h" @@ -13,12 +12,14 @@ #include "vfilelist.h" #include "vconfigmanager.h" -extern VNote *g_vnote; extern VConfigManager *g_config; +extern VMainWindow *g_mainWin; -VEditWindow::VEditWindow(VNote *vnote, VEditArea *editArea, QWidget *parent) - : QTabWidget(parent), vnote(vnote), m_editArea(editArea), - m_curTabWidget(NULL), m_lastTabWidget(NULL) +VEditWindow::VEditWindow(VEditArea *editArea, QWidget *parent) + : QTabWidget(parent), + m_editArea(editArea), + m_curTabWidget(NULL), + m_lastTabWidget(NULL) { setAcceptDrops(true); initTabActions(); @@ -137,9 +138,9 @@ void VEditWindow::initTabActions() Q_ASSERT(file); if (file->getType() == FileType::Note) { VNoteFile *tmpFile = dynamic_cast((VFile *)file); - g_vnote->getMainWindow()->getFileList()->fileInfo(tmpFile); + g_mainWin->getFileList()->fileInfo(tmpFile); } else if (file->getType() == FileType::Orphan) { - g_vnote->getMainWindow()->editOrphanFileInfo(file); + g_mainWin->editOrphanFileInfo(file); } }); @@ -817,7 +818,7 @@ void VEditWindow::handleLocateAct() VEditTab *editor = getTab(tab); QPointer file = editor->getFile(); if (file->getType() == FileType::Note) { - vnote->getMainWindow()->locateFile(file); + g_mainWin->locateFile(file); } } @@ -1022,7 +1023,7 @@ void VEditWindow::dropEvent(QDropEvent *p_event) if (!files.isEmpty()) { focusWindow(); - g_vnote->getMainWindow()->openFiles(files); + g_mainWin->openFiles(files); } p_event->acceptProposedAction(); diff --git a/src/veditwindow.h b/src/veditwindow.h index e0cdd8a8..d7f35ae7 100644 --- a/src/veditwindow.h +++ b/src/veditwindow.h @@ -11,7 +11,6 @@ #include "vconstants.h" #include "vnotefile.h" -class VNote; class QPushButton; class QActionGroup; class VEditArea; @@ -20,7 +19,7 @@ class VEditWindow : public QTabWidget { Q_OBJECT public: - explicit VEditWindow(VNote *vnote, VEditArea *editArea, QWidget *parent = 0); + explicit VEditWindow(VEditArea *editArea, QWidget *parent = 0); int findTabByFile(const VFile *p_file) const; int openFile(VFile *p_file, OpenFileMode p_mode); bool closeFile(const VFile *p_file, bool p_forced); @@ -160,7 +159,6 @@ private: // Connect the signals of VEditTab to this VEditWindow. void connectEditTab(const VEditTab *p_tab); - VNote *vnote; VEditArea *m_editArea; // These two members are only used for alternateTab(). diff --git a/src/vlineedit.cpp b/src/vlineedit.cpp new file mode 100644 index 00000000..7824196c --- /dev/null +++ b/src/vlineedit.cpp @@ -0,0 +1,46 @@ +#include "vlineedit.h" + +#include +#include + +#include "utils/vmetawordmanager.h" + + +extern VMetaWordManager *g_mwMgr; + +VLineEdit::VLineEdit(QWidget *p_parent) + : QLineEdit(p_parent) +{ + init(); +} + +VLineEdit::VLineEdit(const QString &p_contents, QWidget *p_parent) + : QLineEdit(p_contents, p_parent) +{ + init(); +} + +void VLineEdit::handleTextChanged(const QString &p_text) +{ + m_evaluatedText = g_mwMgr->evaluate(p_text); + qDebug() << "evaluate text:" << m_evaluatedText; + + if (m_evaluatedText == p_text) { + return; + } + + // Display tooltip at bottom-left. + QPoint pos = mapToGlobal(QPoint(0, height())); + QToolTip::showText(pos, m_evaluatedText, this); +} + +void VLineEdit::init() +{ + connect(this, &QLineEdit::textChanged, + this, &VLineEdit::handleTextChanged); +} + +const QString VLineEdit::getEvaluatedText() const +{ + return m_evaluatedText; +} diff --git a/src/vlineedit.h b/src/vlineedit.h new file mode 100644 index 00000000..81a8a86c --- /dev/null +++ b/src/vlineedit.h @@ -0,0 +1,29 @@ +#ifndef VLINEEDIT_H +#define VLINEEDIT_H + +#include + + +// QLineEdit with meta word support. +class VLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit VLineEdit(QWidget *p_parent = nullptr); + + VLineEdit(const QString &p_contents, QWidget *p_parent = Q_NULLPTR); + + // Return the evaluated text. + const QString getEvaluatedText() const; + +private slots: + void handleTextChanged(const QString &p_text); + +private: + void init(); + + // We should keep the evaluated text identical with what's displayed. + QString m_evaluatedText; +}; + +#endif // VLINEEDIT_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 022bea55..f5591724 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -45,6 +45,8 @@ VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent) : QMainWindow(p_parent), m_guard(p_guard), m_windowOldState(Qt::WindowNoState), m_requestQuit(false) { + qsrand(QDateTime::currentDateTime().toTime_t()); + setWindowIcon(QIcon(":/resources/icons/vnote.ico")); vnote = new VNote(this); g_vnote = vnote; @@ -110,7 +112,7 @@ void VMainWindow::setupUI() m_fileList = new VFileList(); m_fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); - editArea = new VEditArea(vnote); + editArea = new VEditArea(); editArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_findReplaceDialog = editArea->getFindReplaceDialog(); m_fileList->setEditArea(editArea); diff --git a/src/vnote.cpp b/src/vnote.cpp index 720d5725..48d2e6d9 100644 --- a/src/vnote.cpp +++ b/src/vnote.cpp @@ -16,6 +16,10 @@ extern VConfigManager *g_config; +// Meta word manager. +VMetaWordManager *g_mwMgr; + + QString VNote::s_markdownTemplate; QString VNote::s_markdownTemplatePDF; @@ -53,10 +57,15 @@ const QString VNote::c_markdownGuideDocFile_en = ":/resources/docs/markdown_guid const QString VNote::c_markdownGuideDocFile_zh = ":/resources/docs/markdown_guide_zh.md"; VNote::VNote(QObject *parent) - : QObject(parent), m_mainWindow(dynamic_cast(parent)) + : QObject(parent) { initTemplate(); + g_config->getNotebooks(m_notebooks, this); + + m_metaWordMgr.init(); + + g_mwMgr = &m_metaWordMgr; } void VNote::initPalette(QPalette palette) diff --git a/src/vnote.h b/src/vnote.h index 81a9abc9..11f4ebd6 100644 --- a/src/vnote.h +++ b/src/vnote.h @@ -12,11 +12,12 @@ #include #include "vnotebook.h" #include "vconstants.h" +#include "utils/vmetawordmanager.h" -class VMainWindow; class VOrphanFile; class VNoteFile; + class VNote : public QObject { Q_OBJECT @@ -74,7 +75,6 @@ public: const QVector > &getPalette() const; void initPalette(QPalette palette); QString getColorFromPalette(const QString &p_name) const; - VMainWindow *getMainWindow() const; QString getNavigationLabelStyle(const QString &p_str) const; @@ -106,7 +106,8 @@ private: // Maintain all the notebooks. Other holder should use QPointer. QVector m_notebooks; QVector > m_palette; - VMainWindow *m_mainWindow; + + VMetaWordManager m_metaWordMgr; // Hold all external file: Orphan File. // Need to clean up periodly. @@ -118,9 +119,4 @@ inline const QVector >& VNote::getPalette() const return m_palette; } -inline VMainWindow *VNote::getMainWindow() const -{ - return m_mainWindow; -} - #endif // VNOTE_H diff --git a/src/vpreviewpage.cpp b/src/vpreviewpage.cpp index 524ae109..d87859d7 100644 --- a/src/vpreviewpage.cpp +++ b/src/vpreviewpage.cpp @@ -2,10 +2,9 @@ #include -#include "vnote.h" #include "vmainwindow.h" -extern VNote *g_vnote; +extern VMainWindow *g_mainWin; VPreviewPage::VPreviewPage(QWidget *parent) : QWebEnginePage(parent) { @@ -21,7 +20,7 @@ bool VPreviewPage::acceptNavigationRequest(const QUrl &p_url, if (p_url.isLocalFile()) { QString filePath = p_url.toLocalFile(); - if (g_vnote->getMainWindow()->tryOpenInternalFile(filePath)) { + if (g_mainWin->tryOpenInternalFile(filePath)) { qDebug() << "internal notes jump" << filePath; return false; }