mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
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;
This commit is contained in:
parent
f1f6980921
commit
787c61a5af
@ -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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: 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();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <QDialog>
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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("<span style=\"%1\">WARNING</span>: Name (case-insensitive) already exists. "
|
||||
"Please choose another name.")
|
||||
.arg(g_config->c_warningTextStyle);
|
||||
m_warnLabel->setText(nameConflictText);
|
||||
warnText = tr("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: "
|
||||
"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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: 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();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <QDialog>
|
||||
|
||||
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;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <QRegExp>
|
||||
#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
|
||||
|
@ -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;
|
||||
|
@ -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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: 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();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <QDialog>
|
||||
|
||||
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;
|
||||
|
@ -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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: 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
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <QDialog>
|
||||
|
||||
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;
|
||||
|
@ -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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: 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.
|
||||
|
@ -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;
|
||||
|
@ -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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name (case-insensitive) <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: "
|
||||
"Name <span style=\"%2\">%3</span> 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("<span style=\"%1\">WARNING</span>: 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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
593
src/utils/vmetawordmanager.cpp
Normal file
593
src/utils/vmetawordmanager.cpp
Normal file
@ -0,0 +1,593 @@
|
||||
#include "vmetawordmanager.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QWidget>
|
||||
#include <QApplication>
|
||||
#include <QToolTip>
|
||||
|
||||
#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<QString> 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<VMagicWord> 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<VMetaWordManager *>(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<QString, QString> 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::Token> VMetaWord::parseToTokens(const QString &p_text)
|
||||
{
|
||||
QVector<Token> 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<VMetaWord::Token> &p_tokens,
|
||||
QHash<QString, QString> &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;
|
||||
}
|
202
src/utils/vmetawordmanager.h
Normal file
202
src/utils/vmetawordmanager.h
Normal file
@ -0,0 +1,202 @@
|
||||
#ifndef VMETAWORDMANAGER_H
|
||||
#define VMETAWORDMANAGER_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QHash>
|
||||
#include <QDateTime>
|
||||
|
||||
|
||||
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<QString(const VMetaWord *)> 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<VMetaWord::Token> 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<VMetaWord::Token> &p_tokens,
|
||||
QHash<QString, QString> &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<Token> 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<QString, VMetaWord> &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<QString, VMetaWord> 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<QString, VMetaWord> &VMetaWordManager::getAllMetaWords() const
|
||||
{
|
||||
return m_metaWords;
|
||||
}
|
||||
|
||||
#endif // VMETAWORDMANAGER_H
|
@ -33,6 +33,8 @@ QVector<QPair<QString, QString>> 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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -1152,3 +1152,21 @@ void VConfigManager::setLastOpenedFiles(const QVector<VFileSessionInfo> &p_files
|
||||
<< "items in [last_opened_files] section";
|
||||
|
||||
}
|
||||
|
||||
QVector<VMagicWord> VConfigManager::getCustomMagicWords()
|
||||
{
|
||||
QVector<VMagicWord> 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;
|
||||
}
|
||||
|
@ -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<VFileSessionInfo> &p_files);
|
||||
|
||||
// Read custom magic words from [magic_words] section.
|
||||
QVector<VMagicWord> getCustomMagicWords();
|
||||
|
||||
// Return the configured key sequence of @p_operation.
|
||||
// Return empty if there is no corresponding config.
|
||||
QString getShortcutKeySequence(const QString &p_operation) const;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include <QtDebug>
|
||||
#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<VNoteFile *>((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<VFile> 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();
|
||||
|
@ -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().
|
||||
|
46
src/vlineedit.cpp
Normal file
46
src/vlineedit.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "vlineedit.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QToolTip>
|
||||
|
||||
#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;
|
||||
}
|
29
src/vlineedit.h
Normal file
29
src/vlineedit.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef VLINEEDIT_H
|
||||
#define VLINEEDIT_H
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
|
||||
// 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
|
@ -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);
|
||||
|
@ -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<VMainWindow *>(parent))
|
||||
: QObject(parent)
|
||||
{
|
||||
initTemplate();
|
||||
|
||||
g_config->getNotebooks(m_notebooks, this);
|
||||
|
||||
m_metaWordMgr.init();
|
||||
|
||||
g_mwMgr = &m_metaWordMgr;
|
||||
}
|
||||
|
||||
void VNote::initPalette(QPalette palette)
|
||||
|
12
src/vnote.h
12
src/vnote.h
@ -12,11 +12,12 @@
|
||||
#include <QPalette>
|
||||
#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<QPair<QString, QString> > &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<VNotebook *> m_notebooks;
|
||||
QVector<QPair<QString, QString> > 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<QPair<QString, QString> >& VNote::getPalette() const
|
||||
return m_palette;
|
||||
}
|
||||
|
||||
inline VMainWindow *VNote::getMainWindow() const
|
||||
{
|
||||
return m_mainWindow;
|
||||
}
|
||||
|
||||
#endif // VNOTE_H
|
||||
|
@ -2,10 +2,9 @@
|
||||
|
||||
#include <QDesktopServices>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user