From 964dfbb085e1c1d1722f5de30682119fa0dc5cba Mon Sep 17 00:00:00 2001 From: Le Tan Date: Tue, 26 Oct 2021 08:09:50 +0800 Subject: [PATCH] remove recycle bin node --- src/core/exception.h | 2 + src/core/notebook/bundlenotebook.cpp | 5 + src/core/notebook/bundlenotebook.h | 2 + src/core/notebook/node.cpp | 20 +- src/core/notebook/node.h | 3 +- src/core/notebook/notebook.cpp | 130 +++---- src/core/notebook/notebook.h | 23 +- src/core/notebookbackend/inotebookbackend.h | 4 +- .../notebookbackend/localnotebookbackend.cpp | 8 +- .../notebookbackend/localnotebookbackend.h | 4 +- .../bundlenotebookconfigmgr.cpp | 6 +- .../notebookconfigmgr/inotebookconfigmgr.h | 2 + .../notebookconfigmgr/vxnotebookconfigmgr.cpp | 165 +++++---- .../notebookconfigmgr/vxnotebookconfigmgr.h | 26 +- .../vxnotebookconfigmgrfactory.cpp | 17 +- .../vxnotebookconfigmgrfactory.h | 6 + src/core/sessionconfig.cpp | 7 - src/core/widgetconfig.cpp | 12 - src/core/widgetconfig.h | 5 - src/data/core/vnotex.json | 1 - src/search/searcher.cpp | 5 - src/utils/fileutils.cpp | 2 +- src/widgets/dialogs/managenotebooksdialog.cpp | 10 +- src/widgets/dialogs/newfolderdialog.cpp | 4 +- src/widgets/dialogs/newnotedialog.cpp | 4 +- src/widgets/mainwindow.cpp | 2 +- src/widgets/notebookexplorer.cpp | 76 ++--- src/widgets/notebookexplorer.h | 2 + src/widgets/notebooknodeexplorer.cpp | 316 ++++-------------- src/widgets/notebooknodeexplorer.h | 17 +- src/widgets/searchpanel.cpp | 2 +- src/widgets/tagexplorer.cpp | 5 - 32 files changed, 349 insertions(+), 544 deletions(-) diff --git a/src/core/exception.h b/src/core/exception.h index 1f86b8d9..e293c278 100644 --- a/src/core/exception.h +++ b/src/core/exception.h @@ -23,6 +23,8 @@ namespace vnotex FailToRemoveDir, FileMissingOnDisk, EssentialFileMissing, + FileExistsOnCreate, + DirExistsOnCreate, InvalidArgument }; diff --git a/src/core/notebook/bundlenotebook.cpp b/src/core/notebook/bundlenotebook.cpp index dd3d1884..9ebe13cd 100644 --- a/src/core/notebook/bundlenotebook.cpp +++ b/src/core/notebook/bundlenotebook.cpp @@ -254,3 +254,8 @@ TagI *BundleNotebook::tag() { return getTagMgr(); } + +int BundleNotebook::getConfigVersion() const +{ + return m_configVersion; +} diff --git a/src/core/notebook/bundlenotebook.h b/src/core/notebook/bundlenotebook.h index 7c8956e5..e60a5a93 100644 --- a/src/core/notebook/bundlenotebook.h +++ b/src/core/notebook/bundlenotebook.h @@ -41,6 +41,8 @@ namespace vnotex TagI *tag() Q_DECL_OVERRIDE; + int getConfigVersion() const; + // HistoryI. public: HistoryI *history() Q_DECL_OVERRIDE; diff --git a/src/core/notebook/node.cpp b/src/core/notebook/node.cpp index db9bf6fa..9f8686e8 100644 --- a/src/core/notebook/node.cpp +++ b/src/core/notebook/node.cpp @@ -109,6 +109,24 @@ bool Node::containsChild(const QSharedPointer &p_node) const return m_children.indexOf(p_node) != -1; } +bool Node::isLegalNameForNewChild(const QString &p_name) const +{ + if (p_name.isEmpty()) { + return false; + } + + auto mgr = getConfigMgr(); + if (mgr->isBuiltInFile(this, p_name) || mgr->isBuiltInFolder(this, p_name)) { + return false; + } + + if (containsChild(p_name, false)) { + return false; + } + + return true; +} + QSharedPointer Node::findChild(const QString &p_name, bool p_caseSensitive) const { auto targetName = p_caseSensitive ? p_name : p_name.toLower(); @@ -356,7 +374,7 @@ bool Node::canRename(const QString &p_newName) const } } - if (m_parent->containsChild(p_newName, false)) { + if (!m_parent->isLegalNameForNewChild(p_newName)) { return false; } diff --git a/src/core/notebook/node.h b/src/core/notebook/node.h index aaa4be5d..eb490816 100644 --- a/src/core/notebook/node.h +++ b/src/core/notebook/node.h @@ -36,7 +36,6 @@ namespace vnotex enum Use { Normal, - RecycleBin, Root }; @@ -115,6 +114,8 @@ namespace vnotex // Case sensitive. bool containsContentChild(const QString &p_name) const; + bool isLegalNameForNewChild(const QString &p_name) const; + void addChild(const QSharedPointer &p_node); void insertChild(int p_idx, const QSharedPointer &p_node); diff --git a/src/core/notebook/notebook.cpp b/src/core/notebook/notebook.cpp index 62af3f34..b316e222 100644 --- a/src/core/notebook/notebook.cpp +++ b/src/core/notebook/notebook.cpp @@ -16,6 +16,8 @@ const QString Notebook::c_defaultAttachmentFolder = QStringLiteral("vx_attachmen const QString Notebook::c_defaultImageFolder = QStringLiteral("vx_images"); +const QString Notebook::c_defaultRecycleBinFolder = QStringLiteral("vx_recycle_bin"); + static vnotex::ID generateNotebookID() { static vnotex::ID id = Notebook::InvalidId; @@ -44,6 +46,9 @@ Notebook::Notebook(const NotebookParameters &p_paras, if (m_attachmentFolder.isEmpty()) { m_attachmentFolder = c_defaultAttachmentFolder; } + if (m_recycleBinFolder.isEmpty()) { + m_recycleBinFolder = c_defaultRecycleBinFolder; + } m_configMgr->setNotebook(this); } @@ -151,6 +156,28 @@ const QString &Notebook::getAttachmentFolder() const return m_attachmentFolder; } +const QString &Notebook::getRecycleBinFolder() const +{ + return m_recycleBinFolder; +} + +QString Notebook::getRecycleBinFolderAbsolutePath() const +{ + if (QDir::isAbsolutePath(m_recycleBinFolder)) { + if (!QFileInfo::exists(m_recycleBinFolder)) { + QDir dir(m_recycleBinFolder); + dir.mkpath(m_recycleBinFolder); + } + return m_recycleBinFolder; + } else { + auto folderPath = getBackend()->getFullPath(m_recycleBinFolder); + if (!getBackend()->exists(m_recycleBinFolder)) { + getBackend()->makePath(m_recycleBinFolder); + } + return folderPath; + } +} + const QSharedPointer &Notebook::getBackend() const { return m_backend; @@ -176,23 +203,6 @@ const QSharedPointer &Notebook::getRootNode() const return m_root; } -QSharedPointer Notebook::getRecycleBinNode() const -{ - auto root = getRootNode(); - const auto &children = root->getChildrenRef(); - auto it = std::find_if(children.begin(), - children.end(), - [this](const QSharedPointer &p_node) { - return isRecycleBinNode(p_node.data()); - }); - - if (it != children.end()) { - return *it; - } - - return nullptr; -} - QSharedPointer Notebook::newNode(Node *p_parent, Node::Flags p_flags, const QString &p_name, @@ -259,27 +269,6 @@ void Notebook::removeNode(Node *p_node, bool p_force, bool p_configOnly) removeNode(p_node->sharedFromThis(), p_force, p_configOnly); } -bool Notebook::isRecycleBinNode(const Node *p_node) const -{ - return p_node && p_node->getUse() == Node::Use::RecycleBin; -} - -bool Notebook::isNodeInRecycleBin(const Node *p_node) const -{ - if (p_node) { - p_node = p_node->getParent(); - while (p_node) { - if (isRecycleBinNode(p_node)) { - return true; - } - - p_node = p_node->getParent(); - } - } - - return false; -} - void Notebook::moveNodeToRecycleBin(Node *p_node) { moveNodeToRecycleBin(p_node->sharedFromThis()); @@ -288,59 +277,41 @@ void Notebook::moveNodeToRecycleBin(Node *p_node) void Notebook::moveNodeToRecycleBin(const QSharedPointer &p_node) { Q_ASSERT(p_node && !p_node->isRoot()); - auto destNode = getOrCreateRecycleBinDateNode(); - copyNodeAsChildOf(p_node, destNode.data(), true); + m_configMgr->removeNodeToFolder(p_node, getOrCreateRecycleBinDateFolder()); } -QSharedPointer Notebook::getOrCreateRecycleBinDateNode() +QString Notebook::getOrCreateRecycleBinDateFolder() { // Name after date. - auto dateNodeName = QDate::currentDate().toString(QStringLiteral("yyyyMMdd")); - - auto recycleBinNode = getRecycleBinNode(); - auto dateNode = recycleBinNode->findChild(dateNodeName, - FileUtils::isPlatformNameCaseSensitive()); - if (!dateNode) { - // Create a date node. - dateNode = newNode(recycleBinNode.data(), Node::Flag::Container, dateNodeName); + auto dateFolderName = QDate::currentDate().toString(QStringLiteral("yyyyMMdd")); + auto folderPath = PathUtils::concatenateFilePath(getRecycleBinFolder(), dateFolderName); + if (QDir::isAbsolutePath(folderPath)) { + qDebug() << "using absolute recycle bin folder" << folderPath; + QDir dir(folderPath); + if (dir.exists()) { + dir.mkpath(folderPath); + } + } else { + if (!getBackend()->exists(folderPath)) { + getBackend()->makePath(folderPath); + } } - return dateNode; -} - -void Notebook::emptyNode(const Node *p_node, bool p_force) -{ - // Empty the children. - auto children = p_node->getChildren(); - for (const auto &child : children) { - removeNode(child, p_force); - } + return folderPath; } void Notebook::moveFileToRecycleBin(const QString &p_filePath) { - auto node = getOrCreateRecycleBinDateNode(); - auto destFilePath = PathUtils::concatenateFilePath(node->fetchPath(), - PathUtils::fileName(p_filePath)); + auto destFilePath = PathUtils::concatenateFilePath(getOrCreateRecycleBinDateFolder(), PathUtils::fileName(p_filePath)); destFilePath = getBackend()->renameIfExistsCaseInsensitive(destFilePath); - m_backend->copyFile(p_filePath, destFilePath); - - getBackend()->removeFile(p_filePath); - - emit nodeUpdated(node.data()); + m_backend->copyFile(p_filePath, destFilePath, true); } void Notebook::moveDirToRecycleBin(const QString &p_dirPath) { - auto node = getOrCreateRecycleBinDateNode(); - auto destDirPath = PathUtils::concatenateFilePath(node->fetchPath(), - PathUtils::fileName(p_dirPath)); + auto destDirPath = PathUtils::concatenateFilePath(getOrCreateRecycleBinDateFolder(), PathUtils::fileName(p_dirPath)); destDirPath = getBackend()->renameIfExistsCaseInsensitive(destDirPath); - m_backend->copyDir(p_dirPath, destDirPath); - - getBackend()->removeDir(p_dirPath); - - emit nodeUpdated(node.data()); + m_backend->copyDir(p_dirPath, destDirPath, true); } QSharedPointer Notebook::addAsNode(Node *p_parent, @@ -416,3 +387,12 @@ TagI *Notebook::tag() { return nullptr; } + +void Notebook::emptyRecycleBin() +{ + QDir dir(getRecycleBinFolderAbsolutePath()); + auto children = dir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); + for (const auto &child : children) { + FileUtils::removeDir(dir.filePath(child)); + } +} diff --git a/src/core/notebook/notebook.h b/src/core/notebook/notebook.h index fe4d329e..4ec3d11f 100644 --- a/src/core/notebook/notebook.h +++ b/src/core/notebook/notebook.h @@ -61,6 +61,10 @@ namespace vnotex const QString &getAttachmentFolder() const; + const QString &getRecycleBinFolder() const; + + QString getRecycleBinFolderAbsolutePath() const; + const QDateTime &getCreatedTimeUtc() const; const QSharedPointer &getBackend() const; @@ -71,8 +75,6 @@ namespace vnotex const QSharedPointer &getRootNode() const; - QSharedPointer getRecycleBinNode() const; - QSharedPointer newNode(Node *p_parent, Node::Flags p_flags, const QString &p_name, @@ -116,17 +118,11 @@ namespace vnotex // Move @p_dirPath to the recycle bin, without adding it as a child node. void moveDirToRecycleBin(const QString &p_dirPath); + virtual void emptyRecycleBin(); + // Remove all files of this notebook from disk. virtual void remove() = 0; - bool isRecycleBinNode(const Node *p_node) const; - - bool isNodeInRecycleBin(const Node *p_node) const; - - // Remove all children node of @p_node. - // @p_force: if true, just delete all folders and files under @p_node. - void emptyNode(const Node *p_node, bool p_force = false); - // Whether @p_name is a built-in file under @p_node. bool isBuiltInFile(const Node *p_node, const QString &p_name) const; @@ -150,6 +146,8 @@ namespace vnotex static const QString c_defaultImageFolder; + static const QString c_defaultRecycleBinFolder; + public: // Return null if history is not suported. virtual HistoryI *history(); @@ -168,7 +166,7 @@ namespace vnotex virtual void initializeInternal() = 0; private: - QSharedPointer getOrCreateRecycleBinDateNode(); + QString getOrCreateRecycleBinDateFolder(); bool m_initialized = false; @@ -196,6 +194,9 @@ namespace vnotex // Name of the folder to hold attachments. QString m_attachmentFolder; + // Name or path of the folder to hold deleted files. + QString m_recycleBinFolder; + QDateTime m_createdTimeUtc; // Backend for file access and synchronization. diff --git a/src/core/notebookbackend/inotebookbackend.h b/src/core/notebookbackend/inotebookbackend.h index 5ba0f7a8..a3ed2960 100644 --- a/src/core/notebookbackend/inotebookbackend.h +++ b/src/core/notebookbackend/inotebookbackend.h @@ -83,13 +83,13 @@ namespace vnotex // Copy @p_filePath to @p_destPath. // @p_filePath could be outside notebook. - virtual void copyFile(const QString &p_filePath, const QString &p_destPath) = 0; + virtual void copyFile(const QString &p_filePath, const QString &p_destPath, bool p_move = false) = 0; // Delete @p_filePath from disk. virtual void removeFile(const QString &p_filePath) = 0; // Copy @p_dirPath to as @p_destPath. - virtual void copyDir(const QString &p_dirPath, const QString &p_destPath) = 0; + virtual void copyDir(const QString &p_dirPath, const QString &p_destPath, bool p_move = false) = 0; // Delete @p_dirPath from disk if it is empty. // Return false if it is not deleted due to non-empty. diff --git a/src/core/notebookbackend/localnotebookbackend.cpp b/src/core/notebookbackend/localnotebookbackend.cpp index e365ef0f..096f7148 100644 --- a/src/core/notebookbackend/localnotebookbackend.cpp +++ b/src/core/notebookbackend/localnotebookbackend.cpp @@ -123,7 +123,7 @@ void LocalNotebookBackend::renameDir(const QString &p_dirPath, const QString &p_ FileUtils::renameFile(dirPath, p_name); } -void LocalNotebookBackend::copyFile(const QString &p_filePath, const QString &p_destPath) +void LocalNotebookBackend::copyFile(const QString &p_filePath, const QString &p_destPath, bool p_move) { auto filePath = p_filePath; if (QFileInfo(filePath).isRelative()) { @@ -132,10 +132,10 @@ void LocalNotebookBackend::copyFile(const QString &p_filePath, const QString &p_ Q_ASSERT(QFileInfo(filePath).isFile()); - FileUtils::copyFile(filePath, getFullPath(p_destPath)); + FileUtils::copyFile(filePath, getFullPath(p_destPath), p_move); } -void LocalNotebookBackend::copyDir(const QString &p_dirPath, const QString &p_destPath) +void LocalNotebookBackend::copyDir(const QString &p_dirPath, const QString &p_destPath, bool p_move) { auto dirPath = p_dirPath; if (QFileInfo(dirPath).isRelative()) { @@ -144,7 +144,7 @@ void LocalNotebookBackend::copyDir(const QString &p_dirPath, const QString &p_de Q_ASSERT(QFileInfo(dirPath).isDir()); - FileUtils::copyDir(dirPath, getFullPath(p_destPath)); + FileUtils::copyDir(dirPath, getFullPath(p_destPath), p_move); } void LocalNotebookBackend::removeFile(const QString &p_filePath) diff --git a/src/core/notebookbackend/localnotebookbackend.h b/src/core/notebookbackend/localnotebookbackend.h index 9a32d63f..09dc15d4 100644 --- a/src/core/notebookbackend/localnotebookbackend.h +++ b/src/core/notebookbackend/localnotebookbackend.h @@ -69,10 +69,10 @@ namespace vnotex // Copy @p_filePath to @p_destPath. // @p_filePath may beyond this notebook backend. - void copyFile(const QString &p_filePath, const QString &p_destPath) Q_DECL_OVERRIDE; + void copyFile(const QString &p_filePath, const QString &p_destPath, bool p_move = false) Q_DECL_OVERRIDE; // Copy @p_dirPath to as @p_destPath. - void copyDir(const QString &p_dirPath, const QString &p_destPath) Q_DECL_OVERRIDE; + void copyDir(const QString &p_dirPath, const QString &p_destPath, bool p_move = false) Q_DECL_OVERRIDE; QString renameIfExistsCaseInsensitive(const QString &p_path) const Q_DECL_OVERRIDE; diff --git a/src/core/notebookconfigmgr/bundlenotebookconfigmgr.cpp b/src/core/notebookconfigmgr/bundlenotebookconfigmgr.cpp index 6f2d3b6a..86047094 100644 --- a/src/core/notebookconfigmgr/bundlenotebookconfigmgr.cpp +++ b/src/core/notebookconfigmgr/bundlenotebookconfigmgr.cpp @@ -95,12 +95,14 @@ bool BundleNotebookConfigMgr::isBuiltInFile(const Node *p_node, const QString &p bool BundleNotebookConfigMgr::isBuiltInFolder(const Node *p_node, const QString &p_name) const { if (p_node->isRoot()) { - return p_name.toLower() == c_configFolderName; + const auto name = p_name.toLower(); + return (name == c_configFolderName + || name == getNotebook()->getRecycleBinFolder().toLower()); } return false; } int BundleNotebookConfigMgr::getCodeVersion() const { - return 2; + return 3; } diff --git a/src/core/notebookconfigmgr/inotebookconfigmgr.h b/src/core/notebookconfigmgr/inotebookconfigmgr.h index 843819d2..5d94f076 100644 --- a/src/core/notebookconfigmgr/inotebookconfigmgr.h +++ b/src/core/notebookconfigmgr/inotebookconfigmgr.h @@ -69,6 +69,8 @@ namespace vnotex virtual void removeNode(const QSharedPointer &p_node, bool p_force, bool p_configOnly) = 0; + virtual void removeNodeToFolder(const QSharedPointer &p_node, const QString &p_destFolder) = 0; + // Whether @p_name is a built-in file under @p_node. virtual bool isBuiltInFile(const Node *p_node, const QString &p_name) const = 0; diff --git a/src/core/notebookconfigmgr/vxnotebookconfigmgr.cpp b/src/core/notebookconfigmgr/vxnotebookconfigmgr.cpp index 0a3b8df3..30bb9c16 100644 --- a/src/core/notebookconfigmgr/vxnotebookconfigmgr.cpp +++ b/src/core/notebookconfigmgr/vxnotebookconfigmgr.cpp @@ -22,6 +22,7 @@ #include #include "vxnodeconfig.h" +#include "vxnotebookconfigmgrfactory.h" using namespace vnotex; @@ -29,19 +30,12 @@ using namespace vnotex::vx_node_config; const QString VXNotebookConfigMgr::c_nodeConfigName = "vx.json"; -const QString VXNotebookConfigMgr::c_recycleBinFolderName = "vx_recycle_bin"; - bool VXNotebookConfigMgr::s_initialized = false; QVector VXNotebookConfigMgr::s_externalNodeExcludePatterns; -VXNotebookConfigMgr::VXNotebookConfigMgr(const QString &p_name, - const QString &p_displayName, - const QString &p_description, - const QSharedPointer &p_backend, - QObject *p_parent) - : BundleNotebookConfigMgr(p_backend, p_parent), - m_info(p_name, p_displayName, p_description) +VXNotebookConfigMgr::VXNotebookConfigMgr(const QSharedPointer &p_backend, QObject *p_parent) + : BundleNotebookConfigMgr(p_backend, p_parent) { if (!s_initialized) { s_initialized = true; @@ -58,17 +52,17 @@ VXNotebookConfigMgr::VXNotebookConfigMgr(const QString &p_name, QString VXNotebookConfigMgr::getName() const { - return m_info.m_name; + return VXNotebookConfigMgrFactory::c_name; } QString VXNotebookConfigMgr::getDisplayName() const { - return m_info.m_displayName; + return VXNotebookConfigMgrFactory::c_displayName; } QString VXNotebookConfigMgr::getDescription() const { - return m_info.m_description; + return VXNotebookConfigMgrFactory::c_description; } void VXNotebookConfigMgr::createEmptySkeleton(const NotebookParameters &p_paras) @@ -97,29 +91,21 @@ QSharedPointer VXNotebookConfigMgr::loadRootNode() root->setExists(true); Q_ASSERT(root->isLoaded()); - if (!markRecycleBinNode(root)) { - const_cast(this)->createRecycleBinNode(root); + if (static_cast(getNotebook())->getConfigVersion() < 3) { + removeLegacyRecycleBinNode(root); } return root; } -bool VXNotebookConfigMgr::markRecycleBinNode(const QSharedPointer &p_root) +void VXNotebookConfigMgr::removeLegacyRecycleBinNode(const QSharedPointer &p_root) { - auto node = p_root->findChild(c_recycleBinFolderName, + // Do not support recycle bin node as it complicates everything. + auto node = p_root->findChild(QStringLiteral("vx_recycle_bin"), FileUtils::isPlatformNameCaseSensitive()); if (node) { - if (!node->exists()) { - removeNode(node, true, true); - return false; - } - - node->setUse(Node::Use::RecycleBin); - markNodeReadOnly(node.data()); - return true; + removeNode(node, true, true); } - - return false; } void VXNotebookConfigMgr::markNodeReadOnly(Node *p_node) const @@ -134,15 +120,6 @@ void VXNotebookConfigMgr::markNodeReadOnly(Node *p_node) const } } -void VXNotebookConfigMgr::createRecycleBinNode(const QSharedPointer &p_root) -{ - Q_ASSERT(p_root->isRoot()); - - auto node = newNode(p_root.data(), Node::Flag::Container, c_recycleBinFolderName, ""); - node->setUse(Node::Use::RecycleBin); - markNodeReadOnly(node.data()); -} - QSharedPointer VXNotebookConfigMgr::readNodeConfig(const QString &p_path) const { auto backend = getBackend(); @@ -314,6 +291,13 @@ QSharedPointer VXNotebookConfigMgr::newFileNode(Node *p_parent, // Write empty file. if (p_create) { + if (getBackend()->childExistsCaseInsensitive(p_parent->fetchPath(), p_name)) { + // File already exists. Exception. + Exception::throwOne(Exception::Type::FileExistsOnCreate, + QString("file (%1) already exists when creating new node").arg(node->fetchPath())); + return nullptr; + } + getBackend()->writeFile(node->fetchPath(), p_content); node->setExists(true); } else { @@ -343,6 +327,13 @@ QSharedPointer VXNotebookConfigMgr::newFolderNode(Node *p_parent, // Make folder. if (p_create) { + if (getBackend()->childExistsCaseInsensitive(p_parent->fetchPath(), p_name)) { + // Dir already exists. Exception. + Exception::throwOne(Exception::Type::DirExistsOnCreate, + QString("dir (%1) already exists when creating new node").arg(node->fetchPath())); + return nullptr; + } + getBackend()->makePath(node->fetchPath()); node->setExists(true); } else { @@ -513,22 +504,9 @@ QSharedPointer VXNotebookConfigMgr::copyFileNodeAsChildOf(const QSharedPoi bool p_move, bool p_updateDatabase) { - // Copy source file itself. - auto srcFilePath = p_src->fetchAbsolutePath(); - auto destFilePath = PathUtils::concatenateFilePath(p_dest->fetchPath(), - PathUtils::fileName(srcFilePath)); - destFilePath = getBackend()->renameIfExistsCaseInsensitive(destFilePath); - getBackend()->copyFile(srcFilePath, destFilePath); - - // Copy media files fetched from content. - ContentMediaUtils::copyMediaFiles(p_src.data(), getBackend().data(), destFilePath); - - // Copy attachment folder. Rename attachment folder if conflicts. - QString attachmentFolder = p_src->getAttachmentFolder(); - if (!attachmentFolder.isEmpty()) { - auto destAttachmentFolderPath = fetchNodeAttachmentFolder(destFilePath, attachmentFolder); - ContentMediaUtils::copyAttachment(p_src.data(), getBackend().data(), destFilePath, destAttachmentFolderPath); - } + QString destFilePath; + QString attachmentFolder; + copyFilesOfFileNode(p_src, p_dest->fetchPath(), destFilePath, attachmentFolder); // Create a file node. auto notebook = getNotebook(); @@ -571,7 +549,7 @@ QSharedPointer VXNotebookConfigMgr::copyFileNodeAsChildOf(const QSharedPoi if (p_move) { if (sameNotebook) { - // The same notebook. Do not directly call removeNode() since we need to update the record + // The same notebook. Do not directly call removeNode() since we already update the record // in database directly. removeNode(p_src, false, false, false); } else { @@ -587,9 +565,7 @@ QSharedPointer VXNotebookConfigMgr::copyFolderNodeAsChildOf(const QSharedP bool p_move, bool p_updateDatabase) { - auto srcFolderPath = p_src->fetchAbsolutePath(); - auto destFolderPath = PathUtils::concatenateFilePath(p_dest->fetchPath(), - PathUtils::fileName(srcFolderPath)); + auto destFolderPath = PathUtils::concatenateFilePath(p_dest->fetchPath(), p_src->getName()); destFolderPath = getBackend()->renameIfExistsCaseInsensitive(destFolderPath); // Make folder. @@ -662,7 +638,6 @@ void VXNotebookConfigMgr::removeNode(const QSharedPointer &p_node, bool p_configOnly, bool p_updateDatabase) { - auto parentNode = p_node->getParent(); if (!p_configOnly && p_node->exists()) { // Remove all children. auto children = p_node->getChildren(); @@ -683,7 +658,7 @@ void VXNotebookConfigMgr::removeNode(const QSharedPointer &p_node, removeNodeFromDatabase(p_node.data()); } - if (parentNode) { + if (auto parentNode = p_node->getParent()) { parentNode->removeChild(p_node); writeNodeConfig(parentNode); } @@ -722,10 +697,74 @@ void VXNotebookConfigMgr::removeFilesOfNode(Node *p_node, bool p_force) } } +void VXNotebookConfigMgr::removeNodeToFolder(const QSharedPointer &p_node, const QString &p_destFolder) +{ + if (p_node->isContainer()) { + removeFolderNodeToFolder(p_node, p_destFolder); + } else { + removeFileNodeToFolder(p_node, p_destFolder); + } +} + +void VXNotebookConfigMgr::removeFolderNodeToFolder(const QSharedPointer &p_node, const QString &p_destFolder) +{ + auto destFolderPath = PathUtils::concatenateFilePath(p_destFolder, p_node->getName()); + destFolderPath = getBackend()->renameIfExistsCaseInsensitive(destFolderPath); + + // Make folder. + getBackend()->makePath(destFolderPath); + + // Children. + auto children = p_node->getChildren(); + for (const auto &child : children) { + removeNodeToFolder(child, destFolderPath); + } + + removeNode(p_node, false, false); +} + +void VXNotebookConfigMgr::removeFileNodeToFolder(const QSharedPointer &p_node, const QString &p_destFolder) +{ + // Use a wrapper folder. + auto destFolderPath = PathUtils::concatenateFilePath(p_destFolder, p_node->getName()); + destFolderPath = getBackend()->renameIfExistsCaseInsensitive(destFolderPath); + + // Make folder. + getBackend()->makePath(destFolderPath); + + QString destFilePath; + QString attachmentFolder; + copyFilesOfFileNode(p_node, destFolderPath, destFilePath, attachmentFolder); + + removeNode(p_node, false, false); +} + +void VXNotebookConfigMgr::copyFilesOfFileNode(const QSharedPointer &p_node, + const QString &p_destFolder, + QString &p_destFilePath, + QString &p_attachmentFolder) +{ + // Copy source file itself. + auto nodeFilePath = p_node->fetchAbsolutePath(); + p_destFilePath = PathUtils::concatenateFilePath(p_destFolder, PathUtils::fileName(nodeFilePath)); + p_destFilePath = getBackend()->renameIfExistsCaseInsensitive(p_destFilePath); + getBackend()->copyFile(nodeFilePath, p_destFilePath); + + // Copy media files fetched from content. + ContentMediaUtils::copyMediaFiles(p_node.data(), getBackend().data(), p_destFilePath); + + // Copy attachment folder. Rename attachment folder if conflicts. + p_attachmentFolder = p_node->getAttachmentFolder(); + if (!p_attachmentFolder.isEmpty()) { + auto destAttachmentFolderPath = fetchNodeAttachmentFolder(p_destFilePath, p_attachmentFolder); + ContentMediaUtils::copyAttachment(p_node.data(), getBackend().data(), p_destFilePath, destAttachmentFolderPath); + } +} + QString VXNotebookConfigMgr::fetchNodeImageFolderPath(Node *p_node) { auto pa = PathUtils::concatenateFilePath(PathUtils::parentDirPath(p_node->fetchAbsolutePath()), - getNotebook()->getImageFolder()); + getNotebook()->getImageFolder()); // Do not make the folder when it is a folder node request. if (p_node->hasContent()) { getBackend()->makePath(pa); @@ -736,7 +775,7 @@ QString VXNotebookConfigMgr::fetchNodeImageFolderPath(Node *p_node) QString VXNotebookConfigMgr::fetchNodeAttachmentFolderPath(Node *p_node) { auto notebookFolder = PathUtils::concatenateFilePath(PathUtils::parentDirPath(p_node->fetchAbsolutePath()), - getNotebook()->getAttachmentFolder()); + getNotebook()->getAttachmentFolder()); if (p_node->hasContent()) { auto nodeFolder = p_node->getAttachmentFolder(); if (nodeFolder.isEmpty()) { @@ -783,9 +822,9 @@ bool VXNotebookConfigMgr::isBuiltInFile(const Node *p_node, const QString &p_nam bool VXNotebookConfigMgr::isBuiltInFolder(const Node *p_node, const QString &p_name) const { const auto name = p_name.toLower(); - if (name == c_recycleBinFolderName - || name == getNotebook()->getImageFolder().toLower() - || name == getNotebook()->getAttachmentFolder().toLower() + const auto &nb = getNotebook(); + if (name == nb->getImageFolder().toLower() + || name == nb->getAttachmentFolder().toLower() || name == QStringLiteral("_v_images") || name == QStringLiteral("_v_attachments")) { return true; @@ -946,7 +985,7 @@ bool VXNotebookConfigMgr::checkNodeExists(Node *p_node) QStringList VXNotebookConfigMgr::scanAndImportExternalFiles(Node *p_node) { QStringList files; - if (!p_node->isContainer() || p_node->getUse() == Node::Use::RecycleBin) { + if (!p_node->isContainer()) { return files; } diff --git a/src/core/notebookconfigmgr/vxnotebookconfigmgr.h b/src/core/notebookconfigmgr/vxnotebookconfigmgr.h index 074a53ae..787ecaf0 100644 --- a/src/core/notebookconfigmgr/vxnotebookconfigmgr.h +++ b/src/core/notebookconfigmgr/vxnotebookconfigmgr.h @@ -27,11 +27,7 @@ namespace vnotex { Q_OBJECT public: - explicit VXNotebookConfigMgr(const QString &p_name, - const QString &p_displayName, - const QString &p_description, - const QSharedPointer &p_backend, - QObject *p_parent = nullptr); + VXNotebookConfigMgr(const QSharedPointer &p_backend, QObject *p_parent = nullptr); QString getName() const Q_DECL_OVERRIDE; @@ -71,6 +67,8 @@ namespace vnotex void removeNode(const QSharedPointer &p_node, bool p_force = false, bool p_configOnly = false) Q_DECL_OVERRIDE; + void removeNodeToFolder(const QSharedPointer &p_node, const QString &p_destFolder) Q_DECL_OVERRIDE; + bool isBuiltInFile(const Node *p_node, const QString &p_name) const Q_DECL_OVERRIDE; bool isBuiltInFolder(const Node *p_node, const QString &p_name) const Q_DECL_OVERRIDE; @@ -137,11 +135,9 @@ namespace vnotex void removeFilesOfNode(Node *p_node, bool p_force); - bool markRecycleBinNode(const QSharedPointer &p_root); - void markNodeReadOnly(Node *p_node) const; - void createRecycleBinNode(const QSharedPointer &p_root); + void removeLegacyRecycleBinNode(const QSharedPointer &p_root); // Generate node attachment folder. // @p_folderName: suggested folder name if not empty, may be renamed due to conflicts. @@ -171,9 +167,16 @@ namespace vnotex bool sameNotebook(const Node *p_node) const; - static bool isLikelyImageFolder(const QString &p_dirPath); + void removeFolderNodeToFolder(const QSharedPointer &p_node, const QString &p_destFolder); - Info m_info; + void removeFileNodeToFolder(const QSharedPointer &p_node, const QString &p_destFolder); + + void copyFilesOfFileNode(const QSharedPointer &p_node, + const QString &p_destFolder, + QString &p_destFilePath, + QString &p_attachmentFolder); + + static bool isLikelyImageFolder(const QString &p_dirPath); static bool s_initialized; @@ -181,9 +184,6 @@ namespace vnotex // Name of the node's config file. static const QString c_nodeConfigName; - - // Name of the recycle bin folder which should be a child of the root node. - static const QString c_recycleBinFolderName; }; } // ns vnotex diff --git a/src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.cpp b/src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.cpp index 842ee7a0..de9c781b 100644 --- a/src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.cpp +++ b/src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.cpp @@ -7,29 +7,32 @@ using namespace vnotex; +const QString VXNotebookConfigMgrFactory::c_name = QStringLiteral("vx.vnotex"); + +const QString VXNotebookConfigMgrFactory::c_displayName = QObject::tr("VNoteX Notebook Configuration"); + +const QString VXNotebookConfigMgrFactory::c_description = QObject::tr("Built-in VNoteX notebook configuration"); + VXNotebookConfigMgrFactory::VXNotebookConfigMgrFactory() { } QString VXNotebookConfigMgrFactory::getName() const { - return QStringLiteral("vx.vnotex"); + return c_name; } QString VXNotebookConfigMgrFactory::getDisplayName() const { - return QObject::tr("VNoteX Notebook Configuration"); + return c_displayName; } QString VXNotebookConfigMgrFactory::getDescription() const { - return QObject::tr("Built-in VNoteX notebook configuration"); + return c_description; } QSharedPointer VXNotebookConfigMgrFactory::createNotebookConfigMgr(const QSharedPointer &p_backend) { - return QSharedPointer::create(getName(), - getDisplayName(), - getDescription(), - p_backend); + return QSharedPointer::create(p_backend); } diff --git a/src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.h b/src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.h index 3a345444..02f67b57 100644 --- a/src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.h +++ b/src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.h @@ -19,6 +19,12 @@ namespace vnotex QString getDescription()const Q_DECL_OVERRIDE; QSharedPointer createNotebookConfigMgr(const QSharedPointer &p_backend) Q_DECL_OVERRIDE; + + static const QString c_name; + + static const QString c_displayName; + + static const QString c_description; }; } // ns vnotex diff --git a/src/core/sessionconfig.cpp b/src/core/sessionconfig.cpp index 4f9d2ef3..179797c8 100644 --- a/src/core/sessionconfig.cpp +++ b/src/core/sessionconfig.cpp @@ -114,11 +114,7 @@ void SessionConfig::loadCore(const QJsonObject &p_session) if (!isUndefinedKey(coreObj, QStringLiteral("system_title_bar"))) { m_systemTitleBarEnabled = readBool(coreObj, QStringLiteral("system_title_bar")); } else { -#ifdef Q_OS_WIN - m_systemTitleBarEnabled = false; -#else m_systemTitleBarEnabled = true; -#endif } if (!isUndefinedKey(coreObj, QStringLiteral("minimize_to_system_tray"))) { @@ -318,9 +314,6 @@ void SessionConfig::doVersionSpecificOverride() { // In a new version, we may want to change one value by force. // SHOULD set the in memory variable only, or will override the notebook list. -#ifdef Q_OS_WIN - m_systemTitleBarEnabled = false; -#endif } const ExportOption &SessionConfig::getExportOption() const diff --git a/src/core/widgetconfig.cpp b/src/core/widgetconfig.cpp index 29bfa151..64c9cc4c 100644 --- a/src/core/widgetconfig.cpp +++ b/src/core/widgetconfig.cpp @@ -31,7 +31,6 @@ void WidgetConfig::init(const QJsonObject &p_app, { m_nodeExplorerViewOrder = READINT(QStringLiteral("node_explorer_view_order")); - m_nodeExplorerRecycleBinNodeVisible = READBOOL(QStringLiteral("node_explorer_recycle_bin_node_visible")); m_nodeExplorerExternalFilesVisible = READBOOL(QStringLiteral("node_explorer_external_files_visible")); m_nodeExplorerAutoImportExternalFilesEnabled = READBOOL(QStringLiteral("node_explorer_auto_import_external_files_enabled")); m_nodeExplorerCloseBeforeOpenWithEnabled = READBOOL(QStringLiteral("node_explorer_close_before_open_with_enabled")); @@ -55,7 +54,6 @@ QJsonObject WidgetConfig::toJson() const obj[QStringLiteral("find_and_replace_options")] = static_cast(m_findAndReplaceOptions); obj[QStringLiteral("node_explorer_view_order")] = m_nodeExplorerViewOrder; - obj[QStringLiteral("node_explorer_recycle_bin_node_visible")] = m_nodeExplorerRecycleBinNodeVisible; obj[QStringLiteral("node_explorer_external_files_visible")] = m_nodeExplorerExternalFilesVisible; obj[QStringLiteral("node_explorer_auto_import_external_files_enabled")] = m_nodeExplorerAutoImportExternalFilesEnabled; obj[QStringLiteral("node_explorer_close_before_open_with_enabled")] = m_nodeExplorerCloseBeforeOpenWithEnabled; @@ -108,16 +106,6 @@ void WidgetConfig::setNodeExplorerViewOrder(int p_viewOrder) updateConfig(m_nodeExplorerViewOrder, p_viewOrder, this); } -bool WidgetConfig::isNodeExplorerRecycleBinNodeVisible() const -{ - return m_nodeExplorerRecycleBinNodeVisible; -} - -void WidgetConfig::setNodeExplorerRecycleBinNodeVisible(bool p_visible) -{ - updateConfig(m_nodeExplorerRecycleBinNodeVisible, p_visible, this); -} - bool WidgetConfig::isNodeExplorerExternalFilesVisible() const { return m_nodeExplorerExternalFilesVisible; diff --git a/src/core/widgetconfig.h b/src/core/widgetconfig.h index e70c5a2e..91f369cc 100644 --- a/src/core/widgetconfig.h +++ b/src/core/widgetconfig.h @@ -30,9 +30,6 @@ namespace vnotex int getNodeExplorerViewOrder() const; void setNodeExplorerViewOrder(int p_viewOrder); - bool isNodeExplorerRecycleBinNodeVisible() const; - void setNodeExplorerRecycleBinNodeVisible(bool p_visible); - bool isNodeExplorerExternalFilesVisible() const; void setNodeExplorerExternalFilesVisible(bool p_visible); @@ -63,8 +60,6 @@ namespace vnotex int m_nodeExplorerViewOrder = 0; - bool m_nodeExplorerRecycleBinNodeVisible = false; - bool m_nodeExplorerExternalFilesVisible = true; bool m_nodeExplorerAutoImportExternalFilesEnabled = true; diff --git a/src/data/core/vnotex.json b/src/data/core/vnotex.json index a8e55299..7fcc7c94 100644 --- a/src/data/core/vnotex.json +++ b/src/data/core/vnotex.json @@ -373,7 +373,6 @@ "find_and_replace_options" : 16, "//comment" : "View order of the node explorer", "node_explorer_view_order" : 0, - "node_explorer_recycle_bin_node_visible" : false, "node_explorer_external_files_visible" : true, "node_explorer_auto_import_external_files_enabled" : true, "//comment" : "Whether close the file before opening it with external program", diff --git a/src/search/searcher.cpp b/src/search/searcher.cpp index 2da88547..1b6becb7 100644 --- a/src/search/searcher.cpp +++ b/src/search/searcher.cpp @@ -470,11 +470,6 @@ bool Searcher::firstPhaseSearch(Notebook *p_notebook, QVectorisRecycleBinNode(child.data())) { - qDebug() << "skipped searching recycle bin"; - continue; - } - if (child->hasContent() && testTarget(SearchTarget::SearchFile)) { if (!firstPhaseSearch(child.data(), p_secondPhaseItems)) { return false; diff --git a/src/utils/fileutils.cpp b/src/utils/fileutils.cpp index 0f12ca13..f44fcf6a 100644 --- a/src/utils/fileutils.cpp +++ b/src/utils/fileutils.cpp @@ -118,7 +118,7 @@ void FileUtils::copyFile(const QString &p_filePath, QDir dir; if (!dir.mkpath(PathUtils::parentDirPath(p_destPath))) { Exception::throwOne(Exception::Type::FailToCreateDir, - QString("failed to create directory: %1").arg(PathUtils::parentDirPath(p_destPath))); + QString("failed to create directory: %1").arg(PathUtils::parentDirPath(p_destPath))); } bool failed = false; diff --git a/src/widgets/dialogs/managenotebooksdialog.cpp b/src/widgets/dialogs/managenotebooksdialog.cpp index fde11567..37b10ae2 100644 --- a/src/widgets/dialogs/managenotebooksdialog.cpp +++ b/src/widgets/dialogs/managenotebooksdialog.cpp @@ -260,7 +260,7 @@ void ManageNotebooksDialog::closeNotebook(const Notebook *p_notebook) int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Question, tr("Close notebook (%1)?") .arg(p_notebook->getName()), - tr("The notebook could be imported again later."), + tr("The notebook could be opened by VNote again."), tr("Notebook location: %1").arg(p_notebook->getRootFolderAbsolutePath()), this); if (ret != QMessageBox::Ok) { @@ -289,14 +289,18 @@ void ManageNotebooksDialog::removeNotebook(const Notebook *p_notebook) int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Warning, tr("Please close the notebook in VNote first and delete the notebook root folder files manually."), - tr("Press \"Ok\" to open the location of the notebook root folder."), + tr("Press \"Ok\" to close the notebook and open the location of the notebook root folder."), tr("Notebook location: %1").arg(p_notebook->getRootFolderAbsolutePath()), this); if (ret != QMessageBox::Ok) { return; } - WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(p_notebook->getRootFolderAbsolutePath())); + const auto rootFolder = p_notebook->getRootFolderAbsolutePath(); + + closeNotebook(p_notebook); + + WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(rootFolder)); } bool ManageNotebooksDialog::checkUnsavedChanges() diff --git a/src/widgets/dialogs/newfolderdialog.cpp b/src/widgets/dialogs/newfolderdialog.cpp index aeac5c01..b8c601c2 100644 --- a/src/widgets/dialogs/newfolderdialog.cpp +++ b/src/widgets/dialogs/newfolderdialog.cpp @@ -57,8 +57,8 @@ bool NewFolderDialog::validateNameInput(QString &p_msg) return false; } - if (m_infoWidget->getParentNode()->containsChild(name, false)) { - p_msg = tr("Name conflicts with existing folder."); + if (!m_infoWidget->getParentNode()->isLegalNameForNewChild(name)) { + p_msg = tr("Name conflicts with existing or built-in folder."); return false; } diff --git a/src/widgets/dialogs/newnotedialog.cpp b/src/widgets/dialogs/newnotedialog.cpp index 800e0737..673e75b9 100644 --- a/src/widgets/dialogs/newnotedialog.cpp +++ b/src/widgets/dialogs/newnotedialog.cpp @@ -93,8 +93,8 @@ bool NewNoteDialog::validateNameInput(QString &p_msg) return false; } - if (m_infoWidget->getParentNode()->containsChild(name, false)) { - p_msg = tr("Name conflicts with existing note."); + if (!m_infoWidget->getParentNode()->isLegalNameForNewChild(name)) { + p_msg = tr("Name conflicts with existing or built-in note."); return false; } diff --git a/src/widgets/mainwindow.cpp b/src/widgets/mainwindow.cpp index 8b8d65fc..98fa1738 100644 --- a/src/widgets/mainwindow.cpp +++ b/src/widgets/mainwindow.cpp @@ -629,7 +629,7 @@ void MainWindow::exportNotes() auto currentNotebook = m_notebookExplorer->currentNotebook().data(); auto viewWindow = m_viewArea->getCurrentViewWindow(); auto folderNode = m_notebookExplorer->currentExploredFolderNode(); - if (folderNode && (folderNode->isRoot() || currentNotebook->isRecycleBinNode(folderNode))) { + if (folderNode && (folderNode->isRoot())) { folderNode = nullptr; } auto noteNode = m_notebookExplorer->currentExploredNode(); diff --git a/src/widgets/notebookexplorer.cpp b/src/widgets/notebookexplorer.cpp index ad113dfa..a24fe802 100644 --- a/src/widgets/notebookexplorer.cpp +++ b/src/widgets/notebookexplorer.cpp @@ -81,7 +81,6 @@ void NotebookExplorer::setupUI() const auto &widgetConfig = ConfigMgr::getInst().getWidgetConfig(); m_nodeExplorer = new NotebookNodeExplorer(this); - m_nodeExplorer->setRecycleBinNodeVisible(widgetConfig.isNodeExplorerRecycleBinNodeVisible()); m_nodeExplorer->setViewOrder(widgetConfig.getNodeExplorerViewOrder()); m_nodeExplorer->setExternalFilesVisible(widgetConfig.isNodeExplorerExternalFilesVisible()); connect(m_nodeExplorer, &NotebookNodeExplorer::nodeActivated, @@ -122,15 +121,9 @@ TitleBar *NotebookExplorer::setupTitleBar(QWidget *p_parent) } { - auto btn = titleBar->addActionButton(QStringLiteral("recycle_bin.svg"), tr("Toggle Recycle Bin Node")); - btn->defaultAction()->setCheckable(true); - btn->defaultAction()->setChecked(widgetConfig.isNodeExplorerRecycleBinNodeVisible()); - connect(btn, &QToolButton::triggered, - this, [this](QAction *p_act) { - const bool checked = p_act->isChecked(); - ConfigMgr::getInst().getWidgetConfig().setNodeExplorerRecycleBinNodeVisible(checked); - m_nodeExplorer->setRecycleBinNodeVisible(checked); - }); + auto recycleBinMenu = WidgetsFactory::createMenu(titleBar); + setupRecycleBinMenu(recycleBinMenu); + titleBar->addActionButton(QStringLiteral("recycle_bin.svg"), tr("Recycle Bin"), recycleBinMenu); } { @@ -260,15 +253,6 @@ void NotebookExplorer::newFolder() return; } - if (m_currentNotebook->isRecycleBinNode(node)) { - node = m_currentNotebook->getRootNode().data(); - } else if (m_currentNotebook->isNodeInRecycleBin(node)) { - MessageBoxHelper::notify(MessageBoxHelper::Information, - tr("Could not create folder within Recycle Bin."), - VNoteX::getInst().getMainWindow()); - return; - } - NewFolderDialog dialog(node, VNoteX::getInst().getMainWindow()); if (dialog.exec() == QDialog::Accepted) { m_nodeExplorer->setCurrentNode(dialog.getNewNode().data()); @@ -282,15 +266,6 @@ void NotebookExplorer::newNote() return; } - if (m_currentNotebook->isRecycleBinNode(node)) { - node = m_currentNotebook->getRootNode().data(); - } else if (m_currentNotebook->isNodeInRecycleBin(node)) { - MessageBoxHelper::notify(MessageBoxHelper::Information, - tr("Could not create note within Recycle Bin."), - VNoteX::getInst().getMainWindow()); - return; - } - NewNoteDialog dialog(node, VNoteX::getInst().getMainWindow()); if (dialog.exec() == QDialog::Accepted) { m_nodeExplorer->setCurrentNode(dialog.getNewNode().data()); @@ -340,15 +315,6 @@ void NotebookExplorer::importFile() return; } - if (m_currentNotebook->isRecycleBinNode(node)) { - node = m_currentNotebook->getRootNode().data(); - } else if (m_currentNotebook->isNodeInRecycleBin(node)) { - MessageBoxHelper::notify(MessageBoxHelper::Information, - tr("Could not create note within Recycle Bin."), - VNoteX::getInst().getMainWindow()); - return; - } - static QString lastFolderPath = QDir::homePath(); QStringList files = QFileDialog::getOpenFileNames(VNoteX::getInst().getMainWindow(), tr("Select Files To Import"), @@ -381,15 +347,6 @@ void NotebookExplorer::importFolder() return; } - if (m_currentNotebook->isRecycleBinNode(node)) { - node = m_currentNotebook->getRootNode().data(); - } else if (m_currentNotebook->isNodeInRecycleBin(node)) { - MessageBoxHelper::notify(MessageBoxHelper::Information, - tr("Could not create folder within Recycle Bin."), - VNoteX::getInst().getMainWindow()); - return; - } - ImportFolderDialog dialog(node, VNoteX::getInst().getMainWindow()); if (dialog.exec() == QDialog::Accepted) { m_nodeExplorer->setCurrentNode(dialog.getNewNode().data()); @@ -483,6 +440,33 @@ void NotebookExplorer::setupViewMenu(QMenu *p_menu) }); } +void NotebookExplorer::setupRecycleBinMenu(QMenu *p_menu) +{ + p_menu->addAction(tr("Open Recycle Bin"), + this, + [this]() { + if (m_currentNotebook) { + WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(m_currentNotebook->getRecycleBinFolderAbsolutePath())); + } + }); + + p_menu->addAction(tr("Empty Recycle Bin"), + this, + [this]() { + if (!m_currentNotebook) { + return; + } + int okRet = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Warning, + tr("Empty the recycle bin of notebook (%1)?").arg(m_currentNotebook->getName()), + tr("CAUTION! All the files under the recycle bin folder will be deleted and unrecoverable!"), + tr("Recycle bin folder: %1").arg(m_currentNotebook->getRecycleBinFolderAbsolutePath()), + VNoteX::getInst().getMainWindow()); + if (okRet == QMessageBox::Ok) { + m_currentNotebook->emptyRecycleBin(); + } + }); +} + void NotebookExplorer::saveSession() { updateSession(); diff --git a/src/widgets/notebookexplorer.h b/src/widgets/notebookexplorer.h index affb28c7..7218d634 100644 --- a/src/widgets/notebookexplorer.h +++ b/src/widgets/notebookexplorer.h @@ -69,6 +69,8 @@ namespace vnotex void setupViewMenu(QMenu *p_menu); + void setupRecycleBinMenu(QMenu *p_menu); + void saveSession(); void loadSession(); diff --git a/src/widgets/notebooknodeexplorer.cpp b/src/widgets/notebooknodeexplorer.cpp index ba3c5582..fbc0e85b 100644 --- a/src/widgets/notebooknodeexplorer.cpp +++ b/src/widgets/notebooknodeexplorer.cpp @@ -192,13 +192,11 @@ void NotebookNodeExplorer::initNodeIcons() const const QString folderIconName("folder_node.svg"); const QString fileIconName("file_node.svg"); - const QString recycleBinIconName("recycle_bin.svg"); s_nodeIcons[NodeIcon::FolderNode] = IconUtils::fetchIcon(themeMgr.getIconFile(folderIconName), fg); s_nodeIcons[NodeIcon::FileNode] = IconUtils::fetchIcon(themeMgr.getIconFile(fileIconName), fg); s_nodeIcons[NodeIcon::InvalidFolderNode] = IconUtils::fetchIcon(themeMgr.getIconFile(folderIconName), invalidFg); s_nodeIcons[NodeIcon::InvalidFileNode] = IconUtils::fetchIcon(themeMgr.getIconFile(fileIconName), invalidFg); - s_nodeIcons[NodeIcon::RecycleBinNode] = IconUtils::fetchIcon(themeMgr.getIconFile(recycleBinIconName), fg); s_nodeIcons[NodeIcon::ExternalFolderNode] = IconUtils::fetchIcon(themeMgr.getIconFile(folderIconName), externalFg); s_nodeIcons[NodeIcon::ExternalFileNode] = IconUtils::fetchIcon(themeMgr.getIconFile(fileIconName), externalFg); } @@ -341,7 +339,6 @@ void NotebookNodeExplorer::generateNodeTree() if (currentNode) { setCurrentNode(currentNode); } else { - // Do not focus the recycle bin. focusNormalNode(); } @@ -352,12 +349,6 @@ void NotebookNodeExplorer::loadRootNode(const Node *p_node) const { Q_ASSERT(p_node->isLoaded() && p_node->isContainer()); - // Render recycle bin node first. - auto recycleBinNode = m_notebook->getRecycleBinNode(); - if (recycleBinNode) { - loadRecycleBinNode(recycleBinNode.data()); - } - // External children. if (m_externalFilesVisible) { auto externalChildren = p_node->fetchExternalChildren(); @@ -372,10 +363,6 @@ void NotebookNodeExplorer::loadRootNode(const Node *p_node) const auto children = p_node->getChildren(); sortNodes(children); for (const auto &child : children) { - if (recycleBinNode == child) { - continue; - } - auto item = new QTreeWidgetItem(m_masterExplorer); loadNode(item, child.data(), 1); } @@ -444,43 +431,6 @@ void NotebookNodeExplorer::loadChildren(QTreeWidgetItem *p_item, Node *p_node, i } } -void NotebookNodeExplorer::loadRecycleBinNode(Node *p_node) const -{ - if (!m_recycleBinNodeVisible) { - return; - } - - auto item = new QTreeWidgetItem(); - item->setWhatsThis(Column::Name, - tr("Recycle bin of this notebook. Deleted files could be found here. " - "It is organized in folders named by date. Nodes could be moved to " - "other folders by Cut and Paste.")); - m_masterExplorer->insertTopLevelItem(0, item); - - loadRecycleBinNode(item, p_node, 1); -} - -void NotebookNodeExplorer::loadRecycleBinNode(QTreeWidgetItem *p_item, Node *p_node, int p_level) const -{ - if (!m_recycleBinNodeVisible) { - return; - } - - if (!p_node->isLoaded()) { - p_node->load(); - } - - clearTreeWigetItemChildren(p_item); - - setItemNodeData(p_item, NodeData(p_node, true)); - p_item->setText(Column::Name, tr("Recycle Bin")); - p_item->setIcon(Column::Name, getNodeItemIcon(p_node)); - - loadChildren(p_item, p_node, p_level - 1); - - // No need to restore state. -} - void NotebookNodeExplorer::fillTreeItem(QTreeWidgetItem *p_item, Node *p_node, bool p_loaded) const { setItemNodeData(p_item, NodeData(p_node, p_loaded)); @@ -502,10 +452,6 @@ const QIcon &NotebookNodeExplorer::getNodeItemIcon(const Node *p_node) const if (p_node->hasContent()) { return p_node->exists() ? s_nodeIcons[NodeIcon::FileNode] : s_nodeIcons[NodeIcon::InvalidFileNode]; } else { - if (p_node->getUse() == Node::Use::RecycleBin) { - return s_nodeIcons[NodeIcon::RecycleBinNode]; - } - return p_node->exists() ? s_nodeIcons[NodeIcon::FolderNode] : s_nodeIcons[NodeIcon::InvalidFolderNode]; } } @@ -561,20 +507,9 @@ void NotebookNodeExplorer::updateNode(Node *p_node) if (item) { bool expanded = item->isExpanded(); item->setExpanded(false); - - if (m_notebook->isRecycleBinNode(p_node)) { - loadRecycleBinNode(item, p_node, 1); - } else { - loadNode(item, p_node, 1); - } - + loadNode(item, p_node, 1); item->setExpanded(expanded); } else { - if (m_notebook->isRecycleBinNode(p_node) && !m_recycleBinNodeVisible) { - // No need to update. - return; - } - saveNotebookTreeState(false); generateNodeTree(); @@ -762,106 +697,63 @@ void NotebookNodeExplorer::createContextMenuOnRoot(QMenu *p_menu) void NotebookNodeExplorer::createContextMenuOnNode(QMenu *p_menu, const Node *p_node) { const int selectedSize = m_masterExplorer->selectedItems().size(); - if (m_notebook->isRecycleBinNode(p_node)) { - // Recycle bin node. - createAndAddAction(Action::Reload, p_menu); - createAndAddAction(Action::ReloadIndex, p_menu); + createAndAddAction(Action::Open, p_menu); - if (selectedSize == 1) { - createAndAddAction(Action::EmptyRecycleBin, p_menu); + addOpenWithMenu(p_menu); - createAndAddAction(Action::OpenLocation, p_menu); - } - } else if (m_notebook->isNodeInRecycleBin(p_node)) { - // Node in recycle bin. - createAndAddAction(Action::Open, p_menu); + p_menu->addSeparator(); - addOpenWithMenu(p_menu); + if (selectedSize == 1 && p_node->isContainer()) { + createAndAddAction(Action::ExpandAll, p_menu); + } + p_menu->addSeparator(); + + createAndAddAction(Action::NewNote, p_menu); + + createAndAddAction(Action::NewFolder, p_menu); + + p_menu->addSeparator(); + + createAndAddAction(Action::Copy, p_menu); + + createAndAddAction(Action::Cut, p_menu); + + if (selectedSize == 1 && isPasteOnNodeAvailable(p_node)) { + createAndAddAction(Action::Paste, p_menu); + } + + createAndAddAction(Action::Delete, p_menu); + + createAndAddAction(Action::RemoveFromConfig, p_menu); + + p_menu->addSeparator(); + + createAndAddAction(Action::Reload, p_menu); + + createAndAddAction(Action::ReloadIndex, p_menu); + + createAndAddAction(Action::Sort, p_menu); + + if (selectedSize == 1 + && m_notebook->tag() + && !p_node->isContainer()) { p_menu->addSeparator(); - if (selectedSize == 1 && p_node->isContainer()) { - createAndAddAction(Action::ExpandAll, p_menu); - } + createAndAddAction(Action::Tag, p_menu); + } - p_menu->addSeparator(); + p_menu->addSeparator(); - createAndAddAction(Action::Cut, p_menu); + createAndAddAction(Action::PinToQuickAccess, p_menu); - createAndAddAction(Action::DeleteFromRecycleBin, p_menu); + if (selectedSize == 1) { + createAndAddAction(Action::CopyPath, p_menu); - p_menu->addSeparator(); + createAndAddAction(Action::OpenLocation, p_menu); - createAndAddAction(Action::Reload, p_menu); - - createAndAddAction(Action::ReloadIndex, p_menu); - - if (selectedSize == 1) { - p_menu->addSeparator(); - - createAndAddAction(Action::CopyPath, p_menu); - - createAndAddAction(Action::OpenLocation, p_menu); - } - } else { - createAndAddAction(Action::Open, p_menu); - - addOpenWithMenu(p_menu); - - p_menu->addSeparator(); - - if (selectedSize == 1 && p_node->isContainer()) { - createAndAddAction(Action::ExpandAll, p_menu); - } - - p_menu->addSeparator(); - - createAndAddAction(Action::NewNote, p_menu); - - createAndAddAction(Action::NewFolder, p_menu); - - p_menu->addSeparator(); - - createAndAddAction(Action::Copy, p_menu); - - createAndAddAction(Action::Cut, p_menu); - - if (selectedSize == 1 && isPasteOnNodeAvailable(p_node)) { - createAndAddAction(Action::Paste, p_menu); - } - - createAndAddAction(Action::Delete, p_menu); - - createAndAddAction(Action::RemoveFromConfig, p_menu); - - p_menu->addSeparator(); - - createAndAddAction(Action::Reload, p_menu); - - createAndAddAction(Action::ReloadIndex, p_menu); - - createAndAddAction(Action::Sort, p_menu); - - if (selectedSize == 1 - && m_notebook->tag() - && !p_node->isContainer()) { - p_menu->addSeparator(); - - createAndAddAction(Action::Tag, p_menu); - } - - p_menu->addSeparator(); - - createAndAddAction(Action::PinToQuickAccess, p_menu); - - if (selectedSize == 1) { - createAndAddAction(Action::CopyPath, p_menu); - - createAndAddAction(Action::OpenLocation, p_menu); - - createAndAddAction(Action::Properties, p_menu); - } + createAndAddAction(Action::Properties, p_menu); } } @@ -901,7 +793,7 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent) switch (p_act) { case Action::NewNote: act = new QAction(generateMenuActionIcon("new_note.svg"), - tr("New N&ote"), + tr("New &Note"), p_parent); connect(act, &QAction::triggered, this, []() { @@ -1033,47 +925,10 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent) }); break; - case Action::EmptyRecycleBin: - act = new QAction(tr("&Empty"), p_parent); - connect(act, &QAction::triggered, - this, [this]() { - auto rbNode = m_notebook->getRecycleBinNode().data(); - auto rbNodePath = rbNode->fetchAbsolutePath(); - int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Warning, - tr("Empty the recycle bin of this notebook?"), - tr("All files in recycle bin will be deleted permanently."), - tr("Location of recycle bin: %1").arg(rbNodePath)); - if (ret != QMessageBox::Ok) { - return; - } - - try { - m_notebook->emptyNode(rbNode, true); - } catch (Exception &p_e) { - MessageBoxHelper::notify(MessageBoxHelper::Critical, - tr("Failed to empty recycle bin (%1) (%2).").arg(rbNodePath, p_e.what()), - VNoteX::getInst().getMainWindow()); - } - - updateNode(rbNode); - }); - break; - case Action::Delete: act = new QAction(tr("&Delete"), p_parent); connect(act, &QAction::triggered, - this, [this]() { - removeSelectedNodes(false); - }); - break; - - case Action::DeleteFromRecycleBin: - // It is fine to have &D with Action::Delete since they won't be at the same context. - act = new QAction(tr("&Delete From Recycle Bin"), p_parent); - connect(act, &QAction::triggered, - this, [this]() { - removeSelectedNodes(true); - }); + this, &NotebookNodeExplorer::removeSelectedNodes); break; case Action::RemoveFromConfig: @@ -1417,21 +1272,12 @@ void NotebookNodeExplorer::selectNodes(const QVector &p_nodes) } } -void NotebookNodeExplorer::removeSelectedNodes(bool p_skipRecycleBin) +void NotebookNodeExplorer::removeSelectedNodes() { - QString text; - QString info; - if (p_skipRecycleBin) { - text = tr("Delete these folders and notes permanently?"); - info = tr("Files will be deleted permanently and could not be found even " - "in operating system's recycle bin."); - } else { - text = tr("Delete these folders and notes?"); - info = tr("Deleted files could be found in the recycle bin of notebook."); - } - + const QString text = tr("Delete these folders and notes?"); + const QString info = tr("Deleted files could be found in the recycle bin of notebook."); auto nodes = confirmSelectedNodes(tr("Confirm Deletion"), text, info); - removeNodes(nodes, p_skipRecycleBin, false); + removeNodes(nodes, false); } QVector NotebookNodeExplorer::confirmSelectedNodes(const QString &p_title, @@ -1471,9 +1317,7 @@ QVector NotebookNodeExplorer::confirmSelectedNodes(const QString &p_titl return nodesToDelete; } -void NotebookNodeExplorer::removeNodes(QVector p_nodes, - bool p_skipRecycleBin, - bool p_configOnly) +void NotebookNodeExplorer::removeNodes(QVector p_nodes, bool p_configOnly) { if (p_nodes.isEmpty()) { return; @@ -1494,8 +1338,8 @@ void NotebookNodeExplorer::removeNodes(QVector p_nodes, continue; } - if (p_configOnly || p_skipRecycleBin) { - m_notebook->removeNode(node, false, p_configOnly); + if (p_configOnly) { + m_notebook->removeNode(node, false, true); } else { m_notebook->moveNodeToRecycleBin(node); } @@ -1515,10 +1359,6 @@ void NotebookNodeExplorer::removeNodes(QVector p_nodes, updateNode(node); } - if (!p_configOnly && !p_skipRecycleBin && m_recycleBinNodeVisible) { - updateNode(m_notebook->getRecycleBinNode().data()); - } - VNoteX::getInst().showStatusMessageShort(tr("Deleted/Removed %n item(s)", "", nrDeleted)); } @@ -1527,7 +1367,7 @@ void NotebookNodeExplorer::removeSelectedNodesFromConfig() auto nodes = confirmSelectedNodes(tr("Confirm Removal"), tr("Remove these folders and notes from index?"), tr("Files are not touched but just removed from notebook index.")); - removeNodes(nodes, false, true); + removeNodes(nodes, true); } void NotebookNodeExplorer::filterAwayChildrenNodes(QVector &p_nodes) @@ -1569,29 +1409,6 @@ bool NotebookNodeExplorer::allSelectedItemsSameType() const } } - if (type == NodeData::NodeType::Node) { - bool hasNormalNode = false; - bool hasNodeInRecycleBin = false; - for (auto &item : items) { - auto node = getItemNodeData(item).getNode(); - if (m_notebook->isRecycleBinNode(node)) { - return false; - } else if (m_notebook->isNodeInRecycleBin(node)) { - if (hasNormalNode) { - return false; - } - - hasNodeInRecycleBin = true; - } else { - if (hasNodeInRecycleBin) { - return false; - } - - hasNormalNode = true; - } - } - } - return true; } @@ -1603,12 +1420,11 @@ void NotebookNodeExplorer::reload() void NotebookNodeExplorer::focusNormalNode() { auto item = m_masterExplorer->currentItem(); - if (item && (!m_recycleBinNodeVisible || item != m_masterExplorer->topLevelItem(0))) { - // Not recycle bin. + if (item) { return; } - m_masterExplorer->setCurrentItem(m_masterExplorer->topLevelItem(m_recycleBinNodeVisible ? 1 : 0)); + m_masterExplorer->setCurrentItem(m_masterExplorer->topLevelItem(0)); } void NotebookNodeExplorer::sortNodes(QVector> &p_nodes) const @@ -1688,16 +1504,6 @@ void NotebookNodeExplorer::sortNodes(QVector> &p_nodes, int } } -void NotebookNodeExplorer::setRecycleBinNodeVisible(bool p_visible) -{ - if (m_recycleBinNodeVisible == p_visible) { - return; - } - - m_recycleBinNodeVisible = p_visible; - reload(); -} - void NotebookNodeExplorer::setExternalFilesVisible(bool p_visible) { if (m_externalFilesVisible == p_visible) { @@ -1748,11 +1554,7 @@ void NotebookNodeExplorer::manualSort() const auto &children = parentNode->getChildrenRef(); for (int i = 0; i < children.size(); ++i) { const auto &child = children[i]; - if (m_notebook->isRecycleBinNode(child.data())) { - continue; - } - - bool selected = sortFolders ? child->isContainer() : !child->isContainer(); + const bool selected = sortFolders ? child->isContainer() : !child->isContainer(); if (selected) { selectedIdx.push_back(i); diff --git a/src/widgets/notebooknodeexplorer.h b/src/widgets/notebooknodeexplorer.h index b8aec079..227b51d3 100644 --- a/src/widgets/notebooknodeexplorer.h +++ b/src/widgets/notebooknodeexplorer.h @@ -103,8 +103,6 @@ namespace vnotex void reload(); - void setRecycleBinNodeVisible(bool p_visible); - void setViewOrder(int p_order); void setExternalFilesVisible(bool p_visible); @@ -146,9 +144,7 @@ namespace vnotex Copy, Cut, Paste, - EmptyRecycleBin, Delete, - DeleteFromRecycleBin, RemoveFromConfig, Sort, Reload, @@ -180,10 +176,6 @@ namespace vnotex void loadNode(QTreeWidgetItem *p_item, const QSharedPointer &p_node) const; - void loadRecycleBinNode(Node *p_node) const; - - void loadRecycleBinNode(QTreeWidgetItem *p_item, Node *p_node, int p_level) const; - void fillTreeItem(QTreeWidgetItem *p_item, Node *p_node, bool p_loaded) const; void fillTreeItem(QTreeWidgetItem *p_item, const QSharedPointer &p_node) const; @@ -225,7 +217,7 @@ namespace vnotex QPair, QVector>> getSelectedNodes() const; - void removeSelectedNodes(bool p_skipRecycleBin); + void removeSelectedNodes(); void removeSelectedNodesFromConfig(); @@ -241,8 +233,7 @@ namespace vnotex void selectNodes(const QVector &p_nodes); - // @p_skipRecycleBin is irrelevant if @p_configOnly is true. - void removeNodes(QVector p_nodes, bool p_skipRecycleBin, bool p_configOnly); + void removeNodes(QVector p_nodes, bool p_configOnly); void filterAwayChildrenNodes(QVector &p_nodes); @@ -251,7 +242,6 @@ namespace vnotex // Check if all selected items are the same type for operations. bool allSelectedItemsSameType() const; - // Skip the recycle bin node if possible. void focusNormalNode(); void sortNodes(QVector> &p_nodes) const; @@ -298,8 +288,6 @@ namespace vnotex QScopedPointer> m_navigationWrapper; - bool m_recycleBinNodeVisible = false; - int m_viewOrder = ViewOrder::OrderedByConfiguration; bool m_externalFilesVisible = true; @@ -312,7 +300,6 @@ namespace vnotex FileNode, InvalidFolderNode, InvalidFileNode, - RecycleBinNode, ExternalFolderNode, ExternalFileNode, MaxIcons diff --git a/src/widgets/searchpanel.cpp b/src/widgets/searchpanel.cpp index 4580e9f5..4e195f84 100644 --- a/src/widgets/searchpanel.cpp +++ b/src/widgets/searchpanel.cpp @@ -448,7 +448,7 @@ SearchState SearchPanel::search(const QSharedPointer &p_option) break; } auto folder = m_provider->getCurrentFolder(); - if (folder && (folder->isRoot() || notebook->isRecycleBinNode(folder))) { + if (folder && (folder->isRoot())) { folder = nullptr; } if (!folder) { diff --git a/src/widgets/tagexplorer.cpp b/src/widgets/tagexplorer.cpp index 91549ca8..a7f845b8 100644 --- a/src/widgets/tagexplorer.cpp +++ b/src/widgets/tagexplorer.cpp @@ -239,11 +239,6 @@ void TagExplorer::updateNodeList(const QString &p_tag) continue; } - if (m_notebook->isNodeInRecycleBin(node.data())) { - qDebug() << "skipped node in recycle bin" << p_tag << pa; - continue; - } - auto item = new QListWidgetItem(m_nodeList); item->setText(node->getName()); item->setToolTip(NotebookNodeExplorer::generateToolTip(node.data()));