support attachments
Support attachments to internal note file. - Add/Delete/Clear/Sort; - Support custom attachment folder for each notebook (read-only); - Support renaming attachment;
@ -56,8 +56,14 @@ private:
|
|||||||
VConfirmDeletionDialog::VConfirmDeletionDialog(const QString &p_title,
|
VConfirmDeletionDialog::VConfirmDeletionDialog(const QString &p_title,
|
||||||
const QString &p_info,
|
const QString &p_info,
|
||||||
const QVector<QString> &p_files,
|
const QVector<QString> &p_files,
|
||||||
|
bool p_enableAskAgain,
|
||||||
|
bool p_askAgainEnabled,
|
||||||
|
bool p_enablePreview,
|
||||||
QWidget *p_parent)
|
QWidget *p_parent)
|
||||||
: QDialog(p_parent)
|
: QDialog(p_parent),
|
||||||
|
m_enableAskAgain(p_enableAskAgain),
|
||||||
|
m_askAgainEnabled(p_askAgainEnabled),
|
||||||
|
m_enablePreview(p_enablePreview)
|
||||||
{
|
{
|
||||||
setupUI(p_title, p_info);
|
setupUI(p_title, p_info);
|
||||||
|
|
||||||
@ -66,33 +72,44 @@ VConfirmDeletionDialog::VConfirmDeletionDialog(const QString &p_title,
|
|||||||
|
|
||||||
void VConfirmDeletionDialog::setupUI(const QString &p_title, const QString &p_info)
|
void VConfirmDeletionDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||||
{
|
{
|
||||||
QLabel *infoLabel = new QLabel(p_info);
|
QLabel *infoLabel = NULL;
|
||||||
|
if (!p_info.isEmpty()) {
|
||||||
|
infoLabel = new QLabel(p_info);
|
||||||
|
infoLabel->setWordWrap(true);
|
||||||
|
}
|
||||||
|
|
||||||
m_listWidget = new QListWidget();
|
m_listWidget = new QListWidget();
|
||||||
connect(m_listWidget, &QListWidget::currentRowChanged,
|
connect(m_listWidget, &QListWidget::currentRowChanged,
|
||||||
this, &VConfirmDeletionDialog::currentFileChanged);
|
this, &VConfirmDeletionDialog::currentFileChanged);
|
||||||
|
|
||||||
m_previewer = new QLabel();
|
m_previewer = new QLabel();
|
||||||
|
|
||||||
m_askAgainCB = new QCheckBox(tr("Just delete them and do not ask for confirmation again"));
|
m_askAgainCB = new QCheckBox(tr("Do not ask for confirmation again"));
|
||||||
m_askAgainCB->setChecked(!g_config->getConfirmImagesCleanUp());
|
m_askAgainCB->setChecked(!m_askAgainEnabled);
|
||||||
|
m_askAgainCB->setVisible(m_enableAskAgain);
|
||||||
|
|
||||||
// Ok is the default button.
|
// Ok is the default button.
|
||||||
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
connect(m_btnBox, &QDialogButtonBox::accepted,
|
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
this, [this]() {
|
|
||||||
g_config->setConfirmImagesCleanUp(!m_askAgainCB->isChecked());
|
|
||||||
QDialog::accept();
|
|
||||||
});
|
|
||||||
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
m_btnBox->button(QDialogButtonBox::Ok)->setStyleSheet(g_config->c_dangerBtnStyle);
|
||||||
|
|
||||||
QHBoxLayout *midLayout = new QHBoxLayout;
|
QHBoxLayout *midLayout = new QHBoxLayout;
|
||||||
midLayout->addWidget(m_listWidget);
|
midLayout->addWidget(m_listWidget);
|
||||||
midLayout->addStretch();
|
if (m_enablePreview) {
|
||||||
midLayout->addWidget(m_previewer);
|
midLayout->addStretch();
|
||||||
midLayout->addStretch();
|
midLayout->addWidget(m_previewer);
|
||||||
|
midLayout->addStretch();
|
||||||
|
} else {
|
||||||
|
midLayout->addWidget(m_previewer);
|
||||||
|
m_previewer->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
mainLayout->addWidget(infoLabel);
|
if (infoLabel) {
|
||||||
|
mainLayout->addWidget(infoLabel);
|
||||||
|
}
|
||||||
|
|
||||||
mainLayout->addWidget(m_askAgainCB);
|
mainLayout->addWidget(m_askAgainCB);
|
||||||
mainLayout->addWidget(m_btnBox);
|
mainLayout->addWidget(m_btnBox);
|
||||||
mainLayout->addLayout(midLayout);
|
mainLayout->addLayout(midLayout);
|
||||||
@ -137,10 +154,15 @@ void VConfirmDeletionDialog::initFileItems(const QVector<QString> &p_files)
|
|||||||
m_listWidget->setCurrentRow(0);
|
m_listWidget->setCurrentRow(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VConfirmDeletionDialog::getAskAgainEnabled() const
|
||||||
|
{
|
||||||
|
return !m_askAgainCB->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
void VConfirmDeletionDialog::currentFileChanged(int p_row)
|
void VConfirmDeletionDialog::currentFileChanged(int p_row)
|
||||||
{
|
{
|
||||||
bool succeed = false;
|
bool succeed = false;
|
||||||
if (p_row > -1) {
|
if (p_row > -1 && m_enablePreview) {
|
||||||
ConfirmItemWidget *widget = getItemWidget(m_listWidget->item(p_row));
|
ConfirmItemWidget *widget = getItemWidget(m_listWidget->item(p_row));
|
||||||
if (widget) {
|
if (widget) {
|
||||||
QPixmap image(widget->getFile());
|
QPixmap image(widget->getFile());
|
||||||
|
@ -19,10 +19,15 @@ public:
|
|||||||
VConfirmDeletionDialog(const QString &p_title,
|
VConfirmDeletionDialog(const QString &p_title,
|
||||||
const QString &p_info,
|
const QString &p_info,
|
||||||
const QVector<QString> &p_files,
|
const QVector<QString> &p_files,
|
||||||
|
bool p_enableAskAgain,
|
||||||
|
bool p_askAgainEnabled,
|
||||||
|
bool p_enablePreview,
|
||||||
QWidget *p_parent = 0);
|
QWidget *p_parent = 0);
|
||||||
|
|
||||||
QVector<QString> getConfirmedFiles() const;
|
QVector<QString> getConfirmedFiles() const;
|
||||||
|
|
||||||
|
bool getAskAgainEnabled() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void currentFileChanged(int p_row);
|
void currentFileChanged(int p_row);
|
||||||
|
|
||||||
@ -37,6 +42,12 @@ private:
|
|||||||
QLabel *m_previewer;
|
QLabel *m_previewer;
|
||||||
QDialogButtonBox *m_btnBox;
|
QDialogButtonBox *m_btnBox;
|
||||||
QCheckBox *m_askAgainCB;
|
QCheckBox *m_askAgainCB;
|
||||||
|
|
||||||
|
bool m_enableAskAgain;
|
||||||
|
// Init value if m_enableAskAgain is true.
|
||||||
|
bool m_askAgainEnabled;
|
||||||
|
|
||||||
|
bool m_enablePreview;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VCONFIRMDELETIONDIALOG_H
|
#endif // VCONFIRMDELETIONDIALOG_H
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "vdirinfodialog.h"
|
#include "vdirinfodialog.h"
|
||||||
#include "vdirectory.h"
|
#include "vdirectory.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
@ -31,8 +32,7 @@ void VDirInfoDialog::setupUI()
|
|||||||
nameEdit->selectAll();
|
nameEdit->selectAll();
|
||||||
|
|
||||||
// Created time.
|
// Created time.
|
||||||
QString createdTimeStr = m_directory->getCreatedTimeUtc().toLocalTime()
|
QString createdTimeStr = VUtils::displayDateTime(m_directory->getCreatedTimeUtc().toLocalTime());
|
||||||
.toString(Qt::DefaultLocaleLongDate);
|
|
||||||
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
||||||
|
|
||||||
QFormLayout *topLayout = new QFormLayout();
|
QFormLayout *topLayout = new QFormLayout();
|
||||||
|
@ -40,19 +40,24 @@ void VFileInfoDialog::setupUI(const QString &p_title, const QString &p_info)
|
|||||||
// Select without suffix.
|
// Select without suffix.
|
||||||
nameEdit->setSelection(baseStart, baseLength);
|
nameEdit->setSelection(baseStart, baseLength);
|
||||||
|
|
||||||
|
// Attachment folder.
|
||||||
|
QLineEdit *attachmentFolderEdit = new QLineEdit(m_file->getAttachmentFolder());
|
||||||
|
attachmentFolderEdit->setPlaceholderText(tr("Will be assigned when adding attachments"));
|
||||||
|
attachmentFolderEdit->setToolTip(tr("The folder to hold attachments of this note"));
|
||||||
|
attachmentFolderEdit->setReadOnly(true);
|
||||||
|
|
||||||
// Created time.
|
// Created time.
|
||||||
QString createdTimeStr = m_file->getCreatedTimeUtc().toLocalTime()
|
QString createdTimeStr = VUtils::displayDateTime(m_file->getCreatedTimeUtc().toLocalTime());
|
||||||
.toString(Qt::DefaultLocaleLongDate);
|
|
||||||
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
||||||
|
|
||||||
// Modified time.
|
// Modified time.
|
||||||
createdTimeStr = m_file->getModifiedTimeUtc().toLocalTime()
|
createdTimeStr = VUtils::displayDateTime(m_file->getModifiedTimeUtc().toLocalTime());
|
||||||
.toString(Qt::DefaultLocaleLongDate);
|
|
||||||
QLabel *modifiedTimeLabel = new QLabel(createdTimeStr);
|
QLabel *modifiedTimeLabel = new QLabel(createdTimeStr);
|
||||||
modifiedTimeLabel->setToolTip(tr("Last modified time within VNote"));
|
modifiedTimeLabel->setToolTip(tr("Last modified time within VNote"));
|
||||||
|
|
||||||
QFormLayout *topLayout = new QFormLayout();
|
QFormLayout *topLayout = new QFormLayout();
|
||||||
topLayout->addRow(tr("Note &name:"), nameEdit);
|
topLayout->addRow(tr("Note &name:"), nameEdit);
|
||||||
|
topLayout->addRow(tr("Attachment folder:"), attachmentFolderEdit);
|
||||||
topLayout->addRow(tr("Created time:"), createdTimeLabel);
|
topLayout->addRow(tr("Created time:"), createdTimeLabel);
|
||||||
topLayout->addRow(tr("Modified time:"), modifiedTimeLabel);
|
topLayout->addRow(tr("Modified time:"), modifiedTimeLabel);
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@ VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info
|
|||||||
const QVector<VNotebook *> &p_notebooks,
|
const QVector<VNotebook *> &p_notebooks,
|
||||||
QWidget *parent)
|
QWidget *parent)
|
||||||
: QDialog(parent),
|
: QDialog(parent),
|
||||||
title(title), info(info), defaultName(defaultName), defaultPath(defaultPath),
|
defaultName(defaultName), defaultPath(defaultPath),
|
||||||
m_importNotebook(false), m_manualPath(false), m_manualName(false),
|
m_importNotebook(false), m_manualPath(false), m_manualName(false),
|
||||||
m_notebooks(p_notebooks)
|
m_notebooks(p_notebooks)
|
||||||
{
|
{
|
||||||
setupUI();
|
setupUI(title, info);
|
||||||
|
|
||||||
connect(nameEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
|
connect(nameEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
|
||||||
connect(pathEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
|
connect(pathEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
|
||||||
@ -25,11 +25,11 @@ VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info
|
|||||||
handleInputChanged();
|
handleInputChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNewNotebookDialog::setupUI()
|
void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||||
{
|
{
|
||||||
QLabel *infoLabel = NULL;
|
QLabel *infoLabel = NULL;
|
||||||
if (!info.isEmpty()) {
|
if (!p_info.isEmpty()) {
|
||||||
infoLabel = new QLabel(info);
|
infoLabel = new QLabel(p_info);
|
||||||
infoLabel->setWordWrap(true);
|
infoLabel->setWordWrap(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,16 +44,26 @@ void VNewNotebookDialog::setupUI()
|
|||||||
|
|
||||||
QLabel *imageFolderLabel = new QLabel(tr("&Image folder:"));
|
QLabel *imageFolderLabel = new QLabel(tr("&Image folder:"));
|
||||||
m_imageFolderEdit = new QLineEdit();
|
m_imageFolderEdit = new QLineEdit();
|
||||||
|
imageFolderLabel->setBuddy(m_imageFolderEdit);
|
||||||
m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||||
.arg(g_config->getImageFolder()));
|
.arg(g_config->getImageFolder()));
|
||||||
imageFolderLabel->setBuddy(m_imageFolderEdit);
|
m_imageFolderEdit->setToolTip(tr("Set the name of the folder to hold images of all the notes in this notebook "
|
||||||
QString imageFolderTip = tr("Set the name of the folder for all the notes of this notebook to store images "
|
"(empty to use global configuration)"));
|
||||||
"(empty to use global configuration)");
|
imageFolderLabel->setToolTip(m_imageFolderEdit->toolTip());
|
||||||
m_imageFolderEdit->setToolTip(imageFolderTip);
|
|
||||||
imageFolderLabel->setToolTip(imageFolderTip);
|
|
||||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit);
|
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit);
|
||||||
m_imageFolderEdit->setValidator(validator);
|
m_imageFolderEdit->setValidator(validator);
|
||||||
|
|
||||||
|
QLabel *attachmentFolderLabel = new QLabel(tr("&Attachment folder:"));
|
||||||
|
m_attachmentFolderEdit = new QLineEdit();
|
||||||
|
attachmentFolderLabel->setBuddy(m_attachmentFolderEdit);
|
||||||
|
m_attachmentFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||||
|
.arg(g_config->getAttachmentFolder()));
|
||||||
|
m_attachmentFolderEdit->setToolTip(tr("Set the name of the folder to hold attachments of all the notes in this notebook "
|
||||||
|
"(empty to use global configuration, read-only once created)"));
|
||||||
|
attachmentFolderLabel->setToolTip(m_attachmentFolderEdit->toolTip());
|
||||||
|
validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_attachmentFolderEdit);
|
||||||
|
m_attachmentFolderEdit->setValidator(validator);
|
||||||
|
|
||||||
QGridLayout *topLayout = new QGridLayout();
|
QGridLayout *topLayout = new QGridLayout();
|
||||||
topLayout->addWidget(nameLabel, 0, 0);
|
topLayout->addWidget(nameLabel, 0, 0);
|
||||||
topLayout->addWidget(nameEdit, 0, 1, 1, 2);
|
topLayout->addWidget(nameEdit, 0, 1, 1, 2);
|
||||||
@ -62,6 +72,8 @@ void VNewNotebookDialog::setupUI()
|
|||||||
topLayout->addWidget(browseBtn, 1, 2);
|
topLayout->addWidget(browseBtn, 1, 2);
|
||||||
topLayout->addWidget(imageFolderLabel, 2, 0);
|
topLayout->addWidget(imageFolderLabel, 2, 0);
|
||||||
topLayout->addWidget(m_imageFolderEdit, 2, 1);
|
topLayout->addWidget(m_imageFolderEdit, 2, 1);
|
||||||
|
topLayout->addWidget(attachmentFolderLabel, 3, 0);
|
||||||
|
topLayout->addWidget(m_attachmentFolderEdit, 3, 1);
|
||||||
|
|
||||||
// Warning label.
|
// Warning label.
|
||||||
m_warnLabel = new QLabel();
|
m_warnLabel = new QLabel();
|
||||||
@ -87,7 +99,7 @@ void VNewNotebookDialog::setupUI()
|
|||||||
// Will set the parent of above widgets properly.
|
// Will set the parent of above widgets properly.
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||||
setWindowTitle(title);
|
setWindowTitle(p_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VNewNotebookDialog::getNameInput() const
|
QString VNewNotebookDialog::getNameInput() const
|
||||||
@ -111,6 +123,15 @@ QString VNewNotebookDialog::getImageFolder() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VNewNotebookDialog::getAttachmentFolder() const
|
||||||
|
{
|
||||||
|
if (m_attachmentFolderEdit->isEnabled()) {
|
||||||
|
return m_attachmentFolderEdit->text();
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VNewNotebookDialog::handleBrowseBtnClicked()
|
void VNewNotebookDialog::handleBrowseBtnClicked()
|
||||||
{
|
{
|
||||||
static QString defaultPath;
|
static QString defaultPath;
|
||||||
@ -251,6 +272,7 @@ void VNewNotebookDialog::handleInputChanged()
|
|||||||
m_warnLabel->setVisible(showWarnLabel);
|
m_warnLabel->setVisible(showWarnLabel);
|
||||||
m_importNotebook = configExist;
|
m_importNotebook = configExist;
|
||||||
m_imageFolderEdit->setEnabled(!m_importNotebook);
|
m_imageFolderEdit->setEnabled(!m_importNotebook);
|
||||||
|
m_attachmentFolderEdit->setEnabled(!m_importNotebook);
|
||||||
|
|
||||||
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
|
||||||
okBtn->setEnabled(nameOk && pathOk);
|
okBtn->setEnabled(nameOk && pathOk);
|
||||||
|
@ -29,6 +29,10 @@ public:
|
|||||||
// Empty string indicates using global config.
|
// Empty string indicates using global config.
|
||||||
QString getImageFolder() const;
|
QString getImageFolder() const;
|
||||||
|
|
||||||
|
// Get the custom attachment folder for this notebook.
|
||||||
|
// Empty string indicates using global config.
|
||||||
|
QString getAttachmentFolder() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleBrowseBtnClicked();
|
void handleBrowseBtnClicked();
|
||||||
|
|
||||||
@ -39,7 +43,7 @@ protected:
|
|||||||
void showEvent(QShowEvent *event) Q_DECL_OVERRIDE;
|
void showEvent(QShowEvent *event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI();
|
void setupUI(const QString &p_title, const QString &p_info);
|
||||||
|
|
||||||
// Should be called before enableOkButton() when path changed.
|
// Should be called before enableOkButton() when path changed.
|
||||||
void checkRootFolder(const QString &p_path);
|
void checkRootFolder(const QString &p_path);
|
||||||
@ -53,10 +57,9 @@ private:
|
|||||||
QPushButton *browseBtn;
|
QPushButton *browseBtn;
|
||||||
QLabel *m_warnLabel;
|
QLabel *m_warnLabel;
|
||||||
QLineEdit *m_imageFolderEdit;
|
QLineEdit *m_imageFolderEdit;
|
||||||
|
QLineEdit *m_attachmentFolderEdit;
|
||||||
QDialogButtonBox *m_btnBox;
|
QDialogButtonBox *m_btnBox;
|
||||||
|
|
||||||
QString title;
|
|
||||||
QString info;
|
|
||||||
QString defaultName;
|
QString defaultName;
|
||||||
QString defaultPath;
|
QString defaultPath;
|
||||||
|
|
||||||
|
@ -35,28 +35,37 @@ void VNotebookInfoDialog::setupUI(const QString &p_title, const QString &p_info)
|
|||||||
m_pathEdit = new QLineEdit(m_notebook->getPath());
|
m_pathEdit = new QLineEdit(m_notebook->getPath());
|
||||||
m_pathEdit->setReadOnly(true);
|
m_pathEdit->setReadOnly(true);
|
||||||
|
|
||||||
|
// Image folder.
|
||||||
m_imageFolderEdit = new QLineEdit(m_notebook->getImageFolderConfig());
|
m_imageFolderEdit = new QLineEdit(m_notebook->getImageFolderConfig());
|
||||||
m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||||
.arg(g_config->getImageFolder()));
|
.arg(g_config->getImageFolder()));
|
||||||
m_imageFolderEdit->setToolTip(tr("Set the name of the folder for all the notes of this notebook to store images "
|
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)"));
|
"(empty to use global configuration)"));
|
||||||
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit);
|
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit);
|
||||||
m_imageFolderEdit->setValidator(validator);
|
m_imageFolderEdit->setValidator(validator);
|
||||||
|
|
||||||
|
// Attachment folder.
|
||||||
|
Q_ASSERT(!m_notebook->getAttachmentFolder().isEmpty());
|
||||||
|
m_attachmentFolderEdit = new QLineEdit(m_notebook->getAttachmentFolder());
|
||||||
|
m_attachmentFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
|
||||||
|
.arg(g_config->getAttachmentFolder()));
|
||||||
|
m_attachmentFolderEdit->setToolTip(tr("The folder to hold attachments of all the notes in this notebook"));
|
||||||
|
m_attachmentFolderEdit->setReadOnly(true);
|
||||||
|
|
||||||
// Recycle bin folder.
|
// Recycle bin folder.
|
||||||
QLineEdit *recycleBinFolderEdit = new QLineEdit(m_notebook->getRecycleBinFolder());
|
QLineEdit *recycleBinFolderEdit = new QLineEdit(m_notebook->getRecycleBinFolder());
|
||||||
recycleBinFolderEdit->setReadOnly(true);
|
recycleBinFolderEdit->setReadOnly(true);
|
||||||
recycleBinFolderEdit->setToolTip(tr("The folder to hold deleted files from within VNote"));
|
recycleBinFolderEdit->setToolTip(tr("The folder to hold deleted files from within VNote of all the notes in this notebook"));
|
||||||
|
|
||||||
// Created time.
|
// Created time.
|
||||||
QString createdTimeStr = const_cast<VNotebook *>(m_notebook)->getCreatedTimeUtc().toLocalTime()
|
QString createdTimeStr = VUtils::displayDateTime(const_cast<VNotebook *>(m_notebook)->getCreatedTimeUtc().toLocalTime());
|
||||||
.toString(Qt::DefaultLocaleLongDate);
|
|
||||||
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
QLabel *createdTimeLabel = new QLabel(createdTimeStr);
|
||||||
|
|
||||||
QFormLayout *topLayout = new QFormLayout();
|
QFormLayout *topLayout = new QFormLayout();
|
||||||
topLayout->addRow(tr("Notebook &name:"), m_nameEdit);
|
topLayout->addRow(tr("Notebook &name:"), m_nameEdit);
|
||||||
topLayout->addRow(tr("Notebook &root folder:"), m_pathEdit);
|
topLayout->addRow(tr("Notebook &root folder:"), m_pathEdit);
|
||||||
topLayout->addRow(tr("&Image folder:"), m_imageFolderEdit);
|
topLayout->addRow(tr("&Image folder:"), m_imageFolderEdit);
|
||||||
|
topLayout->addRow(tr("Attachment folder:"), m_attachmentFolderEdit);
|
||||||
topLayout->addRow(tr("Recycle bin folder:"), recycleBinFolderEdit);
|
topLayout->addRow(tr("Recycle bin folder:"), recycleBinFolderEdit);
|
||||||
topLayout->addRow(tr("Created time:"), createdTimeLabel);
|
topLayout->addRow(tr("Created time:"), createdTimeLabel);
|
||||||
|
|
||||||
|
@ -41,6 +41,8 @@ private:
|
|||||||
QLineEdit *m_nameEdit;
|
QLineEdit *m_nameEdit;
|
||||||
QLineEdit *m_pathEdit;
|
QLineEdit *m_pathEdit;
|
||||||
QLineEdit *m_imageFolderEdit;
|
QLineEdit *m_imageFolderEdit;
|
||||||
|
// Read-only.
|
||||||
|
QLineEdit *m_attachmentFolderEdit;
|
||||||
QLabel *m_warnLabel;
|
QLabel *m_warnLabel;
|
||||||
QDialogButtonBox *m_btnBox;
|
QDialogButtonBox *m_btnBox;
|
||||||
const QVector<VNotebook *> &m_notebooks;
|
const QVector<VNotebook *> &m_notebooks;
|
||||||
|
@ -295,7 +295,7 @@ VNoteManagementTab::VNoteManagementTab(QWidget *p_parent)
|
|||||||
// Note.
|
// Note.
|
||||||
// Image folder.
|
// Image folder.
|
||||||
m_customImageFolder = new QCheckBox(tr("Custom image folder"), this);
|
m_customImageFolder = new QCheckBox(tr("Custom image folder"), this);
|
||||||
m_customImageFolder->setToolTip(tr("Set the global name of the image folder to store images "
|
m_customImageFolder->setToolTip(tr("Set the global name of the image folder to hold images "
|
||||||
"of notes (restart VNote to make it work)"));
|
"of notes (restart VNote to make it work)"));
|
||||||
connect(m_customImageFolder, &QCheckBox::stateChanged,
|
connect(m_customImageFolder, &QCheckBox::stateChanged,
|
||||||
this, &VNoteManagementTab::customImageFolderChanged);
|
this, &VNoteManagementTab::customImageFolderChanged);
|
||||||
@ -310,14 +310,32 @@ VNoteManagementTab::VNoteManagementTab(QWidget *p_parent)
|
|||||||
imageFolderLayout->addWidget(m_customImageFolder);
|
imageFolderLayout->addWidget(m_customImageFolder);
|
||||||
imageFolderLayout->addWidget(m_imageFolderEdit);
|
imageFolderLayout->addWidget(m_imageFolderEdit);
|
||||||
|
|
||||||
|
// Attachment folder.
|
||||||
|
m_customAttachmentFolder = new QCheckBox(tr("Custom attachment folder"), this);
|
||||||
|
m_customAttachmentFolder->setToolTip(tr("Set the global name of the attachment folder to hold attachments "
|
||||||
|
"of notes (restart VNote to make it work)"));
|
||||||
|
connect(m_customAttachmentFolder, &QCheckBox::stateChanged,
|
||||||
|
this, &VNoteManagementTab::customAttachmentFolderChanged);
|
||||||
|
|
||||||
|
m_attachmentFolderEdit = new QLineEdit(this);
|
||||||
|
m_attachmentFolderEdit->setPlaceholderText(tr("Name of the attachment folder"));
|
||||||
|
m_attachmentFolderEdit->setToolTip(m_customAttachmentFolder->toolTip());
|
||||||
|
validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), this);
|
||||||
|
m_attachmentFolderEdit->setValidator(validator);
|
||||||
|
|
||||||
|
QHBoxLayout *attachmentFolderLayout = new QHBoxLayout();
|
||||||
|
attachmentFolderLayout->addWidget(m_customAttachmentFolder);
|
||||||
|
attachmentFolderLayout->addWidget(m_attachmentFolderEdit);
|
||||||
|
|
||||||
QFormLayout *noteLayout = new QFormLayout();
|
QFormLayout *noteLayout = new QFormLayout();
|
||||||
noteLayout->addRow(imageFolderLayout);
|
noteLayout->addRow(imageFolderLayout);
|
||||||
|
noteLayout->addRow(attachmentFolderLayout);
|
||||||
m_noteBox->setLayout(noteLayout);
|
m_noteBox->setLayout(noteLayout);
|
||||||
|
|
||||||
// External File.
|
// External File.
|
||||||
// Image folder.
|
// Image folder.
|
||||||
m_customImageFolderExt = new QCheckBox(tr("Custom image folder"), this);
|
m_customImageFolderExt = new QCheckBox(tr("Custom image folder"), this);
|
||||||
m_customImageFolderExt->setToolTip(tr("Set the path of the global image folder to store images "
|
m_customImageFolderExt->setToolTip(tr("Set the path of the global image folder to hold images "
|
||||||
"of external files (restart VNote to make it work).\nYou "
|
"of external files (restart VNote to make it work).\nYou "
|
||||||
"could use both absolute or relative path here. If "
|
"could use both absolute or relative path here. If "
|
||||||
"absolute path is used, VNote will not manage\nthose images, "
|
"absolute path is used, VNote will not manage\nthose images, "
|
||||||
@ -350,6 +368,10 @@ bool VNoteManagementTab::loadConfiguration()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!loadAttachmentFolder()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!loadImageFolderExt()) {
|
if (!loadImageFolderExt()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -363,6 +385,10 @@ bool VNoteManagementTab::saveConfiguration()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!saveAttachmentFolder()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!saveImageFolderExt()) {
|
if (!saveImageFolderExt()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -403,6 +429,39 @@ void VNoteManagementTab::customImageFolderChanged(int p_state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VNoteManagementTab::loadAttachmentFolder()
|
||||||
|
{
|
||||||
|
bool isCustom = g_config->isCustomAttachmentFolder();
|
||||||
|
|
||||||
|
m_customAttachmentFolder->setChecked(isCustom);
|
||||||
|
m_attachmentFolderEdit->setText(g_config->getAttachmentFolder());
|
||||||
|
m_attachmentFolderEdit->setEnabled(isCustom);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VNoteManagementTab::saveAttachmentFolder()
|
||||||
|
{
|
||||||
|
if (m_customAttachmentFolder->isChecked()) {
|
||||||
|
g_config->setAttachmentFolder(m_attachmentFolderEdit->text());
|
||||||
|
} else {
|
||||||
|
g_config->setAttachmentFolder("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNoteManagementTab::customAttachmentFolderChanged(int p_state)
|
||||||
|
{
|
||||||
|
if (p_state == Qt::Checked) {
|
||||||
|
m_attachmentFolderEdit->setEnabled(true);
|
||||||
|
m_attachmentFolderEdit->selectAll();
|
||||||
|
m_attachmentFolderEdit->setFocus();
|
||||||
|
} else {
|
||||||
|
m_attachmentFolderEdit->setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool VNoteManagementTab::loadImageFolderExt()
|
bool VNoteManagementTab::loadImageFolderExt()
|
||||||
{
|
{
|
||||||
bool isCustom = g_config->isCustomImageFolderExt();
|
bool isCustom = g_config->isCustomImageFolderExt();
|
||||||
|
@ -69,9 +69,14 @@ public:
|
|||||||
QCheckBox *m_customImageFolderExt;
|
QCheckBox *m_customImageFolderExt;
|
||||||
QLineEdit *m_imageFolderEditExt;
|
QLineEdit *m_imageFolderEditExt;
|
||||||
|
|
||||||
|
// Attachment folder.
|
||||||
|
QCheckBox *m_customAttachmentFolder;
|
||||||
|
QLineEdit *m_attachmentFolderEdit;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void customImageFolderChanged(int p_state);
|
void customImageFolderChanged(int p_state);
|
||||||
void customImageFolderExtChanged(int p_state);
|
void customImageFolderExtChanged(int p_state);
|
||||||
|
void customAttachmentFolderChanged(int p_state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool loadImageFolder();
|
bool loadImageFolder();
|
||||||
@ -79,6 +84,9 @@ private:
|
|||||||
|
|
||||||
bool loadImageFolderExt();
|
bool loadImageFolderExt();
|
||||||
bool saveImageFolderExt();
|
bool saveImageFolderExt();
|
||||||
|
|
||||||
|
bool loadAttachmentFolder();
|
||||||
|
bool saveAttachmentFolder();
|
||||||
};
|
};
|
||||||
|
|
||||||
class VMarkdownTab : public QWidget
|
class VMarkdownTab : public QWidget
|
||||||
|
205
src/dialog/vsortdialog.cpp
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#include "vsortdialog.h"
|
||||||
|
|
||||||
|
#include <QtWidgets>
|
||||||
|
|
||||||
|
VSortDialog::VSortDialog(const QString &p_title,
|
||||||
|
const QString &p_info,
|
||||||
|
QWidget *p_parent)
|
||||||
|
: QDialog(p_parent)
|
||||||
|
{
|
||||||
|
setupUI(p_title, p_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSortDialog::setupUI(const QString &p_title, const QString &p_info)
|
||||||
|
{
|
||||||
|
QLabel *infoLabel = NULL;
|
||||||
|
if (!p_info.isEmpty()) {
|
||||||
|
infoLabel = new QLabel(p_info);
|
||||||
|
infoLabel->setWordWrap(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_treeWidget = new QTreeWidget();
|
||||||
|
m_treeWidget->setRootIsDecorated(false);
|
||||||
|
m_treeWidget->setSelectionMode(QAbstractItemView::ContiguousSelection);
|
||||||
|
m_treeWidget->setDragDropMode(QAbstractItemView::InternalMove);
|
||||||
|
|
||||||
|
// Buttons for top/up/down/bottom.
|
||||||
|
m_topBtn = new QPushButton(tr("&Top"));
|
||||||
|
m_topBtn->setToolTip(tr("Move selected items to top"));
|
||||||
|
connect(m_topBtn, &QPushButton::clicked,
|
||||||
|
this, [this]() {
|
||||||
|
this->handleMoveOperation(MoveOperation::Top);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_upBtn = new QPushButton(tr("&Up"));
|
||||||
|
m_upBtn->setToolTip(tr("Move selected items up"));
|
||||||
|
connect(m_upBtn, &QPushButton::clicked,
|
||||||
|
this, [this]() {
|
||||||
|
this->handleMoveOperation(MoveOperation::Up);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_downBtn = new QPushButton(tr("&Down"));
|
||||||
|
m_downBtn->setToolTip(tr("Move selected items down"));
|
||||||
|
connect(m_downBtn, &QPushButton::clicked,
|
||||||
|
this, [this]() {
|
||||||
|
this->handleMoveOperation(MoveOperation::Down);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_bottomBtn = new QPushButton(tr("&Bottom"));
|
||||||
|
m_bottomBtn->setToolTip(tr("Move selected items to bottom"));
|
||||||
|
connect(m_bottomBtn, &QPushButton::clicked,
|
||||||
|
this, [this]() {
|
||||||
|
this->handleMoveOperation(MoveOperation::Bottom);
|
||||||
|
});
|
||||||
|
|
||||||
|
QVBoxLayout *btnLayout = new QVBoxLayout;
|
||||||
|
btnLayout->addWidget(m_topBtn);
|
||||||
|
btnLayout->addWidget(m_upBtn);
|
||||||
|
btnLayout->addWidget(m_downBtn);
|
||||||
|
btnLayout->addWidget(m_bottomBtn);
|
||||||
|
btnLayout->addStretch();
|
||||||
|
|
||||||
|
QHBoxLayout *midLayout = new QHBoxLayout;
|
||||||
|
midLayout->addWidget(m_treeWidget);
|
||||||
|
midLayout->addLayout(btnLayout);
|
||||||
|
|
||||||
|
// Ok is the default button.
|
||||||
|
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
|
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
if (infoLabel) {
|
||||||
|
mainLayout->addWidget(infoLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
mainLayout->addLayout(midLayout);
|
||||||
|
mainLayout->addWidget(m_btnBox);
|
||||||
|
|
||||||
|
setLayout(mainLayout);
|
||||||
|
setWindowTitle(p_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSortDialog::treeUpdated()
|
||||||
|
{
|
||||||
|
// We just need single level.
|
||||||
|
int cnt = m_treeWidget->topLevelItemCount();
|
||||||
|
for (int i = 0; i < cnt; ++i) {
|
||||||
|
QTreeWidgetItem *item = m_treeWidget->topLevelItem(i);
|
||||||
|
item->setFlags(item->flags() & ~Qt::ItemIsDropEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSortDialog::handleMoveOperation(MoveOperation p_op)
|
||||||
|
{
|
||||||
|
const QList<QTreeWidgetItem *> selectedItems = m_treeWidget->selectedItems();
|
||||||
|
if (selectedItems.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int first = m_treeWidget->topLevelItemCount();
|
||||||
|
int last = -1;
|
||||||
|
for (auto const & it : selectedItems) {
|
||||||
|
int idx = m_treeWidget->indexOfTopLevelItem(it);
|
||||||
|
Q_ASSERT(idx > -1);
|
||||||
|
if (idx < first) {
|
||||||
|
first = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx > last) {
|
||||||
|
last = idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(first <= last && (last - first + 1) == selectedItems.size());
|
||||||
|
QTreeWidgetItem *firstItem = NULL;
|
||||||
|
|
||||||
|
switch (p_op) {
|
||||||
|
case MoveOperation::Top:
|
||||||
|
if (first == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_treeWidget->clearSelection();
|
||||||
|
|
||||||
|
// Insert item[last] to index 0 repeatedly.
|
||||||
|
for (int i = last - first; i >= 0; --i) {
|
||||||
|
QTreeWidgetItem *item = m_treeWidget->takeTopLevelItem(last);
|
||||||
|
Q_ASSERT(item);
|
||||||
|
m_treeWidget->insertTopLevelItem(0, item);
|
||||||
|
item->setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
firstItem = m_treeWidget->topLevelItem(0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MoveOperation::Up:
|
||||||
|
if (first == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_treeWidget->clearSelection();
|
||||||
|
|
||||||
|
// Insert item[last] to index (first -1) repeatedly.
|
||||||
|
for (int i = last - first; i >= 0; --i) {
|
||||||
|
QTreeWidgetItem *item = m_treeWidget->takeTopLevelItem(last);
|
||||||
|
Q_ASSERT(item);
|
||||||
|
m_treeWidget->insertTopLevelItem(first - 1, item);
|
||||||
|
item->setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
firstItem = m_treeWidget->topLevelItem(first - 1);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MoveOperation::Down:
|
||||||
|
if (last == m_treeWidget->topLevelItemCount() - 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_treeWidget->clearSelection();
|
||||||
|
|
||||||
|
// Insert item[first] to index (last) repeatedly.
|
||||||
|
for (int i = last - first; i >= 0; --i) {
|
||||||
|
QTreeWidgetItem *item = m_treeWidget->takeTopLevelItem(first);
|
||||||
|
Q_ASSERT(item);
|
||||||
|
m_treeWidget->insertTopLevelItem(last + 1, item);
|
||||||
|
item->setSelected(true);
|
||||||
|
|
||||||
|
if (!firstItem) {
|
||||||
|
firstItem = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MoveOperation::Bottom:
|
||||||
|
if (last == m_treeWidget->topLevelItemCount() - 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_treeWidget->clearSelection();
|
||||||
|
|
||||||
|
// Insert item[first] to the last of the tree repeatedly.
|
||||||
|
for (int i = last - first; i >= 0; --i) {
|
||||||
|
QTreeWidgetItem *item = m_treeWidget->takeTopLevelItem(first);
|
||||||
|
Q_ASSERT(item);
|
||||||
|
m_treeWidget->addTopLevelItem(item);
|
||||||
|
item->setSelected(true);
|
||||||
|
|
||||||
|
if (!firstItem) {
|
||||||
|
firstItem = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstItem) {
|
||||||
|
m_treeWidget->scrollToItem(firstItem);
|
||||||
|
}
|
||||||
|
}
|
46
src/dialog/vsortdialog.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#ifndef VSORTDIALOG_H
|
||||||
|
#define VSORTDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class QDialogButtonBox;
|
||||||
|
class QTreeWidget;
|
||||||
|
|
||||||
|
class VSortDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VSortDialog(const QString &p_title,
|
||||||
|
const QString &p_info,
|
||||||
|
QWidget *p_parent = 0);
|
||||||
|
|
||||||
|
QTreeWidget *getTreeWidget() const;
|
||||||
|
|
||||||
|
// Called after updating the m_treeWidget.
|
||||||
|
void treeUpdated();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum MoveOperation { Top, Up, Down, Bottom };
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleMoveOperation(MoveOperation p_op);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupUI(const QString &p_title, const QString &p_info);
|
||||||
|
|
||||||
|
QTreeWidget *m_treeWidget;
|
||||||
|
QPushButton *m_topBtn;
|
||||||
|
QPushButton *m_upBtn;
|
||||||
|
QPushButton *m_downBtn;
|
||||||
|
QPushButton *m_bottomBtn;
|
||||||
|
QDialogButtonBox *m_btnBox;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline QTreeWidget *VSortDialog::getTreeWidget() const
|
||||||
|
{
|
||||||
|
return m_treeWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // VSORTDIALOG_H
|
7
src/resources/icons/add_attachment.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<polygon points="448,224 288,224 288,64 224,64 224,224 64,224 64,288 224,288 224,448 288,448 288,288 448,288 "/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 597 B |
15
src/resources/icons/attachment.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||||
|
<g id="Icon_3_">
|
||||||
|
<g>
|
||||||
|
<path d="M341.334,128v234.666C341.334,409.604,302.938,448,256,448c-46.937,0-85.333-38.396-85.333-85.334V117.334
|
||||||
|
C170.667,87.469,194.135,64,224,64c29.864,0,53.333,23.469,53.333,53.334v245.333c0,11.729-9.605,21.333-21.334,21.333
|
||||||
|
c-11.729,0-21.333-9.604-21.333-21.333V160h-32v202.667C202.667,392.531,226.135,416,256,416
|
||||||
|
c29.865,0,53.334-23.469,53.334-53.333V117.334C309.334,70.401,270.938,32,224,32c-46.938,0-85.334,38.401-85.334,85.334v245.332
|
||||||
|
C138.667,427.729,190.938,480,256,480c65.062,0,117.334-52.271,117.334-117.334V128H341.334z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
14
src/resources/icons/attachment_full.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<g>
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<g id="Icon_3_">
|
||||||
|
<g id="svg_1">
|
||||||
|
<path d="m341.334,128l0,234.666c0,46.938 -38.396,85.334 -85.334,85.334c-46.937,0 -85.333,-38.396 -85.333,-85.334l0,-245.332c0,-29.865 23.468,-53.334 53.333,-53.334c29.864,0 53.333,23.469 53.333,53.334l0,245.333c0,11.729 -9.605,21.333 -21.334,21.333c-11.729,0 -21.333,-9.604 -21.333,-21.333l0,-202.667l-32,0l0,202.667c0.001,29.864 23.469,53.333 53.334,53.333c29.865,0 53.334,-23.469 53.334,-53.333l0,-245.333c0,-46.933 -38.396,-85.334 -85.334,-85.334c-46.938,0 -85.334,38.401 -85.334,85.334l0,245.332c0.001,65.063 52.272,117.334 117.334,117.334c65.062,0 117.334,-52.271 117.334,-117.334l0,-234.666l-32,0z" id="svg_2"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<title>Layer 2</title>
|
||||||
|
<circle stroke="#000000" fill="#15ae67" stroke-width="5" stroke-opacity="0" cx="435.5" cy="75.50001" r="70.05334" id="svg_3"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1001 B |
10
src/resources/icons/clear_attachment.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<path style="fill:#C9302C" d="M443.6,387.1L312.4,255.4l131.5-130c5.4-5.4,5.4-14.2,0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4
|
||||||
|
L256,197.8L124.9,68.3c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4L68,105.9c-5.4,5.4-5.4,14.2,0,19.6l131.5,130L68.4,387.1
|
||||||
|
c-2.6,2.6-4.1,6.1-4.1,9.8c0,3.7,1.4,7.2,4.1,9.8l37.4,37.6c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1L256,313.1l130.7,131.1
|
||||||
|
c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1l37.4-37.6c2.6-2.6,4.1-6.1,4.1-9.8C447.7,393.2,446.2,389.7,443.6,387.1z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 995 B |
10
src/resources/icons/delete_attachment.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<path d="M341,128V99c0-19.1-14.5-35-34.5-35H205.4C185.5,64,171,79.9,171,99v29H80v32h9.2c0,0,5.4,0.6,8.2,3.4c2.8,2.8,3.9,9,3.9,9
|
||||||
|
l19,241.7c1.5,29.4,1.5,33.9,36,33.9h199.4c34.5,0,34.5-4.4,36-33.8l19-241.6c0,0,1.1-6.3,3.9-9.1c2.8-2.8,8.2-3.4,8.2-3.4h9.2v-32
|
||||||
|
h-91V128z M192,99c0-9.6,7.8-15,17.7-15h91.7c9.9,0,18.6,5.5,18.6,15v29H192V99z M183.5,384l-10.3-192h20.3L204,384H183.5z
|
||||||
|
M267.1,384h-22V192h22V384z M328.7,384h-20.4l10.5-192h20.3L328.7,384z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 934 B |
10
src/resources/icons/locate_attachment.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||||
|
<path d="M437.334,144H256.006l-42.668-48H74.666C51.197,96,32,115.198,32,138.667v234.666C32,396.802,51.197,416,74.666,416h362.668
|
||||||
|
C460.803,416,480,396.802,480,373.333V186.667C480,163.198,460.803,144,437.334,144z M448,373.333
|
||||||
|
c0,5.782-4.885,10.667-10.666,10.667H74.666C68.884,384,64,379.115,64,373.333V176h373.334c5.781,0,10.666,4.885,10.666,10.667
|
||||||
|
V373.333z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 840 B |
12
src/resources/icons/sort.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<rect x="80" y="352" width="64" height="64"/>
|
||||||
|
<rect x="176" y="288" width="64" height="128"/>
|
||||||
|
<rect x="272" y="192" width="64" height="224"/>
|
||||||
|
<rect x="368" y="96" width="64" height="320"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 698 B |
@ -47,6 +47,9 @@ image_folder=_v_images
|
|||||||
; Image folder name for the external files
|
; Image folder name for the external files
|
||||||
external_image_folder=_v_images
|
external_image_folder=_v_images
|
||||||
|
|
||||||
|
; Attachment folder name for the notes
|
||||||
|
attachment_folder=_v_attachments
|
||||||
|
|
||||||
; Enable trailing space highlight
|
; Enable trailing space highlight
|
||||||
enable_trailing_space_highlight=true
|
enable_trailing_space_highlight=true
|
||||||
|
|
||||||
|
@ -75,7 +75,9 @@ SOURCES += main.cpp\
|
|||||||
vtextblockdata.cpp \
|
vtextblockdata.cpp \
|
||||||
utils/vpreviewutils.cpp \
|
utils/vpreviewutils.cpp \
|
||||||
dialog/vconfirmdeletiondialog.cpp \
|
dialog/vconfirmdeletiondialog.cpp \
|
||||||
vnotefile.cpp
|
vnotefile.cpp \
|
||||||
|
vattachmentlist.cpp \
|
||||||
|
dialog/vsortdialog.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
@ -138,7 +140,9 @@ HEADERS += vmainwindow.h \
|
|||||||
vtextblockdata.h \
|
vtextblockdata.h \
|
||||||
utils/vpreviewutils.h \
|
utils/vpreviewutils.h \
|
||||||
dialog/vconfirmdeletiondialog.h \
|
dialog/vconfirmdeletiondialog.h \
|
||||||
vnotefile.h
|
vnotefile.h \
|
||||||
|
vattachmentlist.h \
|
||||||
|
dialog/vsortdialog.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc \
|
vnote.qrc \
|
||||||
|
@ -123,14 +123,13 @@ QString VUtils::generateImageFileName(const QString &path, const QString &title,
|
|||||||
baseName = baseName + '_' + QString::number(QDateTime::currentDateTime().toTime_t());
|
baseName = baseName + '_' + QString::number(QDateTime::currentDateTime().toTime_t());
|
||||||
baseName = baseName + '_' + QString::number(qrand());
|
baseName = baseName + '_' + QString::number(qrand());
|
||||||
|
|
||||||
|
QDir dir(path);
|
||||||
QString imageName = baseName + "." + format.toLower();
|
QString imageName = baseName + "." + format.toLower();
|
||||||
QString filePath = QDir(path).filePath(imageName);
|
|
||||||
int index = 1;
|
int index = 1;
|
||||||
|
|
||||||
while (QFileInfo::exists(filePath)) {
|
while (fileExists(dir, imageName, true)) {
|
||||||
imageName = QString("%1_%2.%3").arg(baseName).arg(index++)
|
imageName = QString("%1_%2.%3").arg(baseName).arg(index++)
|
||||||
.arg(format.toLower());
|
.arg(format.toLower());
|
||||||
filePath = QDir(path).filePath(imageName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageName;
|
return imageName;
|
||||||
@ -191,6 +190,7 @@ QVector<ImageLink> VUtils::fetchImagesFromMarkdownFile(VFile *p_file,
|
|||||||
QString linkText = text.mid(reg.m_startPos, reg.m_endPos - reg.m_startPos);
|
QString linkText = text.mid(reg.m_startPos, reg.m_endPos - reg.m_startPos);
|
||||||
bool matched = regExp.exactMatch(linkText);
|
bool matched = regExp.exactMatch(linkText);
|
||||||
Q_ASSERT(matched);
|
Q_ASSERT(matched);
|
||||||
|
Q_UNUSED(matched);
|
||||||
QString imageUrl = regExp.capturedTexts()[2].trimmed();
|
QString imageUrl = regExp.capturedTexts()[2].trimmed();
|
||||||
|
|
||||||
ImageLink link;
|
ImageLink link;
|
||||||
@ -605,11 +605,25 @@ QString VUtils::getFileNameWithSequence(const QString &p_directory,
|
|||||||
if (!suffix.isEmpty()) {
|
if (!suffix.isEmpty()) {
|
||||||
fileName = fileName + "." + suffix;
|
fileName = fileName + "." + suffix;
|
||||||
}
|
}
|
||||||
} while (dir.exists(fileName));
|
} while (fileExists(dir, fileName, true));
|
||||||
|
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VUtils::getRandomFileName(const QString &p_directory)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!p_directory.isEmpty());
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
QDir dir(p_directory);
|
||||||
|
do {
|
||||||
|
name = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
|
||||||
|
name = name + '_' + QString::number(qrand());
|
||||||
|
} while (fileExists(dir, name, true));
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
bool VUtils::checkPathLegal(const QString &p_path)
|
bool VUtils::checkPathLegal(const QString &p_path)
|
||||||
{
|
{
|
||||||
// Ensure every part of the p_path is a valid file name until we come to
|
// Ensure every part of the p_path is a valid file name until we come to
|
||||||
@ -869,3 +883,28 @@ QVector<VElementRegion> VUtils::fetchImageRegionsUsingParser(const QString &p_co
|
|||||||
|
|
||||||
return regs;
|
return regs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VUtils::displayDateTime(const QDateTime &p_dateTime)
|
||||||
|
{
|
||||||
|
QString res = p_dateTime.date().toString(Qt::DefaultLocaleLongDate);
|
||||||
|
res += " " + p_dateTime.time().toString();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VUtils::fileExists(const QDir &p_dir, const QString &p_name, bool p_forceCaseInsensitive)
|
||||||
|
{
|
||||||
|
if (!p_forceCaseInsensitive) {
|
||||||
|
return p_dir.exists(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name = p_name.toLower();
|
||||||
|
QStringList names = p_dir.entryList(QDir::Dirs | QDir::Files | QDir::Hidden
|
||||||
|
| QDir::NoSymLinks | QDir::NoDotAndDotDot);
|
||||||
|
foreach (const QString &str, names) {
|
||||||
|
if (str.toLower() == name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QDir>
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
|
|
||||||
@ -104,6 +105,9 @@ public:
|
|||||||
static QString getFileNameWithSequence(const QString &p_directory,
|
static QString getFileNameWithSequence(const QString &p_directory,
|
||||||
const QString &p_baseFileName);
|
const QString &p_baseFileName);
|
||||||
|
|
||||||
|
// Get an available random file name in @p_directory.
|
||||||
|
static QString getRandomFileName(const QString &p_directory);
|
||||||
|
|
||||||
// Try to check if @p_path is legal.
|
// Try to check if @p_path is legal.
|
||||||
static bool checkPathLegal(const QString &p_path);
|
static bool checkPathLegal(const QString &p_path);
|
||||||
|
|
||||||
@ -152,6 +156,12 @@ public:
|
|||||||
static bool deleteFile(const QString &p_path,
|
static bool deleteFile(const QString &p_path,
|
||||||
bool p_skipRecycleBin = false);
|
bool p_skipRecycleBin = false);
|
||||||
|
|
||||||
|
static QString displayDateTime(const QDateTime &p_dateTime);
|
||||||
|
|
||||||
|
// Check if file @p_name exists in @p_dir.
|
||||||
|
// @p_forceCaseInsensitive: if true, will check the name ignoring the case.
|
||||||
|
static bool fileExists(const QDir &p_dir, const QString &p_name, bool p_forceCaseInsensitive = false);
|
||||||
|
|
||||||
// Regular expression for image link.
|
// Regular expression for image link.
|
||||||
// 
|
// 
|
||||||
// Captured texts (need to be trimmed):
|
// Captured texts (need to be trimmed):
|
||||||
|
404
src/vattachmentlist.cpp
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
#include "vattachmentlist.h"
|
||||||
|
|
||||||
|
#include <QtWidgets>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
VAttachmentList::VAttachmentList(QWidget *p_parent)
|
||||||
|
: QWidget(p_parent), m_file(NULL)
|
||||||
|
{
|
||||||
|
setupUI();
|
||||||
|
|
||||||
|
initActions();
|
||||||
|
|
||||||
|
updateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::setupUI()
|
||||||
|
{
|
||||||
|
m_addBtn = new QPushButton(QIcon(":/resources/icons/add_attachment.svg"), "");
|
||||||
|
m_addBtn->setToolTip(tr("Add"));
|
||||||
|
m_addBtn->setProperty("FlatBtn", true);
|
||||||
|
connect(m_addBtn, &QPushButton::clicked,
|
||||||
|
this, &VAttachmentList::addAttachment);
|
||||||
|
|
||||||
|
m_clearBtn = new QPushButton(QIcon(":/resources/icons/clear_attachment.svg"), "");
|
||||||
|
m_clearBtn->setToolTip(tr("Clear"));
|
||||||
|
m_clearBtn->setProperty("FlatBtn", true);
|
||||||
|
connect(m_clearBtn, &QPushButton::clicked,
|
||||||
|
this, [this]() {
|
||||||
|
if (m_file && m_attachmentList->count() > 0) {
|
||||||
|
int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
||||||
|
tr("Are you sure to clear attachments of note "
|
||||||
|
"<span style=\"%1\">%2</span>?")
|
||||||
|
.arg(g_config->c_dataTextStyle)
|
||||||
|
.arg(m_file->getName()),
|
||||||
|
tr("<span style=\"%1\">WARNING</span>: "
|
||||||
|
"VNote will delete all the files in directory "
|
||||||
|
"<span style=\"%2\">%3</span>."
|
||||||
|
"You could find deleted files in the recycle bin "
|
||||||
|
"of this notebook.<br>The operation is IRREVERSIBLE!")
|
||||||
|
.arg(g_config->c_warningTextStyle)
|
||||||
|
.arg(g_config->c_dataTextStyle)
|
||||||
|
.arg(m_file->fetchAttachmentFolderPath()),
|
||||||
|
QMessageBox::Ok | QMessageBox::Cancel,
|
||||||
|
QMessageBox::Ok,
|
||||||
|
g_vnote->getMainWindow(),
|
||||||
|
MessageBoxType::Danger);
|
||||||
|
if (ret == QMessageBox::Ok) {
|
||||||
|
if (!m_file->deleteAttachments()) {
|
||||||
|
VUtils::showMessage(QMessageBox::Warning,
|
||||||
|
tr("Warning"),
|
||||||
|
tr("Fail to clear attachments of note <span style=\"%1\">%2</span>.")
|
||||||
|
.arg(g_config->c_dataTextStyle)
|
||||||
|
.arg(m_file->getName()),
|
||||||
|
tr("Please maintain the configureation file manually."),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok,
|
||||||
|
g_vnote->getMainWindow());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_attachmentList->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_locateBtn = new QPushButton(QIcon(":/resources/icons/locate_attachment.svg"), "");
|
||||||
|
m_locateBtn->setToolTip(tr("Open Folder"));
|
||||||
|
m_locateBtn->setProperty("FlatBtn", true);
|
||||||
|
connect(m_locateBtn, &QPushButton::clicked,
|
||||||
|
this, [this]() {
|
||||||
|
if (m_file && !m_file->getAttachmentFolder().isEmpty()) {
|
||||||
|
QUrl url = QUrl::fromLocalFile(m_file->fetchAttachmentFolderPath());
|
||||||
|
QDesktopServices::openUrl(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_numLabel = new QLabel();
|
||||||
|
|
||||||
|
QHBoxLayout *btnLayout = new QHBoxLayout;
|
||||||
|
btnLayout->addWidget(m_addBtn);
|
||||||
|
btnLayout->addWidget(m_clearBtn);
|
||||||
|
btnLayout->addWidget(m_locateBtn);
|
||||||
|
btnLayout->addStretch();
|
||||||
|
btnLayout->addWidget(m_numLabel);
|
||||||
|
|
||||||
|
m_attachmentList = new QListWidget;
|
||||||
|
m_attachmentList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
m_attachmentList->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
m_attachmentList->setEditTriggers(QAbstractItemView::SelectedClicked);
|
||||||
|
connect(m_attachmentList, &QListWidget::customContextMenuRequested,
|
||||||
|
this, &VAttachmentList::handleContextMenuRequested);
|
||||||
|
connect(m_attachmentList, &QListWidget::itemActivated,
|
||||||
|
this, &VAttachmentList::handleItemActivated);
|
||||||
|
connect(m_attachmentList->itemDelegate(), &QAbstractItemDelegate::commitData,
|
||||||
|
this, &VAttachmentList::handleListItemCommitData);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||||
|
mainLayout->addLayout(btnLayout);
|
||||||
|
mainLayout->addWidget(m_attachmentList);
|
||||||
|
|
||||||
|
setLayout(mainLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::initActions()
|
||||||
|
{
|
||||||
|
m_openAct = new QAction(tr("&Open"), this);
|
||||||
|
m_openAct->setToolTip(tr("Open current attachment file"));
|
||||||
|
connect(m_openAct, &QAction::triggered,
|
||||||
|
this, [this]() {
|
||||||
|
QListWidgetItem *item = m_attachmentList->currentItem();
|
||||||
|
handleItemActivated(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_deleteAct = new QAction(QIcon(":/resources/icons/delete_attachment.svg"),
|
||||||
|
tr("&Delete"),
|
||||||
|
this);
|
||||||
|
m_deleteAct->setToolTip(tr("Delete selected attachments"));
|
||||||
|
connect(m_deleteAct, &QAction::triggered,
|
||||||
|
this, &VAttachmentList::deleteSelectedItems);
|
||||||
|
|
||||||
|
m_sortAct = new QAction(QIcon(":/resources/icons/sort.svg"),
|
||||||
|
tr("&Sort"),
|
||||||
|
this);
|
||||||
|
m_sortAct->setToolTip(tr("Sort attachments manually"));
|
||||||
|
connect(m_sortAct, &QAction::triggered,
|
||||||
|
this, &VAttachmentList::sortItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::setFile(VNoteFile *p_file)
|
||||||
|
{
|
||||||
|
m_file = p_file;
|
||||||
|
updateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::updateContent()
|
||||||
|
{
|
||||||
|
bool enableAdd = true, enableDelete = true, enableClear = true, enableLocate = true;
|
||||||
|
m_attachmentList->clear();
|
||||||
|
|
||||||
|
if (!m_file) {
|
||||||
|
enableAdd = enableDelete = enableClear = enableLocate = false;
|
||||||
|
} else {
|
||||||
|
QString folder = m_file->getAttachmentFolder();
|
||||||
|
const QVector<VAttachment> &attas = m_file->getAttachments();
|
||||||
|
|
||||||
|
if (folder.isEmpty()) {
|
||||||
|
Q_ASSERT(attas.isEmpty());
|
||||||
|
enableDelete = enableClear = enableLocate = false;
|
||||||
|
} else if (attas.isEmpty()) {
|
||||||
|
enableDelete = enableClear = false;
|
||||||
|
} else {
|
||||||
|
fillAttachmentList(attas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_addBtn->setEnabled(enableAdd);
|
||||||
|
m_clearBtn->setEnabled(enableClear);
|
||||||
|
m_locateBtn->setEnabled(enableLocate);
|
||||||
|
|
||||||
|
int cnt = m_attachmentList->count();
|
||||||
|
if (cnt > 0) {
|
||||||
|
m_numLabel->setText(tr("%1 %2").arg(cnt).arg(cnt > 1 ? tr("Files") : tr("File")));
|
||||||
|
} else {
|
||||||
|
m_numLabel->setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::fillAttachmentList(const QVector<VAttachment> &p_attachments)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_attachmentList->count() == 0);
|
||||||
|
for (int i = 0; i < p_attachments.size(); ++i) {
|
||||||
|
const VAttachment &atta = p_attachments[i];
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(atta.m_name);
|
||||||
|
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||||
|
item->setData(Qt::UserRole, atta.m_name);
|
||||||
|
|
||||||
|
m_attachmentList->addItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::addAttachment()
|
||||||
|
{
|
||||||
|
if (!m_file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString lastPath = QDir::homePath();
|
||||||
|
QStringList files = QFileDialog::getOpenFileNames(g_vnote->getMainWindow(),
|
||||||
|
tr("Select Files As Attachments"),
|
||||||
|
lastPath);
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update lastPath
|
||||||
|
lastPath = QFileInfo(files[0]).path();
|
||||||
|
|
||||||
|
int addedFiles = 0;
|
||||||
|
for (int i = 0; i < files.size(); ++i) {
|
||||||
|
if (!m_file->addAttachment(files[i])) {
|
||||||
|
VUtils::showMessage(QMessageBox::Warning,
|
||||||
|
tr("Warning"),
|
||||||
|
tr("Fail to add attachment %1 for note <span style=\"%2\">%3</span>.")
|
||||||
|
.arg(files[i])
|
||||||
|
.arg(g_config->c_dataTextStyle)
|
||||||
|
.arg(m_file->getName()),
|
||||||
|
"",
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok,
|
||||||
|
g_vnote->getMainWindow());
|
||||||
|
} else {
|
||||||
|
++addedFiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContent();
|
||||||
|
|
||||||
|
if (addedFiles > 0) {
|
||||||
|
g_vnote->getMainWindow()->showStatusMessage(tr("Added %1 %2 as attachments")
|
||||||
|
.arg(addedFiles)
|
||||||
|
.arg(addedFiles > 1 ? tr("files") : tr("file")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::handleContextMenuRequested(QPoint p_pos)
|
||||||
|
{
|
||||||
|
// @p_pos is the position in the coordinate of VAttachmentList, no m_attachmentList.
|
||||||
|
QListWidgetItem *item = m_attachmentList->itemAt(m_attachmentList->mapFromParent(p_pos));
|
||||||
|
QMenu menu(this);
|
||||||
|
menu.setToolTipsVisible(true);
|
||||||
|
|
||||||
|
if (!m_file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
if (!item->isSelected()) {
|
||||||
|
m_attachmentList->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_attachmentList->selectedItems().size() == 1) {
|
||||||
|
menu.addAction(m_openAct);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.addAction(m_deleteAct);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_attachmentList->update();
|
||||||
|
|
||||||
|
if (m_file->getAttachments().size() > 1) {
|
||||||
|
if (!menu.actions().isEmpty()) {
|
||||||
|
menu.addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.addAction(m_sortAct);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!menu.actions().isEmpty()) {
|
||||||
|
menu.exec(mapToGlobal(p_pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::handleItemActivated(QListWidgetItem *p_item)
|
||||||
|
{
|
||||||
|
if (p_item) {
|
||||||
|
Q_ASSERT(m_file);
|
||||||
|
|
||||||
|
QString name = p_item->text();
|
||||||
|
QString folderPath = m_file->fetchAttachmentFolderPath();
|
||||||
|
QUrl url = QUrl::fromLocalFile(QDir(folderPath).filePath(name));
|
||||||
|
QDesktopServices::openUrl(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::deleteSelectedItems()
|
||||||
|
{
|
||||||
|
QVector<QString> names;
|
||||||
|
const QList<QListWidgetItem *> selectedItems = m_attachmentList->selectedItems();
|
||||||
|
|
||||||
|
if (selectedItems.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const & item : selectedItems) {
|
||||||
|
names.push_back(item->text());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString info = tr("Are you sure to delete these attachments of note "
|
||||||
|
"<span style=\"%1\">%2</span>? "
|
||||||
|
"You could find deleted files in the recycle "
|
||||||
|
"bin of this notebook.<br>"
|
||||||
|
"Click \"Cancel\" to leave them untouched.")
|
||||||
|
.arg(g_config->c_dataTextStyle).arg(m_file->getName());
|
||||||
|
VConfirmDeletionDialog dialog(tr("Confirm Deleting Attachments"),
|
||||||
|
info,
|
||||||
|
names,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
g_vnote->getMainWindow());
|
||||||
|
if (dialog.exec()) {
|
||||||
|
names = dialog.getConfirmedFiles();
|
||||||
|
|
||||||
|
if (!m_file->deleteAttachments(names)) {
|
||||||
|
VUtils::showMessage(QMessageBox::Warning,
|
||||||
|
tr("Warning"),
|
||||||
|
tr("Fail to delete attachments of note <span style=\"%1\">%2</span>.")
|
||||||
|
.arg(g_config->c_dataTextStyle)
|
||||||
|
.arg(m_file->getName()),
|
||||||
|
tr("Please maintain the configureation file manually."),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok,
|
||||||
|
g_vnote->getMainWindow());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::sortItems()
|
||||||
|
{
|
||||||
|
const QVector<VAttachment> &attas = m_file->getAttachments();
|
||||||
|
if (attas.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VSortDialog dialog(tr("Sort Attachments"),
|
||||||
|
tr("Sort attachments in the configuration file."),
|
||||||
|
g_vnote->getMainWindow());
|
||||||
|
QTreeWidget *tree = dialog.getTreeWidget();
|
||||||
|
tree->clear();
|
||||||
|
tree->setColumnCount(1);
|
||||||
|
tree->header()->setStretchLastSection(true);
|
||||||
|
QStringList headers;
|
||||||
|
headers << tr("Name");
|
||||||
|
tree->setHeaderLabels(headers);
|
||||||
|
|
||||||
|
for (int i = 0; i < attas.size(); ++i) {
|
||||||
|
QTreeWidgetItem *item = new QTreeWidgetItem(tree, QStringList(attas[i].m_name));
|
||||||
|
|
||||||
|
item->setData(0, Qt::UserRole, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.treeUpdated();
|
||||||
|
|
||||||
|
if (dialog.exec()) {
|
||||||
|
int cnt = tree->topLevelItemCount();
|
||||||
|
Q_ASSERT(cnt == attas.size());
|
||||||
|
QVector<int> sortedIdx(cnt, -1);
|
||||||
|
for (int i = 0; i < cnt; ++i) {
|
||||||
|
QTreeWidgetItem *item = tree->topLevelItem(i);
|
||||||
|
Q_ASSERT(item);
|
||||||
|
sortedIdx[i] = item->data(0, Qt::UserRole).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_file->sortAttachments(sortedIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VAttachmentList::handleListItemCommitData(QWidget *p_itemEdit)
|
||||||
|
{
|
||||||
|
QString text = reinterpret_cast<QLineEdit *>(p_itemEdit)->text();
|
||||||
|
QListWidgetItem *item = m_attachmentList->currentItem();
|
||||||
|
Q_ASSERT(item && item->text() == text);
|
||||||
|
|
||||||
|
QString oldText = item->data(Qt::UserRole).toString();
|
||||||
|
|
||||||
|
if (oldText == text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(oldText.toLower() == text.toLower())
|
||||||
|
&& m_file->findAttachment(text, false) > -1) {
|
||||||
|
// Name conflict.
|
||||||
|
// Recover to old name.
|
||||||
|
item->setText(oldText);
|
||||||
|
} else {
|
||||||
|
if (!m_file->renameAttachment(oldText, text)) {
|
||||||
|
VUtils::showMessage(QMessageBox::Information,
|
||||||
|
tr("Rename Attachment"),
|
||||||
|
tr("Fail to rename attachment <span style=\"%1\">%2</span>.")
|
||||||
|
.arg(g_config->c_dataTextStyle)
|
||||||
|
.arg(oldText),
|
||||||
|
"",
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok,
|
||||||
|
this);
|
||||||
|
// Recover to old name.
|
||||||
|
item->setText(oldText);
|
||||||
|
} else {
|
||||||
|
// Change the data.
|
||||||
|
item->setData(Qt::UserRole, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
src/vattachmentlist.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef VATTACHMENTLIST_H
|
||||||
|
#define VATTACHMENTLIST_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QVector>
|
||||||
|
#include "vnotefile.h"
|
||||||
|
|
||||||
|
class QPushButton;
|
||||||
|
class QListWidget;
|
||||||
|
class QListWidgetItem;
|
||||||
|
class QLabel;
|
||||||
|
class VNoteFile;
|
||||||
|
class QAction;
|
||||||
|
|
||||||
|
class VAttachmentList : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit VAttachmentList(QWidget *p_parent = 0);
|
||||||
|
|
||||||
|
void setFile(VNoteFile *p_file);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void addAttachment();
|
||||||
|
|
||||||
|
void handleContextMenuRequested(QPoint p_pos);
|
||||||
|
|
||||||
|
void handleItemActivated(QListWidgetItem *p_item);
|
||||||
|
|
||||||
|
void deleteSelectedItems();
|
||||||
|
|
||||||
|
void sortItems();
|
||||||
|
|
||||||
|
void handleListItemCommitData(QWidget *p_itemEdit);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupUI();
|
||||||
|
|
||||||
|
void initActions();
|
||||||
|
|
||||||
|
// Update attachment info of m_file.
|
||||||
|
void updateContent();
|
||||||
|
|
||||||
|
void fillAttachmentList(const QVector<VAttachment> &p_attachments);
|
||||||
|
|
||||||
|
QPushButton *m_addBtn;
|
||||||
|
QPushButton *m_clearBtn;
|
||||||
|
QPushButton *m_locateBtn;
|
||||||
|
QLabel *m_numLabel;
|
||||||
|
|
||||||
|
QListWidget *m_attachmentList;
|
||||||
|
|
||||||
|
QAction *m_openAct;
|
||||||
|
QAction *m_deleteAct;
|
||||||
|
QAction *m_sortAct;
|
||||||
|
|
||||||
|
VNoteFile *m_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VATTACHMENTLIST_H
|
@ -1,60 +1,44 @@
|
|||||||
#include "vbuttonwithwidget.h"
|
#include "vbuttonwithwidget.h"
|
||||||
|
|
||||||
#include <QEvent>
|
#include <QMenu>
|
||||||
#include <QMouseEvent>
|
|
||||||
#include <QRect>
|
|
||||||
|
|
||||||
VButtonWithWidget::VButtonWithWidget(QWidget *p_parent)
|
VButtonWithWidget::VButtonWithWidget(QWidget *p_widget,
|
||||||
: QPushButton(p_parent), m_popupWidget(NULL)
|
QWidget *p_parent)
|
||||||
|
: QPushButton(p_parent), m_popupWidget(p_widget)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
VButtonWithWidget::VButtonWithWidget(const QString &p_text, QWidget *p_parent)
|
VButtonWithWidget::VButtonWithWidget(const QString &p_text,
|
||||||
: QPushButton(p_text, p_parent), m_popupWidget(NULL)
|
QWidget *p_widget,
|
||||||
|
QWidget *p_parent)
|
||||||
|
: QPushButton(p_text, p_parent), m_popupWidget(p_widget)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
VButtonWithWidget::VButtonWithWidget(const QIcon &p_icon,
|
VButtonWithWidget::VButtonWithWidget(const QIcon &p_icon,
|
||||||
const QString &p_text,
|
const QString &p_text,
|
||||||
|
QWidget *p_widget,
|
||||||
QWidget *p_parent)
|
QWidget *p_parent)
|
||||||
: QPushButton(p_icon, p_text, p_parent), m_popupWidget(NULL)
|
: QPushButton(p_icon, p_text, p_parent), m_popupWidget(p_widget)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
VButtonWithWidget::~VButtonWithWidget()
|
|
||||||
{
|
|
||||||
if (m_popupWidget) {
|
|
||||||
delete m_popupWidget;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VButtonWithWidget::init()
|
void VButtonWithWidget::init()
|
||||||
{
|
{
|
||||||
connect(this, &QPushButton::clicked,
|
m_popupWidget->setParent(this);
|
||||||
this, &VButtonWithWidget::showPopupWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VButtonWithWidget::setPopupWidget(QWidget *p_widget)
|
QMenu *menu = new QMenu(this);
|
||||||
{
|
VButtonWidgetAction *act = new VButtonWidgetAction(m_popupWidget, menu);
|
||||||
if (m_popupWidget) {
|
menu->addAction(act);
|
||||||
delete m_popupWidget;
|
connect(menu, &QMenu::aboutToShow,
|
||||||
}
|
this, [this]() {
|
||||||
|
emit popupWidgetAboutToShow(m_popupWidget);
|
||||||
|
});
|
||||||
|
|
||||||
m_popupWidget = p_widget;
|
setMenu(menu);
|
||||||
if (m_popupWidget) {
|
|
||||||
m_popupWidget->hide();
|
|
||||||
m_popupWidget->setParent(NULL);
|
|
||||||
|
|
||||||
Qt::WindowFlags flags = Qt::Popup;
|
|
||||||
m_popupWidget->setWindowFlags(flags);
|
|
||||||
m_popupWidget->setWindowModality(Qt::NonModal);
|
|
||||||
|
|
||||||
// Let popup widget to hide itself if focus lost.
|
|
||||||
m_popupWidget->installEventFilter(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget *VButtonWithWidget::getPopupWidget() const
|
QWidget *VButtonWithWidget::getPopupWidget() const
|
||||||
@ -64,35 +48,5 @@ QWidget *VButtonWithWidget::getPopupWidget() const
|
|||||||
|
|
||||||
void VButtonWithWidget::showPopupWidget()
|
void VButtonWithWidget::showPopupWidget()
|
||||||
{
|
{
|
||||||
if (m_popupWidget->isVisible()) {
|
showMenu();
|
||||||
m_popupWidget->hide();
|
|
||||||
} else {
|
|
||||||
emit popupWidgetAboutToShow(m_popupWidget);
|
|
||||||
|
|
||||||
// Calculate the position of the popup widget.
|
|
||||||
QPoint btnPos = mapToGlobal(QPoint(0, 0));
|
|
||||||
int btnWidth = width();
|
|
||||||
|
|
||||||
int popupWidth = btnWidth * 10;
|
|
||||||
int popupHeight = height() * 10;
|
|
||||||
int popupX = btnPos.x() + btnWidth - popupWidth;
|
|
||||||
int popupY = btnPos.y() - popupHeight - 10;
|
|
||||||
|
|
||||||
m_popupWidget->setGeometry(popupX, popupY, popupWidth, popupHeight);
|
|
||||||
m_popupWidget->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VButtonWithWidget::eventFilter(QObject *p_obj, QEvent *p_event)
|
|
||||||
{
|
|
||||||
if (p_event->type() == QEvent::MouseButtonRelease) {
|
|
||||||
QMouseEvent *eve = dynamic_cast<QMouseEvent *>(p_event);
|
|
||||||
QPoint clickPos = eve->pos();
|
|
||||||
const QRect &rect = m_popupWidget->rect();
|
|
||||||
if (!rect.contains(clickPos)) {
|
|
||||||
m_popupWidget->hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return QPushButton::eventFilter(p_obj, p_event);
|
|
||||||
}
|
}
|
||||||
|
@ -4,35 +4,53 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QWidgetAction>
|
||||||
|
|
||||||
|
class VButtonWidgetAction : public QWidgetAction
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VButtonWidgetAction(QWidget *p_widget, QWidget *p_parent)
|
||||||
|
: QWidgetAction(p_parent), m_widget(p_widget)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *createWidget(QWidget *p_parent)
|
||||||
|
{
|
||||||
|
m_widget->setParent(p_parent);
|
||||||
|
return m_widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWidget *m_widget;
|
||||||
|
};
|
||||||
|
|
||||||
// A QPushButton with popup widget.
|
// A QPushButton with popup widget.
|
||||||
class VButtonWithWidget : public QPushButton
|
class VButtonWithWidget : public QPushButton
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VButtonWithWidget(QWidget *p_parent = Q_NULLPTR);
|
VButtonWithWidget(QWidget *p_widget,
|
||||||
VButtonWithWidget(const QString &p_text, QWidget *p_parent = Q_NULLPTR);
|
QWidget *p_parent = Q_NULLPTR);
|
||||||
|
|
||||||
|
VButtonWithWidget(const QString &p_text,
|
||||||
|
QWidget *p_widget,
|
||||||
|
QWidget *p_parent = Q_NULLPTR);
|
||||||
|
|
||||||
VButtonWithWidget(const QIcon &p_icon,
|
VButtonWithWidget(const QIcon &p_icon,
|
||||||
const QString &p_text,
|
const QString &p_text,
|
||||||
|
QWidget *p_widget,
|
||||||
QWidget *p_parent = Q_NULLPTR);
|
QWidget *p_parent = Q_NULLPTR);
|
||||||
~VButtonWithWidget();
|
|
||||||
|
|
||||||
// Set the widget which will transfer the ownership to VButtonWithWidget.
|
|
||||||
void setPopupWidget(QWidget *p_widget);
|
|
||||||
|
|
||||||
QWidget *getPopupWidget() const;
|
QWidget *getPopupWidget() const;
|
||||||
|
|
||||||
|
// Show the popup widget.
|
||||||
|
void showPopupWidget();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// Emit when popup widget is about to show.
|
// Emit when popup widget is about to show.
|
||||||
void popupWidgetAboutToShow(QWidget *p_widget);
|
void popupWidgetAboutToShow(QWidget *p_widget);
|
||||||
|
|
||||||
protected:
|
|
||||||
bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
// Show the popup widget.
|
|
||||||
void showPopupWidget();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
@ -136,6 +136,13 @@ void VConfigManager::initialize()
|
|||||||
m_imageFolderExt = getConfigFromSettings("global",
|
m_imageFolderExt = getConfigFromSettings("global",
|
||||||
"external_image_folder").toString();
|
"external_image_folder").toString();
|
||||||
|
|
||||||
|
m_attachmentFolder = getConfigFromSettings("global",
|
||||||
|
"attachment_folder").toString();
|
||||||
|
if (m_attachmentFolder.isEmpty()) {
|
||||||
|
// Reset the default folder.
|
||||||
|
m_attachmentFolder = resetDefaultConfig("global", "attachment_folder").toString();
|
||||||
|
}
|
||||||
|
|
||||||
m_enableTrailingSpaceHighlight = getConfigFromSettings("global",
|
m_enableTrailingSpaceHighlight = getConfigFromSettings("global",
|
||||||
"enable_trailing_space_highlight").toBool();
|
"enable_trailing_space_highlight").toBool();
|
||||||
|
|
||||||
@ -230,7 +237,7 @@ void VConfigManager::readNotebookFromSettings(QVector<VNotebook *> &p_notebooks,
|
|||||||
QString name = userSettings->value("name").toString();
|
QString name = userSettings->value("name").toString();
|
||||||
QString path = userSettings->value("path").toString();
|
QString path = userSettings->value("path").toString();
|
||||||
VNotebook *notebook = new VNotebook(name, path, parent);
|
VNotebook *notebook = new VNotebook(name, path, parent);
|
||||||
notebook->readConfig();
|
notebook->readConfigNotebook();
|
||||||
p_notebooks.append(notebook);
|
p_notebooks.append(notebook);
|
||||||
}
|
}
|
||||||
userSettings->endArray();
|
userSettings->endArray();
|
||||||
|
@ -213,6 +213,11 @@ public:
|
|||||||
void setImageFolderExt(const QString &p_folder);
|
void setImageFolderExt(const QString &p_folder);
|
||||||
bool isCustomImageFolderExt() const;
|
bool isCustomImageFolderExt() const;
|
||||||
|
|
||||||
|
const QString &getAttachmentFolder() const;
|
||||||
|
// Empty string to reset the default folder.
|
||||||
|
void setAttachmentFolder(const QString &p_folder);
|
||||||
|
bool isCustomAttachmentFolder() const;
|
||||||
|
|
||||||
bool getEnableTrailingSpaceHighlight() const;
|
bool getEnableTrailingSpaceHighlight() const;
|
||||||
void setEnableTrailingSapceHighlight(bool p_enabled);
|
void setEnableTrailingSapceHighlight(bool p_enabled);
|
||||||
|
|
||||||
@ -462,6 +467,10 @@ private:
|
|||||||
// Each file can specify its custom folder.
|
// Each file can specify its custom folder.
|
||||||
QString m_imageFolderExt;
|
QString m_imageFolderExt;
|
||||||
|
|
||||||
|
// Global default folder name to store attachments of all the notes.
|
||||||
|
// Each notebook can specify its custom folder.
|
||||||
|
QString m_attachmentFolder;
|
||||||
|
|
||||||
// Enable trailing-space highlight.
|
// Enable trailing-space highlight.
|
||||||
bool m_enableTrailingSpaceHighlight;
|
bool m_enableTrailingSpaceHighlight;
|
||||||
|
|
||||||
@ -1140,6 +1149,32 @@ inline bool VConfigManager::isCustomImageFolderExt() const
|
|||||||
return m_imageFolderExt != getDefaultConfig("global", "external_image_folder").toString();
|
return m_imageFolderExt != getDefaultConfig("global", "external_image_folder").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getAttachmentFolder() const
|
||||||
|
{
|
||||||
|
return m_attachmentFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VConfigManager::setAttachmentFolder(const QString &p_folder)
|
||||||
|
{
|
||||||
|
if (p_folder.isEmpty()) {
|
||||||
|
// Reset the default folder.
|
||||||
|
m_attachmentFolder = resetDefaultConfig("global", "attachment_folder").toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_attachmentFolder == p_folder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_attachmentFolder = p_folder;
|
||||||
|
setConfigToSettings("global", "attachment_folder", m_attachmentFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool VConfigManager::isCustomAttachmentFolder() const
|
||||||
|
{
|
||||||
|
return m_attachmentFolder != getDefaultConfig("global", "attachment_folder").toString();
|
||||||
|
}
|
||||||
|
|
||||||
inline bool VConfigManager::getEnableTrailingSpaceHighlight() const
|
inline bool VConfigManager::getEnableTrailingSpaceHighlight() const
|
||||||
{
|
{
|
||||||
return m_enableTrailingSpaceHighlight;
|
return m_enableTrailingSpaceHighlight;
|
||||||
|
@ -29,7 +29,9 @@ namespace DirConfig
|
|||||||
static const QString c_version = "version";
|
static const QString c_version = "version";
|
||||||
static const QString c_subDirectories = "sub_directories";
|
static const QString c_subDirectories = "sub_directories";
|
||||||
static const QString c_files = "files";
|
static const QString c_files = "files";
|
||||||
|
static const QString c_attachments = "attachments";
|
||||||
static const QString c_imageFolder = "image_folder";
|
static const QString c_imageFolder = "image_folder";
|
||||||
|
static const QString c_attachmentFolder = "attachment_folder";
|
||||||
static const QString c_recycleBinFolder = "recycle_bin_folder";
|
static const QString c_recycleBinFolder = "recycle_bin_folder";
|
||||||
static const QString c_name = "name";
|
static const QString c_name = "name";
|
||||||
static const QString c_createdTime = "created_time";
|
static const QString c_createdTime = "created_time";
|
||||||
|
@ -163,6 +163,13 @@ bool VDirectory::writeToConfig() const
|
|||||||
return writeToConfig(json);
|
return writeToConfig(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VDirectory::updateFileConfig(const VNoteFile *p_file)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_opened);
|
||||||
|
Q_UNUSED(p_file);
|
||||||
|
return writeToConfig();
|
||||||
|
}
|
||||||
|
|
||||||
bool VDirectory::writeToConfig(const QJsonObject &p_json) const
|
bool VDirectory::writeToConfig(const QJsonObject &p_json) const
|
||||||
{
|
{
|
||||||
return VConfigManager::writeDirectoryConfig(fetchPath(), p_json);
|
return VConfigManager::writeDirectoryConfig(fetchPath(), p_json);
|
||||||
|
@ -82,6 +82,9 @@ public:
|
|||||||
QString getNotebookName() const;
|
QString getNotebookName() const;
|
||||||
bool isExpanded() const;
|
bool isExpanded() const;
|
||||||
void setExpanded(bool p_expanded);
|
void setExpanded(bool p_expanded);
|
||||||
|
|
||||||
|
// Reorder files in m_files by index.
|
||||||
|
// Move [@p_first, @p_last] to @p_destStart.
|
||||||
void reorderFiles(int p_first, int p_last, int p_destStart);
|
void reorderFiles(int p_first, int p_last, int p_destStart);
|
||||||
|
|
||||||
// Serialize current instance to json.
|
// Serialize current instance to json.
|
||||||
@ -97,6 +100,9 @@ public:
|
|||||||
// notebook.
|
// notebook.
|
||||||
bool writeToConfig() const;
|
bool writeToConfig() const;
|
||||||
|
|
||||||
|
// Write the config of @p_file to config file.
|
||||||
|
bool updateFileConfig(const VNoteFile *p_file);
|
||||||
|
|
||||||
// Try to load file given relative path @p_filePath.
|
// Try to load file given relative path @p_filePath.
|
||||||
VNoteFile *tryLoadFile(QStringList &p_filePath);
|
VNoteFile *tryLoadFile(QStringList &p_filePath);
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#include "dialog/vorphanfileinfodialog.h"
|
#include "dialog/vorphanfileinfodialog.h"
|
||||||
#include "vsingleinstanceguard.h"
|
#include "vsingleinstanceguard.h"
|
||||||
#include "vnotefile.h"
|
#include "vnotefile.h"
|
||||||
|
#include "vbuttonwithwidget.h"
|
||||||
|
#include "vattachmentlist.h"
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
@ -124,7 +126,12 @@ void VMainWindow::setupUI()
|
|||||||
connect(notebookSelector, &VNotebookSelector::notebookUpdated,
|
connect(notebookSelector, &VNotebookSelector::notebookUpdated,
|
||||||
editArea, &VEditArea::handleNotebookUpdated);
|
editArea, &VEditArea::handleNotebookUpdated);
|
||||||
connect(notebookSelector, &VNotebookSelector::notebookCreated,
|
connect(notebookSelector, &VNotebookSelector::notebookCreated,
|
||||||
directoryTree, &VDirectoryTree::newRootDirectory);
|
directoryTree, [this](const QString &p_name, bool p_import) {
|
||||||
|
Q_UNUSED(p_name);
|
||||||
|
if (!p_import) {
|
||||||
|
directoryTree->newRootDirectory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
connect(fileList, &VFileList::fileClicked,
|
connect(fileList, &VFileList::fileClicked,
|
||||||
editArea, &VEditArea::openFile);
|
editArea, &VEditArea::openFile);
|
||||||
@ -203,6 +210,7 @@ void VMainWindow::initToolBar()
|
|||||||
initFileToolBar(iconSize);
|
initFileToolBar(iconSize);
|
||||||
initViewToolBar(iconSize);
|
initViewToolBar(iconSize);
|
||||||
initEditToolBar(iconSize);
|
initEditToolBar(iconSize);
|
||||||
|
initNoteToolBar(iconSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMainWindow::initViewToolBar(QSize p_iconSize)
|
void VMainWindow::initViewToolBar(QSize p_iconSize)
|
||||||
@ -319,6 +327,38 @@ void VMainWindow::initEditToolBar(QSize p_iconSize)
|
|||||||
setActionsEnabled(m_editToolBar, false);
|
setActionsEnabled(m_editToolBar, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMainWindow::initNoteToolBar(QSize p_iconSize)
|
||||||
|
{
|
||||||
|
QToolBar *noteToolBar = addToolBar(tr("Note Toolbar"));
|
||||||
|
noteToolBar->setObjectName("NoteToolBar");
|
||||||
|
noteToolBar->setMovable(false);
|
||||||
|
if (p_iconSize.isValid()) {
|
||||||
|
noteToolBar->setIconSize(p_iconSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
noteToolBar->addSeparator();
|
||||||
|
|
||||||
|
// Attachment.
|
||||||
|
m_attachmentList = new VAttachmentList(this);
|
||||||
|
m_attachmentBtn = new VButtonWithWidget(QIcon(":/resources/icons/attachment.svg"),
|
||||||
|
"",
|
||||||
|
m_attachmentList,
|
||||||
|
this);
|
||||||
|
m_attachmentBtn->setToolTip(tr("Attachments"));
|
||||||
|
m_attachmentBtn->setStatusTip(tr("Manage current note's attachments"));
|
||||||
|
m_attachmentBtn->setProperty("CornerBtn", true);
|
||||||
|
m_attachmentBtn->setFocusPolicy(Qt::NoFocus);
|
||||||
|
|
||||||
|
connect(m_attachmentBtn, &VButtonWithWidget::popupWidgetAboutToShow,
|
||||||
|
this, [this]() {
|
||||||
|
m_attachmentList->setFile(dynamic_cast<VNoteFile *>(m_curFile.data()));
|
||||||
|
});
|
||||||
|
|
||||||
|
m_attachmentBtn->setEnabled(false);
|
||||||
|
|
||||||
|
noteToolBar->addWidget(m_attachmentBtn);
|
||||||
|
}
|
||||||
|
|
||||||
void VMainWindow::initFileToolBar(QSize p_iconSize)
|
void VMainWindow::initFileToolBar(QSize p_iconSize)
|
||||||
{
|
{
|
||||||
QToolBar *fileToolBar = addToolBar(tr("Note"));
|
QToolBar *fileToolBar = addToolBar(tr("Note"));
|
||||||
@ -1511,6 +1551,14 @@ void VMainWindow::updateActionStateFromTabStatusChange(const VFile *p_file,
|
|||||||
deleteNoteAct->setEnabled(p_file && p_file->getType() == FileType::Note);
|
deleteNoteAct->setEnabled(p_file && p_file->getType() == FileType::Note);
|
||||||
noteInfoAct->setEnabled(p_file && !systemFile);
|
noteInfoAct->setEnabled(p_file && !systemFile);
|
||||||
|
|
||||||
|
m_attachmentBtn->setEnabled(p_file && p_file->getType() == FileType::Note);
|
||||||
|
if (m_attachmentBtn->isEnabled()
|
||||||
|
&& !dynamic_cast<const VNoteFile *>(p_file)->getAttachments().isEmpty()) {
|
||||||
|
m_attachmentBtn->setIcon(QIcon(":/resources/icons/attachment_full.svg"));
|
||||||
|
} else {
|
||||||
|
m_attachmentBtn->setIcon(QIcon(":/resources/icons/attachment.svg"));
|
||||||
|
}
|
||||||
|
|
||||||
m_insertImageAct->setEnabled(p_file && p_editMode);
|
m_insertImageAct->setEnabled(p_file && p_editMode);
|
||||||
|
|
||||||
setActionsEnabled(m_editToolBar, p_file && p_editMode);
|
setActionsEnabled(m_editToolBar, p_file && p_editMode);
|
||||||
|
@ -35,6 +35,8 @@ class VSingleInstanceGuard;
|
|||||||
class QTimer;
|
class QTimer;
|
||||||
class QSystemTrayIcon;
|
class QSystemTrayIcon;
|
||||||
class QShortcut;
|
class QShortcut;
|
||||||
|
class VButtonWithWidget;
|
||||||
|
class VAttachmentList;
|
||||||
|
|
||||||
class VMainWindow : public QMainWindow
|
class VMainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
@ -65,6 +67,9 @@ public:
|
|||||||
// Try to open @p_filePath as internal note.
|
// Try to open @p_filePath as internal note.
|
||||||
bool tryOpenInternalFile(const QString &p_filePath);
|
bool tryOpenInternalFile(const QString &p_filePath);
|
||||||
|
|
||||||
|
// Show a temporary message in status bar.
|
||||||
|
void showStatusMessage(const QString &p_msg);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void importNoteFromFile();
|
void importNoteFromFile();
|
||||||
void viewSettings();
|
void viewSettings();
|
||||||
@ -107,9 +112,6 @@ private slots:
|
|||||||
void printNote();
|
void printNote();
|
||||||
void exportAsPDF();
|
void exportAsPDF();
|
||||||
|
|
||||||
// Show a temporary message in status bar.
|
|
||||||
void showStatusMessage(const QString &p_msg);
|
|
||||||
|
|
||||||
// Handle Vim status updated.
|
// Handle Vim status updated.
|
||||||
void handleVimStatusUpdated(const VVim *p_vim);
|
void handleVimStatusUpdated(const VVim *p_vim);
|
||||||
|
|
||||||
@ -140,6 +142,8 @@ private:
|
|||||||
void initFileToolBar(QSize p_iconSize = QSize());
|
void initFileToolBar(QSize p_iconSize = QSize());
|
||||||
void initViewToolBar(QSize p_iconSize = QSize());
|
void initViewToolBar(QSize p_iconSize = QSize());
|
||||||
|
|
||||||
|
void initNoteToolBar(QSize p_iconSize = QSize());
|
||||||
|
|
||||||
// Init the Edit toolbar.
|
// Init the Edit toolbar.
|
||||||
void initEditToolBar(QSize p_iconSize = QSize());
|
void initEditToolBar(QSize p_iconSize = QSize());
|
||||||
|
|
||||||
@ -249,6 +253,12 @@ private:
|
|||||||
// Edit Toolbar.
|
// Edit Toolbar.
|
||||||
QToolBar *m_editToolBar;
|
QToolBar *m_editToolBar;
|
||||||
|
|
||||||
|
// Attachment button.
|
||||||
|
VButtonWithWidget *m_attachmentBtn;
|
||||||
|
|
||||||
|
// Attachment list.
|
||||||
|
VAttachmentList *m_attachmentList;
|
||||||
|
|
||||||
QVector<QPixmap> predefinedColorPixmaps;
|
QVector<QPixmap> predefinedColorPixmaps;
|
||||||
|
|
||||||
// Single instance guard.
|
// Single instance guard.
|
||||||
|
@ -291,9 +291,13 @@ void VMdEdit::clearUnusedImages()
|
|||||||
VConfirmDeletionDialog dialog(tr("Confirm Cleaning Up Unused Images"),
|
VConfirmDeletionDialog dialog(tr("Confirm Cleaning Up Unused Images"),
|
||||||
info,
|
info,
|
||||||
unusedImages,
|
unusedImages,
|
||||||
|
true,
|
||||||
|
g_config->getConfirmImagesCleanUp(),
|
||||||
|
true,
|
||||||
this);
|
this);
|
||||||
if (dialog.exec()) {
|
if (dialog.exec()) {
|
||||||
unusedImages = dialog.getConfirmedFiles();
|
unusedImages = dialog.getConfirmedFiles();
|
||||||
|
g_config->setConfirmImagesCleanUp(dialog.getAskAgainEnabled());
|
||||||
} else {
|
} else {
|
||||||
unusedImages.clear();
|
unusedImages.clear();
|
||||||
}
|
}
|
||||||
|
@ -123,5 +123,12 @@
|
|||||||
<file>utils/highlightjs/highlightjs-line-numbers.min.js</file>
|
<file>utils/highlightjs/highlightjs-line-numbers.min.js</file>
|
||||||
<file>resources/icons/recycle_bin.svg</file>
|
<file>resources/icons/recycle_bin.svg</file>
|
||||||
<file>resources/icons/empty_recycle_bin.svg</file>
|
<file>resources/icons/empty_recycle_bin.svg</file>
|
||||||
|
<file>resources/icons/attachment.svg</file>
|
||||||
|
<file>resources/icons/attachment_full.svg</file>
|
||||||
|
<file>resources/icons/add_attachment.svg</file>
|
||||||
|
<file>resources/icons/clear_attachment.svg</file>
|
||||||
|
<file>resources/icons/locate_attachment.svg</file>
|
||||||
|
<file>resources/icons/delete_attachment.svg</file>
|
||||||
|
<file>resources/icons/sort.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -24,7 +24,7 @@ VNotebook::~VNotebook()
|
|||||||
delete m_rootDir;
|
delete m_rootDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VNotebook::readConfig()
|
bool VNotebook::readConfigNotebook()
|
||||||
{
|
{
|
||||||
QJsonObject configJson = VConfigManager::readDirectoryConfig(m_path);
|
QJsonObject configJson = VConfigManager::readDirectoryConfig(m_path);
|
||||||
if (configJson.isEmpty()) {
|
if (configJson.isEmpty()) {
|
||||||
@ -44,6 +44,20 @@ bool VNotebook::readConfig()
|
|||||||
m_recycleBinFolder = it.value().toString();
|
m_recycleBinFolder = it.value().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [attachment_folder] section.
|
||||||
|
// SHOULD be processed at last.
|
||||||
|
it = configJson.find(DirConfig::c_attachmentFolder);
|
||||||
|
if (it != configJson.end()) {
|
||||||
|
m_attachmentFolder = it.value().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not allow empty attachment folder.
|
||||||
|
if (m_attachmentFolder.isEmpty()) {
|
||||||
|
m_attachmentFolder = g_config->getAttachmentFolder();
|
||||||
|
Q_ASSERT(!m_attachmentFolder.isEmpty());
|
||||||
|
writeConfigNotebook();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +68,9 @@ QJsonObject VNotebook::toConfigJsonNotebook() const
|
|||||||
// [image_folder] section.
|
// [image_folder] section.
|
||||||
json[DirConfig::c_imageFolder] = m_imageFolder;
|
json[DirConfig::c_imageFolder] = m_imageFolder;
|
||||||
|
|
||||||
|
// [attachment_folder] section.
|
||||||
|
json[DirConfig::c_attachmentFolder] = m_attachmentFolder;
|
||||||
|
|
||||||
// [recycle_bin_folder] section.
|
// [recycle_bin_folder] section.
|
||||||
json[DirConfig::c_recycleBinFolder] = m_recycleBinFolder;
|
json[DirConfig::c_recycleBinFolder] = m_recycleBinFolder;
|
||||||
|
|
||||||
@ -126,8 +143,11 @@ bool VNotebook::open()
|
|||||||
return m_rootDir->open();
|
return m_rootDir->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
VNotebook *VNotebook::createNotebook(const QString &p_name, const QString &p_path,
|
VNotebook *VNotebook::createNotebook(const QString &p_name,
|
||||||
bool p_import, const QString &p_imageFolder,
|
const QString &p_path,
|
||||||
|
bool p_import,
|
||||||
|
const QString &p_imageFolder,
|
||||||
|
const QString &p_attachmentFolder,
|
||||||
QObject *p_parent)
|
QObject *p_parent)
|
||||||
{
|
{
|
||||||
VNotebook *nb = new VNotebook(p_name, p_path, p_parent);
|
VNotebook *nb = new VNotebook(p_name, p_path, p_parent);
|
||||||
@ -136,10 +156,18 @@ VNotebook *VNotebook::createNotebook(const QString &p_name, const QString &p_pat
|
|||||||
// its image folder.
|
// its image folder.
|
||||||
nb->setImageFolder(p_imageFolder);
|
nb->setImageFolder(p_imageFolder);
|
||||||
|
|
||||||
|
// If @p_attachmentFolder is empty, use global configured folder.
|
||||||
|
QString attachmentFolder = p_attachmentFolder;
|
||||||
|
if (attachmentFolder.isEmpty()) {
|
||||||
|
attachmentFolder = g_config->getAttachmentFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
nb->setAttachmentFolder(attachmentFolder);
|
||||||
|
|
||||||
// Check if there alread exists a config file.
|
// Check if there alread exists a config file.
|
||||||
if (p_import && VConfigManager::directoryConfigExist(p_path)) {
|
if (p_import && VConfigManager::directoryConfigExist(p_path)) {
|
||||||
qDebug() << "import existing notebook";
|
qDebug() << "import existing notebook";
|
||||||
nb->readConfig();
|
nb->readConfigNotebook();
|
||||||
return nb;
|
return nb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +299,16 @@ const QString &VNotebook::getImageFolderConfig() const
|
|||||||
return m_imageFolder;
|
return m_imageFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString &VNotebook::getAttachmentFolder() const
|
||||||
|
{
|
||||||
|
return m_attachmentFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNotebook::setAttachmentFolder(const QString &p_attachmentFolder)
|
||||||
|
{
|
||||||
|
m_attachmentFolder = p_attachmentFolder;
|
||||||
|
}
|
||||||
|
|
||||||
bool VNotebook::isOpened() const
|
bool VNotebook::isOpened() const
|
||||||
{
|
{
|
||||||
return m_rootDir->isOpened();
|
return m_rootDir->isOpened();
|
||||||
|
@ -43,8 +43,11 @@ public:
|
|||||||
|
|
||||||
void rename(const QString &p_name);
|
void rename(const QString &p_name);
|
||||||
|
|
||||||
static VNotebook *createNotebook(const QString &p_name, const QString &p_path,
|
static VNotebook *createNotebook(const QString &p_name,
|
||||||
bool p_import, const QString &p_imageFolder,
|
const QString &p_path,
|
||||||
|
bool p_import,
|
||||||
|
const QString &p_imageFolder,
|
||||||
|
const QString &p_attachmentFolder,
|
||||||
QObject *p_parent = 0);
|
QObject *p_parent = 0);
|
||||||
|
|
||||||
static bool deleteNotebook(VNotebook *p_notebook, bool p_deleteFiles);
|
static bool deleteNotebook(VNotebook *p_notebook, bool p_deleteFiles);
|
||||||
@ -56,6 +59,11 @@ public:
|
|||||||
// Return m_imageFolder.
|
// Return m_imageFolder.
|
||||||
const QString &getImageFolderConfig() const;
|
const QString &getImageFolderConfig() const;
|
||||||
|
|
||||||
|
// Different from image folder. We could not change the attachment folder
|
||||||
|
// of a notebook once it has been created.
|
||||||
|
// Get the attachment folder for this notebook to use.
|
||||||
|
const QString &getAttachmentFolder() const;
|
||||||
|
|
||||||
// Return m_recycleBinFolder.
|
// Return m_recycleBinFolder.
|
||||||
const QString &getRecycleBinFolder() const;
|
const QString &getRecycleBinFolder() const;
|
||||||
|
|
||||||
@ -64,9 +72,10 @@ public:
|
|||||||
|
|
||||||
void setImageFolder(const QString &p_imageFolder);
|
void setImageFolder(const QString &p_imageFolder);
|
||||||
|
|
||||||
// Read configurations (excluding "sub_directories" and "files" section)
|
void setAttachmentFolder(const QString &p_attachmentFolder);
|
||||||
// from root directory config file.
|
|
||||||
bool readConfig();
|
// Read configurations (only notebook part) directly from root directory config file.
|
||||||
|
bool readConfigNotebook();
|
||||||
|
|
||||||
// Write configurations only related to notebook to root directory config file.
|
// Write configurations only related to notebook to root directory config file.
|
||||||
bool writeConfigNotebook() const;
|
bool writeConfigNotebook() const;
|
||||||
@ -95,6 +104,10 @@ private:
|
|||||||
// Otherwise, VNote will use the global configured folder.
|
// Otherwise, VNote will use the global configured folder.
|
||||||
QString m_imageFolder;
|
QString m_imageFolder;
|
||||||
|
|
||||||
|
// Folder name to store attachments.
|
||||||
|
// Should not be empty and changed once a notebook is created.
|
||||||
|
QString m_attachmentFolder;
|
||||||
|
|
||||||
// Folder name to store deleted files.
|
// Folder name to store deleted files.
|
||||||
// Could be relative or absolute.
|
// Could be relative or absolute.
|
||||||
QString m_recycleBinFolder;
|
QString m_recycleBinFolder;
|
||||||
|
@ -271,9 +271,10 @@ bool VNotebookSelector::newNotebook()
|
|||||||
createNotebook(dialog.getNameInput(),
|
createNotebook(dialog.getNameInput(),
|
||||||
dialog.getPathInput(),
|
dialog.getPathInput(),
|
||||||
dialog.isImportExistingNotebook(),
|
dialog.isImportExistingNotebook(),
|
||||||
dialog.getImageFolder());
|
dialog.getImageFolder(),
|
||||||
|
dialog.getAttachmentFolder());
|
||||||
|
|
||||||
emit notebookCreated();
|
emit notebookCreated(dialog.getNameInput(), dialog.isImportExistingNotebook());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,10 +284,12 @@ bool VNotebookSelector::newNotebook()
|
|||||||
void VNotebookSelector::createNotebook(const QString &p_name,
|
void VNotebookSelector::createNotebook(const QString &p_name,
|
||||||
const QString &p_path,
|
const QString &p_path,
|
||||||
bool p_import,
|
bool p_import,
|
||||||
const QString &p_imageFolder)
|
const QString &p_imageFolder,
|
||||||
|
const QString &p_attachmentFolder)
|
||||||
{
|
{
|
||||||
VNotebook *nb = VNotebook::createNotebook(p_name, p_path, p_import,
|
VNotebook *nb = VNotebook::createNotebook(p_name, p_path, p_import,
|
||||||
p_imageFolder, m_vnote);
|
p_imageFolder, p_attachmentFolder,
|
||||||
|
m_vnote);
|
||||||
if (!nb) {
|
if (!nb) {
|
||||||
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
||||||
tr("Fail to create notebook "
|
tr("Fail to create notebook "
|
||||||
@ -383,6 +386,7 @@ void VNotebookSelector::editNotebookInfo()
|
|||||||
m_notebooks, this);
|
m_notebooks, this);
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
|
bool configUpdated = false;
|
||||||
QString name = dialog.getName();
|
QString name = dialog.getName();
|
||||||
if (name != curName) {
|
if (name != curName) {
|
||||||
updated = true;
|
updated = true;
|
||||||
@ -393,8 +397,12 @@ void VNotebookSelector::editNotebookInfo()
|
|||||||
|
|
||||||
QString imageFolder = dialog.getImageFolder();
|
QString imageFolder = dialog.getImageFolder();
|
||||||
if (imageFolder != notebook->getImageFolderConfig()) {
|
if (imageFolder != notebook->getImageFolderConfig()) {
|
||||||
updated = true;
|
configUpdated = true;
|
||||||
notebook->setImageFolder(imageFolder);
|
notebook->setImageFolder(imageFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configUpdated) {
|
||||||
|
updated = true;
|
||||||
notebook->writeConfigNotebook();
|
notebook->writeConfigNotebook();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ signals:
|
|||||||
void notebookUpdated(const VNotebook *p_notebook);
|
void notebookUpdated(const VNotebook *p_notebook);
|
||||||
|
|
||||||
// Emit after creating a new notebook.
|
// Emit after creating a new notebook.
|
||||||
void notebookCreated();
|
void notebookCreated(const QString &p_name, bool p_import);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool newNotebook();
|
bool newNotebook();
|
||||||
@ -62,8 +62,10 @@ private:
|
|||||||
|
|
||||||
// If @p_import is true, we will use the existing config file.
|
// If @p_import is true, we will use the existing config file.
|
||||||
// If @p_imageFolder is empty, we will use the global one.
|
// If @p_imageFolder is empty, we will use the global one.
|
||||||
|
// If @p_attachmentFolder is empty, we will use the global one.
|
||||||
void createNotebook(const QString &p_name, const QString &p_path,
|
void createNotebook(const QString &p_name, const QString &p_path,
|
||||||
bool p_import, const QString &p_imageFolder);
|
bool p_import, const QString &p_imageFolder,
|
||||||
|
const QString &p_attachmentFolder);
|
||||||
|
|
||||||
void deleteNotebook(VNotebook *p_notebook, bool p_deleteFiles);
|
void deleteNotebook(VNotebook *p_notebook, bool p_deleteFiles);
|
||||||
void addNotebookItem(const QString &p_name);
|
void addNotebookItem(const QString &p_name);
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
@ -14,8 +16,12 @@ VNoteFile::VNoteFile(VDirectory *p_directory,
|
|||||||
FileType p_type,
|
FileType p_type,
|
||||||
bool p_modifiable,
|
bool p_modifiable,
|
||||||
QDateTime p_createdTimeUtc,
|
QDateTime p_createdTimeUtc,
|
||||||
QDateTime p_modifiedTimeUtc)
|
QDateTime p_modifiedTimeUtc,
|
||||||
: VFile(p_directory, p_name, p_type, p_modifiable, p_createdTimeUtc, p_modifiedTimeUtc)
|
const QString &p_attachmentFolder,
|
||||||
|
const QVector<VAttachment> &p_attachments)
|
||||||
|
: VFile(p_directory, p_name, p_type, p_modifiable, p_createdTimeUtc, p_modifiedTimeUtc),
|
||||||
|
m_attachmentFolder(p_attachmentFolder),
|
||||||
|
m_attachments(p_attachments)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +130,14 @@ VNoteFile *VNoteFile::fromJson(VDirectory *p_directory,
|
|||||||
FileType p_type,
|
FileType p_type,
|
||||||
bool p_modifiable)
|
bool p_modifiable)
|
||||||
{
|
{
|
||||||
|
// Attachments.
|
||||||
|
QJsonArray attachmentJson = p_json[DirConfig::c_attachments].toArray();
|
||||||
|
QVector<VAttachment> attachments;
|
||||||
|
for (int i = 0; i < attachmentJson.size(); ++i) {
|
||||||
|
QJsonObject attachmentItem = attachmentJson[i].toObject();
|
||||||
|
attachments.push_back(VAttachment(attachmentItem[DirConfig::c_name].toString()));
|
||||||
|
}
|
||||||
|
|
||||||
return new VNoteFile(p_directory,
|
return new VNoteFile(p_directory,
|
||||||
p_json[DirConfig::c_name].toString(),
|
p_json[DirConfig::c_name].toString(),
|
||||||
p_type,
|
p_type,
|
||||||
@ -131,7 +145,9 @@ VNoteFile *VNoteFile::fromJson(VDirectory *p_directory,
|
|||||||
QDateTime::fromString(p_json[DirConfig::c_createdTime].toString(),
|
QDateTime::fromString(p_json[DirConfig::c_createdTime].toString(),
|
||||||
Qt::ISODate),
|
Qt::ISODate),
|
||||||
QDateTime::fromString(p_json[DirConfig::c_modifiedTime].toString(),
|
QDateTime::fromString(p_json[DirConfig::c_modifiedTime].toString(),
|
||||||
Qt::ISODate));
|
Qt::ISODate),
|
||||||
|
p_json[DirConfig::c_attachmentFolder].toString(),
|
||||||
|
attachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject VNoteFile::toConfigJson() const
|
QJsonObject VNoteFile::toConfigJson() const
|
||||||
@ -140,6 +156,18 @@ QJsonObject VNoteFile::toConfigJson() const
|
|||||||
item[DirConfig::c_name] = m_name;
|
item[DirConfig::c_name] = m_name;
|
||||||
item[DirConfig::c_createdTime] = m_createdTimeUtc.toString(Qt::ISODate);
|
item[DirConfig::c_createdTime] = m_createdTimeUtc.toString(Qt::ISODate);
|
||||||
item[DirConfig::c_modifiedTime] = m_modifiedTimeUtc.toString(Qt::ISODate);
|
item[DirConfig::c_modifiedTime] = m_modifiedTimeUtc.toString(Qt::ISODate);
|
||||||
|
item[DirConfig::c_attachmentFolder] = m_attachmentFolder;
|
||||||
|
|
||||||
|
// Attachments.
|
||||||
|
QJsonArray attachmentJson;
|
||||||
|
for (int i = 0; i < m_attachments.size(); ++i) {
|
||||||
|
const VAttachment &item = m_attachments[i];
|
||||||
|
QJsonObject attachmentItem;
|
||||||
|
attachmentItem[DirConfig::c_name] = item.m_name;
|
||||||
|
attachmentJson.append(attachmentItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
item[DirConfig::c_attachments] = attachmentJson;
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@ -185,3 +213,150 @@ void VNoteFile::deleteInternalImages()
|
|||||||
qDebug() << "delete" << deleted << "images for" << m_name << fetchPath();
|
qDebug() << "delete" << deleted << "images for" << m_name << fetchPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VNoteFile::addAttachment(const QString &p_file)
|
||||||
|
{
|
||||||
|
if (p_file.isEmpty() || !QFileInfo::exists(p_file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString folderPath = fetchAttachmentFolderPath();
|
||||||
|
QString name = VUtils::fileNameFromPath(p_file);
|
||||||
|
Q_ASSERT(!name.isEmpty());
|
||||||
|
name = VUtils::getFileNameWithSequence(folderPath, name);
|
||||||
|
QString destPath = QDir(folderPath).filePath(name);
|
||||||
|
if (!VUtils::copyFile(p_file, destPath, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_attachments.push_back(VAttachment(name));
|
||||||
|
|
||||||
|
if (!getDirectory()->updateFileConfig(this)) {
|
||||||
|
qWarning() << "fail to update config of file" << m_name
|
||||||
|
<< "in directory" << fetchBasePath();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VNoteFile::fetchAttachmentFolderPath()
|
||||||
|
{
|
||||||
|
QString folderPath = QDir(fetchBasePath()).filePath(getNotebook()->getAttachmentFolder());
|
||||||
|
if (m_attachmentFolder.isEmpty()) {
|
||||||
|
m_attachmentFolder = VUtils::getRandomFileName(folderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
folderPath = QDir(folderPath).filePath(m_attachmentFolder);
|
||||||
|
if (!QFileInfo::exists(folderPath)) {
|
||||||
|
QDir dir;
|
||||||
|
if (!dir.mkpath(folderPath)) {
|
||||||
|
qWarning() << "fail to create attachment folder of notebook" << m_name << folderPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return folderPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VNoteFile::deleteAttachments()
|
||||||
|
{
|
||||||
|
if (m_attachments.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QString> attas;
|
||||||
|
for (int i = 0; i < m_attachments.size(); ++i) {
|
||||||
|
attas.push_back(m_attachments[i].m_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return deleteAttachments(attas);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VNoteFile::deleteAttachments(const QVector<QString> &p_names)
|
||||||
|
{
|
||||||
|
if (p_names.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir dir(fetchAttachmentFolderPath());
|
||||||
|
bool ret = true;
|
||||||
|
for (int i = 0; i < p_names.size(); ++i) {
|
||||||
|
int idx = findAttachment(p_names[i]);
|
||||||
|
if (idx == -1) {
|
||||||
|
ret = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_attachments.remove(idx);
|
||||||
|
if (!VUtils::deleteFile(getNotebook(), dir.filePath(p_names[i]), false)) {
|
||||||
|
ret = false;
|
||||||
|
qWarning() << "fail to delete attachment" << p_names[i]
|
||||||
|
<< "for note" << m_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getDirectory()->updateFileConfig(this)) {
|
||||||
|
qWarning() << "fail to update config of file" << m_name
|
||||||
|
<< "in directory" << fetchBasePath();
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VNoteFile::findAttachment(const QString &p_name, bool p_caseSensitive)
|
||||||
|
{
|
||||||
|
const QString name = p_caseSensitive ? p_name : p_name.toLower();
|
||||||
|
for (int i = 0; i < m_attachments.size(); ++i) {
|
||||||
|
QString attaName = p_caseSensitive ? m_attachments[i].m_name
|
||||||
|
: m_attachments[i].m_name.toLower();
|
||||||
|
if (name == attaName) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNoteFile::sortAttachments(QVector<int> p_sortedIdx)
|
||||||
|
{
|
||||||
|
V_ASSERT(m_opened);
|
||||||
|
V_ASSERT(p_sortedIdx.size() == m_attachments.size());
|
||||||
|
|
||||||
|
auto oriFiles = m_attachments;
|
||||||
|
|
||||||
|
for (int i = 0; i < p_sortedIdx.size(); ++i) {
|
||||||
|
m_attachments[i] = oriFiles[p_sortedIdx[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getDirectory()->updateFileConfig(this)) {
|
||||||
|
qWarning() << "fail to reorder files in config" << p_sortedIdx;
|
||||||
|
m_attachments = oriFiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VNoteFile::renameAttachment(const QString &p_oldName, const QString &p_newName)
|
||||||
|
{
|
||||||
|
int idx = findAttachment(p_oldName);
|
||||||
|
if (idx == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir dir(fetchAttachmentFolderPath());
|
||||||
|
if (!dir.rename(p_oldName, p_newName)) {
|
||||||
|
qWarning() << "fail to rename attachment file" << p_oldName << p_newName;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_attachments[idx].m_name = p_newName;
|
||||||
|
|
||||||
|
if (!getDirectory()->updateFileConfig(this)) {
|
||||||
|
qWarning() << "fail to rename attachment in config" << p_oldName << p_newName;
|
||||||
|
|
||||||
|
m_attachments[idx].m_name = p_oldName;
|
||||||
|
dir.rename(p_newName, p_oldName);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -1,11 +1,30 @@
|
|||||||
#ifndef VNOTEFILE_H
|
#ifndef VNOTEFILE_H
|
||||||
#define VNOTEFILE_H
|
#define VNOTEFILE_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
|
|
||||||
class VDirectory;
|
class VDirectory;
|
||||||
class VNotebook;
|
class VNotebook;
|
||||||
|
|
||||||
|
// Structure for a note attachment.
|
||||||
|
struct VAttachment
|
||||||
|
{
|
||||||
|
VAttachment()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VAttachment(const QString &p_name)
|
||||||
|
: m_name(p_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// File name of the attachment.
|
||||||
|
QString m_name;
|
||||||
|
};
|
||||||
|
|
||||||
class VNoteFile : public VFile
|
class VNoteFile : public VFile
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -15,7 +34,9 @@ public:
|
|||||||
FileType p_type,
|
FileType p_type,
|
||||||
bool p_modifiable,
|
bool p_modifiable,
|
||||||
QDateTime p_createdTimeUtc,
|
QDateTime p_createdTimeUtc,
|
||||||
QDateTime p_modifiedTimeUtc);
|
QDateTime p_modifiedTimeUtc,
|
||||||
|
const QString &p_attachmentFolder = "",
|
||||||
|
const QVector<VAttachment> &p_attachments = QVector<VAttachment>());
|
||||||
|
|
||||||
QString fetchPath() const Q_DECL_OVERRIDE;
|
QString fetchPath() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
@ -58,9 +79,52 @@ public:
|
|||||||
// Delete this file in disk as well as all its images/attachments.
|
// Delete this file in disk as well as all its images/attachments.
|
||||||
bool deleteFile();
|
bool deleteFile();
|
||||||
|
|
||||||
|
const QString &getAttachmentFolder() const;
|
||||||
|
|
||||||
|
const QVector<VAttachment> &getAttachments() const;
|
||||||
|
|
||||||
|
// Add @p_file as an attachment to this note.
|
||||||
|
bool addAttachment(const QString &p_file);
|
||||||
|
|
||||||
|
// Fetch attachment folder path.
|
||||||
|
// Will create it if it does not exist.
|
||||||
|
QString fetchAttachmentFolderPath();
|
||||||
|
|
||||||
|
// Delete all the attachments.
|
||||||
|
bool deleteAttachments();
|
||||||
|
|
||||||
|
// Delete attachments specified by @p_names.
|
||||||
|
bool deleteAttachments(const QVector<QString> &p_names);
|
||||||
|
|
||||||
|
// Reorder attachments in m_attachments by index.
|
||||||
|
void sortAttachments(QVector<int> p_sortedIdx);
|
||||||
|
|
||||||
|
// Return the index of @p_name in m_attachments.
|
||||||
|
// -1 if not found.
|
||||||
|
int findAttachment(const QString &p_name, bool p_caseSensitive = true);
|
||||||
|
|
||||||
|
bool renameAttachment(const QString &p_oldName, const QString &p_newName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Delete internal images of this file.
|
// Delete internal images of this file.
|
||||||
void deleteInternalImages();
|
void deleteInternalImages();
|
||||||
|
|
||||||
|
// Folder under the attachment folder of the notebook.
|
||||||
|
// Store all the attachments of current file.
|
||||||
|
QString m_attachmentFolder;
|
||||||
|
|
||||||
|
// Attachments.
|
||||||
|
QVector<VAttachment> m_attachments;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline const QString &VNoteFile::getAttachmentFolder() const
|
||||||
|
{
|
||||||
|
return m_attachmentFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QVector<VAttachment> &VNoteFile::getAttachments() const
|
||||||
|
{
|
||||||
|
return m_attachments;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // VNOTEFILE_H
|
#endif // VNOTEFILE_H
|
||||||
|
@ -100,35 +100,35 @@ void VVimIndicator::setupUI()
|
|||||||
|
|
||||||
m_modeLabel = new QLabel(this);
|
m_modeLabel = new QLabel(this);
|
||||||
|
|
||||||
m_regBtn = new VButtonWithWidget(QIcon(":/resources/icons/arrow_dropup.svg"),
|
|
||||||
"\"",
|
|
||||||
this);
|
|
||||||
m_regBtn->setToolTip(tr("Registers"));
|
|
||||||
m_regBtn->setProperty("StatusBtn", true);
|
|
||||||
m_regBtn->setFocusPolicy(Qt::NoFocus);
|
|
||||||
QTreeWidget *regTree = new QTreeWidget(this);
|
QTreeWidget *regTree = new QTreeWidget(this);
|
||||||
regTree->setColumnCount(2);
|
regTree->setColumnCount(2);
|
||||||
regTree->header()->setStretchLastSection(true);
|
regTree->header()->setStretchLastSection(true);
|
||||||
QStringList headers;
|
QStringList headers;
|
||||||
headers << tr("Register") << tr("Value");
|
headers << tr("Register") << tr("Value");
|
||||||
regTree->setHeaderLabels(headers);
|
regTree->setHeaderLabels(headers);
|
||||||
m_regBtn->setPopupWidget(regTree);
|
|
||||||
|
m_regBtn = new VButtonWithWidget("\"",
|
||||||
|
regTree,
|
||||||
|
this);
|
||||||
|
m_regBtn->setToolTip(tr("Registers"));
|
||||||
|
m_regBtn->setProperty("StatusBtn", true);
|
||||||
|
m_regBtn->setFocusPolicy(Qt::NoFocus);
|
||||||
connect(m_regBtn, &VButtonWithWidget::popupWidgetAboutToShow,
|
connect(m_regBtn, &VButtonWithWidget::popupWidgetAboutToShow,
|
||||||
this, &VVimIndicator::updateRegistersTree);
|
this, &VVimIndicator::updateRegistersTree);
|
||||||
|
|
||||||
m_markBtn = new VButtonWithWidget(QIcon(":/resources/icons/arrow_dropup.svg"),
|
|
||||||
"[]",
|
|
||||||
this);
|
|
||||||
m_markBtn->setToolTip(tr("Marks"));
|
|
||||||
m_markBtn->setProperty("StatusBtn", true);
|
|
||||||
m_markBtn->setFocusPolicy(Qt::NoFocus);
|
|
||||||
QTreeWidget *markTree = new QTreeWidget(this);
|
QTreeWidget *markTree = new QTreeWidget(this);
|
||||||
markTree->setColumnCount(4);
|
markTree->setColumnCount(4);
|
||||||
markTree->header()->setStretchLastSection(true);
|
markTree->header()->setStretchLastSection(true);
|
||||||
headers.clear();
|
headers.clear();
|
||||||
headers << tr("Mark") << tr("Line") << tr("Column") << tr("Text");
|
headers << tr("Mark") << tr("Line") << tr("Column") << tr("Text");
|
||||||
markTree->setHeaderLabels(headers);
|
markTree->setHeaderLabels(headers);
|
||||||
m_markBtn->setPopupWidget(markTree);
|
|
||||||
|
m_markBtn = new VButtonWithWidget("[]",
|
||||||
|
markTree,
|
||||||
|
this);
|
||||||
|
m_markBtn->setToolTip(tr("Marks"));
|
||||||
|
m_markBtn->setProperty("StatusBtn", true);
|
||||||
|
m_markBtn->setFocusPolicy(Qt::NoFocus);
|
||||||
connect(m_markBtn, &VButtonWithWidget::popupWidgetAboutToShow,
|
connect(m_markBtn, &VButtonWithWidget::popupWidgetAboutToShow,
|
||||||
this, &VVimIndicator::updateMarksTree);
|
this, &VVimIndicator::updateMarksTree);
|
||||||
|
|
||||||
|