From 2b78711eb4f961b21baf501a4b8046aaaa08cedf Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sat, 16 Sep 2017 14:48:39 +0800 Subject: [PATCH] confirm when cleaning up unused images Add config confirm_images_clean_up. --- src/dialog/vconfirmdeletiondialog.cpp | 163 ++++++++++++++++++++++++++ src/dialog/vconfirmdeletiondialog.h | 42 +++++++ src/dialog/vinsertimagedialog.cpp | 4 +- src/dialog/vnewfiledialog.cpp | 5 +- src/dialog/vnewnotebookdialog.cpp | 5 +- src/dialog/vnewnotebookdialog.h | 3 - src/resources/vnote.ini | 3 + src/src.pro | 6 +- src/vconfigmanager.cpp | 3 + src/vconfigmanager.h | 22 ++++ src/vedit.cpp | 3 - src/vmdedit.cpp | 36 ++++-- 12 files changed, 273 insertions(+), 22 deletions(-) create mode 100644 src/dialog/vconfirmdeletiondialog.cpp create mode 100644 src/dialog/vconfirmdeletiondialog.h diff --git a/src/dialog/vconfirmdeletiondialog.cpp b/src/dialog/vconfirmdeletiondialog.cpp new file mode 100644 index 00000000..0510ccbe --- /dev/null +++ b/src/dialog/vconfirmdeletiondialog.cpp @@ -0,0 +1,163 @@ +#include "vconfirmdeletiondialog.h" + +#include + +#include "utils/vutils.h" +#include "vconfigmanager.h" + +extern VConfigManager *g_config; + +class ConfirmItemWidget : public QWidget +{ +public: + explicit ConfirmItemWidget(QWidget *p_parent = NULL) + : QWidget(p_parent) + { + setupUI(); + } + + ConfirmItemWidget(bool p_checked, const QString &p_file, QWidget *p_parent = NULL) + : QWidget(p_parent) + { + setupUI(); + + m_checkBox->setChecked(p_checked); + m_fileLabel->setText(p_file); + } + + bool isChecked() const + { + return m_checkBox->isChecked(); + } + + QString getFile() const + { + return m_fileLabel->text(); + } + +private: + void setupUI() + { + m_checkBox = new QCheckBox; + m_fileLabel = new QLabel; + QHBoxLayout *mainLayout = new QHBoxLayout; + mainLayout->addWidget(m_checkBox); + mainLayout->addWidget(m_fileLabel); + mainLayout->addStretch(); + mainLayout->setContentsMargins(3, 0, 0, 0); + + setLayout(mainLayout); + } + + QCheckBox *m_checkBox; + QLabel *m_fileLabel; +}; + +VConfirmDeletionDialog::VConfirmDeletionDialog(const QString &p_title, + const QString &p_info, + const QVector &p_files, + QWidget *p_parent) + : QDialog(p_parent) +{ + setupUI(p_title, p_info); + + initFileItems(p_files); +} + +void VConfirmDeletionDialog::setupUI(const QString &p_title, const QString &p_info) +{ + QLabel *infoLabel = new QLabel(p_info); + m_listWidget = new QListWidget(); + connect(m_listWidget, &QListWidget::currentRowChanged, + this, &VConfirmDeletionDialog::currentFileChanged); + + m_previewer = new QLabel(); + + m_askAgainCB = new QCheckBox(tr("Just delete them and do not ask for confirmation again")); + m_askAgainCB->setChecked(!g_config->getConfirmImagesCleanUp()); + + // Ok is the default button. + m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(m_btnBox, &QDialogButtonBox::accepted, + this, [this]() { + g_config->setConfirmImagesCleanUp(!m_askAgainCB->isChecked()); + QDialog::accept(); + }); + connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + QHBoxLayout *midLayout = new QHBoxLayout; + midLayout->addWidget(m_listWidget); + midLayout->addStretch(); + midLayout->addWidget(m_previewer); + midLayout->addStretch(); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(infoLabel); + mainLayout->addWidget(m_askAgainCB); + mainLayout->addWidget(m_btnBox); + mainLayout->addLayout(midLayout); + + setLayout(mainLayout); + setWindowTitle(p_title); +} + +QVector VConfirmDeletionDialog::getConfirmedFiles() const +{ + QVector files; + + for (int i = 0; i < m_listWidget->count(); ++i) { + ConfirmItemWidget *widget = getItemWidget(m_listWidget->item(i)); + if (widget->isChecked()) { + files.push_back(widget->getFile()); + } + } + + return files; +} + +void VConfirmDeletionDialog::initFileItems(const QVector &p_files) +{ + m_listWidget->clear(); + + for (int i = 0; i < p_files.size(); ++i) { + ConfirmItemWidget *itemWidget = new ConfirmItemWidget(true, + p_files[i], + this); + QListWidgetItem *item = new QListWidgetItem(); + QSize size = itemWidget->sizeHint(); + size.setHeight(size.height() * 2); + item->setSizeHint(size); + + m_listWidget->addItem(item); + m_listWidget->setItemWidget(item, itemWidget); + } + + m_listWidget->setMinimumSize(m_listWidget->sizeHint()); + m_listWidget->setCurrentRow(-1); + m_listWidget->setCurrentRow(0); +} + +void VConfirmDeletionDialog::currentFileChanged(int p_row) +{ + bool succeed = false; + if (p_row > -1) { + ConfirmItemWidget *widget = getItemWidget(m_listWidget->item(p_row)); + if (widget) { + QPixmap image(widget->getFile()); + if (!image.isNull()) { + int width = 512 * VUtils::calculateScaleFactor(); + QSize previewSize(width, width); + m_previewer->setPixmap(image.scaled(previewSize)); + succeed = true; + } + } + } + + m_previewer->setVisible(succeed); +} + +ConfirmItemWidget *VConfirmDeletionDialog::getItemWidget(QListWidgetItem *p_item) const +{ + QWidget *wid = m_listWidget->itemWidget(p_item); + return dynamic_cast(wid); +} diff --git a/src/dialog/vconfirmdeletiondialog.h b/src/dialog/vconfirmdeletiondialog.h new file mode 100644 index 00000000..0a73ce0b --- /dev/null +++ b/src/dialog/vconfirmdeletiondialog.h @@ -0,0 +1,42 @@ +#ifndef VCONFIRMDELETIONDIALOG_H +#define VCONFIRMDELETIONDIALOG_H + +#include +#include + +class QLabel; +class QPushButton; +class QDialogButtonBox; +class QListWidget; +class QListWidgetItem; +class ConfirmItemWidget; +class QCheckBox; + +class VConfirmDeletionDialog : public QDialog +{ + Q_OBJECT +public: + VConfirmDeletionDialog(const QString &p_title, + const QString &p_info, + const QVector &p_files, + QWidget *p_parent = 0); + + QVector getConfirmedFiles() const; + +private slots: + void currentFileChanged(int p_row); + +private: + void setupUI(const QString &p_title, const QString &p_info); + + void initFileItems(const QVector &p_files); + + ConfirmItemWidget *getItemWidget(QListWidgetItem *p_item) const; + + QListWidget *m_listWidget; + QLabel *m_previewer; + QDialogButtonBox *m_btnBox; + QCheckBox *m_askAgainCB; +}; + +#endif // VCONFIRMDELETIONDIALOG_H diff --git a/src/dialog/vinsertimagedialog.cpp b/src/dialog/vinsertimagedialog.cpp index debe636b..71803465 100644 --- a/src/dialog/vinsertimagedialog.cpp +++ b/src/dialog/vinsertimagedialog.cpp @@ -2,6 +2,7 @@ #include #include #include "vinsertimagedialog.h" +#include "utils/vutils.h" VInsertImageDialog::VInsertImageDialog(const QString &title, const QString &defaultImageTitle, const QString &defaultPath, QWidget *parent) @@ -112,7 +113,8 @@ void VInsertImageDialog::handleBrowseBtnClicked() void VInsertImageDialog::setImage(const QImage &image) { Q_ASSERT(!image.isNull()); - QSize previewSize(256, 256); + int width = 512 * VUtils::calculateScaleFactor(); + QSize previewSize(width, width); if (!this->image) { this->image = new QImage(image); } else { diff --git a/src/dialog/vnewfiledialog.cpp b/src/dialog/vnewfiledialog.cpp index 5b52fffc..6ef321ff 100644 --- a/src/dialog/vnewfiledialog.cpp +++ b/src/dialog/vnewfiledialog.cpp @@ -45,6 +45,8 @@ void VNewFileDialog::setupUI() topLayout->addRow(nameLabel, nameEdit); topLayout->addWidget(m_insertTitleCB); + nameEdit->setMinimumWidth(m_insertTitleCB->sizeHint().width()); + m_warnLabel = new QLabel(); m_warnLabel->setWordWrap(true); m_warnLabel->hide(); @@ -54,9 +56,6 @@ void VNewFileDialog::setupUI() connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok); - nameEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); - QVBoxLayout *mainLayout = new QVBoxLayout(); if (infoLabel) { mainLayout->addWidget(infoLabel); diff --git a/src/dialog/vnewnotebookdialog.cpp b/src/dialog/vnewnotebookdialog.cpp index 0c484e3c..d385bff9 100644 --- a/src/dialog/vnewnotebookdialog.cpp +++ b/src/dialog/vnewnotebookdialog.cpp @@ -11,7 +11,7 @@ VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info const QString &defaultName, const QString &defaultPath, const QVector &p_notebooks, QWidget *parent) - : QDialog(parent), infoLabel(NULL), + : QDialog(parent), title(title), info(info), defaultName(defaultName), defaultPath(defaultPath), m_importNotebook(false), m_manualPath(false), m_manualName(false), m_notebooks(p_notebooks) @@ -27,12 +27,13 @@ VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info void VNewNotebookDialog::setupUI() { + QLabel *infoLabel = NULL; if (!info.isEmpty()) { infoLabel = new QLabel(info); infoLabel->setWordWrap(true); } - nameLabel = new QLabel(tr("Notebook &name:")); + QLabel *nameLabel = new QLabel(tr("Notebook &name:")); nameEdit = new QLineEdit(defaultName); nameLabel->setBuddy(nameEdit); diff --git a/src/dialog/vnewnotebookdialog.h b/src/dialog/vnewnotebookdialog.h index 823c212e..e470330b 100644 --- a/src/dialog/vnewnotebookdialog.h +++ b/src/dialog/vnewnotebookdialog.h @@ -7,7 +7,6 @@ class QLabel; class QLineEdit; class QPushButton; -class QString; class QDialogButtonBox; class VNotebook; @@ -49,8 +48,6 @@ private: // Returns true if name or path is modified. bool autoComplete(); - QLabel *infoLabel; - QLabel *nameLabel; QLineEdit *nameEdit; QLineEdit *pathEdit; QPushButton *browseBtn; diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index b531d149..a43a6a9a 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -110,6 +110,9 @@ markdownit_opt_linkify=true ; Default name of the recycle bin of notebook recycle_bin_folder=_v_recycle_bin +; Confirm before deleting unused images +confirm_images_clean_up=true + [session] tools_dock_checked=true diff --git a/src/src.pro b/src/src.pro index c356cdd9..16578117 100644 --- a/src/src.pro +++ b/src/src.pro @@ -73,7 +73,8 @@ SOURCES += main.cpp\ dialog/vupdater.cpp \ dialog/vorphanfileinfodialog.cpp \ vtextblockdata.cpp \ - utils/vpreviewutils.cpp + utils/vpreviewutils.cpp \ + dialog/vconfirmdeletiondialog.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -134,7 +135,8 @@ HEADERS += vmainwindow.h \ dialog/vupdater.h \ dialog/vorphanfileinfodialog.h \ vtextblockdata.h \ - utils/vpreviewutils.h + utils/vpreviewutils.h \ + dialog/vconfirmdeletiondialog.h RESOURCES += \ vnote.qrc \ diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index fad62c66..68910e77 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -200,6 +200,9 @@ void VConfigManager::initialize() m_recycleBinFolder = getConfigFromSettings("global", "recycle_bin_folder").toString(); + + m_confirmImagesCleanUp = getConfigFromSettings("global", + "confirm_images_clean_up").toBool(); } void VConfigManager::readPredefinedColorsFromSettings() diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index 30ec89f6..cde163c0 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -266,6 +266,9 @@ public: const QString &getRecycleBinFolder() const; + bool getConfirmImagesCleanUp() const; + void setConfirmImagesCleanUp(bool p_enabled); + // Return the configured key sequence of @p_operation. // Return empty if there is no corresponding config. QString getShortcutKeySequence(const QString &p_operation) const; @@ -536,6 +539,9 @@ private: // Default name of the recycle bin folder of notebook. QString m_recycleBinFolder; + // Confirm before deleting unused images. + bool m_confirmImagesCleanUp; + // The name of the config file in each directory, obsolete. // Use c_dirConfigFile instead. static const QString c_obsoleteDirConfigFile; @@ -1387,4 +1393,20 @@ inline const QString &VConfigManager::getRecycleBinFolder() const return m_recycleBinFolder; } +inline bool VConfigManager::getConfirmImagesCleanUp() const +{ + return m_confirmImagesCleanUp; +} + +inline void VConfigManager::setConfirmImagesCleanUp(bool p_enabled) +{ + if (m_confirmImagesCleanUp == p_enabled) { + return; + } + + m_confirmImagesCleanUp = p_enabled; + setConfigToSettings("global", + "confirm_images_clean_up", + m_confirmImagesCleanUp); +} #endif // VCONFIGMANAGER_H diff --git a/src/vedit.cpp b/src/vedit.cpp index 7045254e..3199b672 100644 --- a/src/vedit.cpp +++ b/src/vedit.cpp @@ -176,9 +176,6 @@ bool VEdit::isModified() const void VEdit::setModified(bool p_modified) { document()->setModified(p_modified); - if (m_file) { - m_file->setModified(p_modified); - } } void VEdit::insertImage() diff --git a/src/vmdedit.cpp b/src/vmdedit.cpp index 76d986f3..1f358008 100644 --- a/src/vmdedit.cpp +++ b/src/vmdedit.cpp @@ -10,6 +10,7 @@ #include "utils/veditutils.h" #include "utils/vpreviewutils.h" #include "dialog/vselectdialog.h" +#include "dialog/vconfirmdeletiondialog.h" #include "vimagepreviewer.h" #include "vtextblockdata.h" @@ -238,6 +239,8 @@ void VMdEdit::clearUnusedImages() QVector images = VUtils::fetchImagesFromMarkdownFile(m_file, ImageLink::LocalRelativeInternal); + QVector unusedImages; + if (!m_insertedImages.isEmpty()) { for (int i = 0; i < m_insertedImages.size(); ++i) { const ImageLink &link = m_insertedImages[i]; @@ -255,11 +258,7 @@ void VMdEdit::clearUnusedImages() // This inserted image is no longer in the file. if (j == images.size()) { - if (!VUtils::deleteFile(m_file->getNotebook(), link.m_path, false)) { - qWarning() << "fail to delete unused inserted image" << link.m_path; - } else { - qDebug() << "delete unused inserted image" << link.m_path; - } + unusedImages.push_back(link.m_path); } } @@ -280,10 +279,31 @@ void VMdEdit::clearUnusedImages() // Original local relative image is no longer in the file. if (j == images.size()) { - if (!VUtils::deleteFile(m_file->getNotebook(), link.m_path, false)) { - qWarning() << "fail to delete unused original image" << link.m_path; + unusedImages.push_back(link.m_path); + } + } + + if (!unusedImages.isEmpty()) { + if (g_config->getConfirmImagesCleanUp()) { + QString info = tr("Following images seems not to be used in this note anymore. " + "Please confirm the deletion of these images.
" + "Click \"Cancel\" to leave them untouched."); + VConfirmDeletionDialog dialog(tr("Confirm Cleaning Up Unused Images"), + info, + unusedImages, + this); + if (dialog.exec()) { + unusedImages = dialog.getConfirmedFiles(); } else { - qDebug() << "delete unused original image" << link.m_path; + unusedImages.clear(); + } + } + + for (int i = 0; i < unusedImages.size(); ++i) { + if (!VUtils::deleteFile(m_file->getNotebook(), unusedImages[i], false)) { + qWarning() << "fail to delete unused original image" << unusedImages[i]; + } else { + qDebug() << "delete unused image" << unusedImages[i]; } } }