support external nodes (#1718)

This commit is contained in:
Le Tan 2021-03-14 09:37:06 +08:00 committed by GitHub
parent aa00164dff
commit 9895207dd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 931 additions and 193 deletions

View File

@ -0,0 +1,34 @@
#include "externalnode.h"
#include "node.h"
#include <utils/pathutils.h>
using namespace vnotex;
ExternalNode::ExternalNode(Node *p_parent, const QString &p_name, Type p_type)
: m_parentNode(p_parent),
m_name(p_name),
m_type(p_type)
{
Q_ASSERT(m_parentNode);
}
Node *ExternalNode::getNode() const
{
return m_parentNode;
}
const QString &ExternalNode::getName() const
{
return m_name;
}
bool ExternalNode::isFolder() const
{
return m_type == Type::Folder;
}
QString ExternalNode::fetchAbsolutePath() const
{
return PathUtils::concatenateFilePath(m_parentNode->fetchAbsolutePath(), m_name);
}

View File

@ -0,0 +1,41 @@
#ifndef EXTERNALNODE_H
#define EXTERNALNODE_H
#include <QString>
namespace vnotex
{
class Node;
// External node not managed by VNote.
class ExternalNode
{
public:
enum class Type
{
File,
Folder
};
ExternalNode(Node *p_parent, const QString &p_name, Type p_type);
Node *getNode() const;
const QString &getName() const;
bool isFolder() const;
QString fetchAbsolutePath() const;
private:
// Parent node.
// We support only one level further the external folder.
Node *m_parentNode = nullptr;
QString m_name;
Type m_type = Type::File;
};
}
#endif // EXTERNALNODE_H

View File

@ -103,13 +103,13 @@ bool Node::containsChild(const QString &p_name, bool p_caseSensitive) const
bool Node::containsChild(const QSharedPointer<Node> &p_node) const
{
return getChildren().indexOf(p_node) != -1;
return m_children.indexOf(p_node) != -1;
}
QSharedPointer<Node> Node::findChild(const QString &p_name, bool p_caseSensitive) const
{
auto targetName = p_caseSensitive ? p_name : p_name.toLower();
for (auto &child : getChildren()) {
for (const auto &child : m_children) {
if (p_caseSensitive ? child->getName() == targetName
: child->getName().toLower() == targetName) {
return child;
@ -164,7 +164,12 @@ void Node::setModifiedTimeUtc()
m_modifiedTimeUtc = QDateTime::currentDateTimeUtc();
}
const QVector<QSharedPointer<Node>> &Node::getChildren() const
const QVector<QSharedPointer<Node>> &Node::getChildrenRef() const
{
return m_children;
}
QVector<QSharedPointer<Node>> Node::getChildren() const
{
return m_children;
}
@ -347,3 +352,54 @@ void Node::sortChildren(const QVector<int> &p_beforeIdx, const QVector<int> &p_a
save();
}
QVector<QSharedPointer<ExternalNode>> Node::fetchExternalChildren() const
{
return getConfigMgr()->fetchExternalChildren(const_cast<Node *>(this));
}
bool Node::containsContainerChild(const QString &p_name) const
{
// TODO: we assume that m_children is sorted first the container children.
for (auto &child : m_children) {
if (!child->isContainer()) {
break;
}
if (child->getName() == p_name) {
return true;
}
}
return false;
}
bool Node::containsContentChild(const QString &p_name) const
{
// TODO: we assume that m_children is sorted: first the container children then content children.
for (int i = m_children.size() - 1; i >= 0; --i) {
if (m_children[i]->isContainer()) {
break;
}
if (m_children[i]->getName() == p_name) {
return true;
}
}
return false;
}
bool Node::exists() const
{
return m_flags & Flag::Exists;
}
void Node::setExists(bool p_exists)
{
if (p_exists) {
m_flags |= Flag::Exists;
} else {
m_flags &= ~Flag::Exists;
}
}

View File

@ -15,6 +15,7 @@ namespace vnotex
class INotebookConfigMgr;
class INotebookBackend;
class File;
class ExternalNode;
// Used when add/new a node.
struct NodeParameters
@ -35,7 +36,9 @@ namespace vnotex
Content = 0x1,
// A node with children.
Container = 0x2,
ReadOnly = 0x4
ReadOnly = 0x4,
// Whether a node exists on disk.
Exists = 0x10
};
Q_DECLARE_FLAGS(Flags, Flag)
@ -86,6 +89,11 @@ namespace vnotex
bool hasContent() const;
// Whether the node exists on disk.
bool exists() const;
void setExists(bool p_exists);
Node::Flags getFlags() const;
Node::Use getUse() const;
@ -98,7 +106,8 @@ namespace vnotex
const QDateTime &getModifiedTimeUtc() const;
void setModifiedTimeUtc();
const QVector<QSharedPointer<Node>> &getChildren() const;
const QVector<QSharedPointer<Node>> &getChildrenRef() const;
QVector<QSharedPointer<Node>> getChildren() const;
int getChildrenCount() const;
QSharedPointer<Node> findChild(const QString &p_name, bool p_caseSensitive = true) const;
@ -107,12 +116,20 @@ namespace vnotex
bool containsChild(const QSharedPointer<Node> &p_node) const;
// Case sensitive.
bool containsContainerChild(const QString &p_name) const;
// Case sensitive.
bool containsContentChild(const QString &p_name) const;
void addChild(const QSharedPointer<Node> &p_node);
void insertChild(int p_idx, const QSharedPointer<Node> &p_node);
void removeChild(const QSharedPointer<Node> &p_node);
QVector<QSharedPointer<ExternalNode>> fetchExternalChildren() const;
void setParent(Node *p_parent);
Node *getParent() const;

View File

@ -161,7 +161,7 @@ const QSharedPointer<Node> &Notebook::getRootNode() const
QSharedPointer<Node> Notebook::getRecycleBinNode() const
{
auto root = getRootNode();
auto children = root->getChildren();
const auto &children = root->getChildrenRef();
auto it = std::find_if(children.begin(),
children.end(),
[this](const QSharedPointer<Node> &p_node) {
@ -203,7 +203,7 @@ QSharedPointer<Node> Notebook::loadNodeByPath(const QString &p_path)
relativePath = p_path;
}
return m_configMgr->loadNodeByPath(m_root, relativePath);
return m_configMgr->loadNodeByPath(getRootNode(), relativePath);
}
QSharedPointer<Node> Notebook::copyNodeAsChildOf(const QSharedPointer<Node> &p_src, Node *p_dest, bool p_move)
@ -227,17 +227,15 @@ QSharedPointer<Node> Notebook::copyNodeAsChildOf(const QSharedPointer<Node> &p_s
void Notebook::removeNode(const QSharedPointer<Node> &p_node, bool p_force, bool p_configOnly)
{
Q_ASSERT(p_node && !p_node->isRoot());
Q_ASSERT(p_node->getNotebook() == this);
m_configMgr->removeNode(p_node, p_force, p_configOnly);
}
void Notebook::removeNode(const Node *p_node, bool p_force, bool p_configOnly)
void Notebook::removeNode(Node *p_node, bool p_force, bool p_configOnly)
{
Q_ASSERT(p_node && !p_node->isRoot());
auto children = p_node->getParent()->getChildren();
auto it = std::find(children.begin(), children.end(), p_node);
Q_ASSERT(it != children.end());
removeNode(*it, p_force, p_configOnly);
Q_ASSERT(p_node);
removeNode(p_node->sharedFromThis(), p_force, p_configOnly);
}
bool Notebook::isRecycleBinNode(const Node *p_node) const
@ -261,22 +259,14 @@ bool Notebook::isNodeInRecycleBin(const Node *p_node) const
return false;
}
void Notebook::moveNodeToRecycleBin(const Node *p_node)
void Notebook::moveNodeToRecycleBin(Node *p_node)
{
Q_ASSERT(p_node && !p_node->isRoot());
auto children = p_node->getParent()->getChildren();
for (auto &child : children) {
if (p_node == child) {
moveNodeToRecycleBin(child);
return;
}
}
Q_ASSERT(false);
moveNodeToRecycleBin(p_node->sharedFromThis());
}
void Notebook::moveNodeToRecycleBin(const QSharedPointer<Node> &p_node)
{
Q_ASSERT(p_node && !p_node->isRoot());
auto destNode = getOrCreateRecycleBinDateNode();
copyNodeAsChildOf(p_node, destNode.data(), true);
}
@ -299,8 +289,9 @@ QSharedPointer<Node> Notebook::getOrCreateRecycleBinDateNode()
void Notebook::emptyNode(const Node *p_node, bool p_force)
{
// Copy the children.
auto children = p_node->getChildren();
for (auto &child : children) {
for (const auto &child : children) {
removeNode(child, p_force);
}
}
@ -355,3 +346,8 @@ QSharedPointer<Node> Notebook::copyAsNode(Node *p_parent,
{
return m_configMgr->copyAsNode(p_parent, p_flags, p_path);
}
void Notebook::reloadNode(Node *p_node)
{
m_configMgr->reloadNode(p_node);
}

View File

@ -99,11 +99,11 @@ namespace vnotex
// @p_configOnly: if true, will just remove node from config.
void removeNode(const QSharedPointer<Node> &p_node, bool p_force = false, bool p_configOnly = false);
void removeNode(const Node *p_node, bool p_force = false, bool p_configOnly = false);
void removeNode(Node *p_node, bool p_force = false, bool p_configOnly = false);
void moveNodeToRecycleBin(const QSharedPointer<Node> &p_node);
void moveNodeToRecycleBin(const Node *p_node);
void moveNodeToRecycleBin(Node *p_node);
// Move @p_filePath to the recycle bin, without adding it as a child node.
void moveFileToRecycleBin(const QString &p_filePath);
@ -127,6 +127,8 @@ namespace vnotex
bool isBuiltInFolder(const Node *p_node, const QString &p_name) const;
void reloadNode(Node *p_node);
static const QString c_defaultAttachmentFolder;
static const QString c_defaultImageFolder;

View File

@ -1,4 +1,5 @@
SOURCES += \
$$PWD/externalnode.cpp \
$$PWD/notebook.cpp \
$$PWD/bundlenotebookfactory.cpp \
$$PWD/notebookparameters.cpp \
@ -8,6 +9,7 @@ SOURCES += \
$$PWD/vxnodefile.cpp
HEADERS += \
$$PWD/externalnode.h \
$$PWD/notebook.h \
$$PWD/inotebookfactory.h \
$$PWD/bundlenotebookfactory.h \

View File

@ -67,6 +67,10 @@ namespace vnotex
virtual bool exists(const QString &p_path) const = 0;
virtual bool existsFile(const QString &p_path) const = 0;
virtual bool existsDir(const QString &p_path) const = 0;
virtual bool childExistsCaseInsensitive(const QString &p_dirPath, const QString &p_name) const = 0;
virtual bool isFile(const QString &p_path) const = 0;

View File

@ -86,6 +86,18 @@ bool LocalNotebookBackend::exists(const QString &p_path) const
return QFileInfo::exists(getFullPath(p_path));
}
bool LocalNotebookBackend::existsFile(const QString &p_path) const
{
QFileInfo fi(getFullPath(p_path));
return fi.exists() && fi.isFile();
}
bool LocalNotebookBackend::existsDir(const QString &p_path) const
{
QFileInfo fi(getFullPath(p_path));
return fi.exists() && fi.isDir();
}
bool LocalNotebookBackend::childExistsCaseInsensitive(const QString &p_dirPath, const QString &p_name) const
{
return FileUtils::childExistsCaseInsensitive(getFullPath(p_dirPath), p_name);

View File

@ -47,6 +47,10 @@ namespace vnotex
bool exists(const QString &p_path) const Q_DECL_OVERRIDE;
bool existsFile(const QString &p_path) const Q_DECL_OVERRIDE;
bool existsDir(const QString &p_path) const Q_DECL_OVERRIDE;
bool childExistsCaseInsensitive(const QString &p_dirPath, const QString &p_name) const Q_DECL_OVERRIDE;
bool isFile(const QString &p_path) const Q_DECL_OVERRIDE;

View File

@ -36,7 +36,7 @@ namespace vnotex
const QSharedPointer<INotebookBackend> &getBackend() const;
virtual QSharedPointer<Node> loadRootNode() const = 0;
virtual QSharedPointer<Node> loadRootNode() = 0;
virtual void loadNode(Node *p_node) const = 0;
virtual void saveNode(const Node *p_node) = 0;
@ -75,6 +75,10 @@ namespace vnotex
virtual QString fetchNodeAttachmentFolderPath(Node *p_node) = 0;
virtual QVector<QSharedPointer<ExternalNode>> fetchExternalChildren(Node *p_node) const = 0;
virtual void reloadNode(Node *p_node) = 0;
protected:
// Version of the config processing code.
virtual QString getCodeVersion() const;

View File

@ -8,6 +8,7 @@
#include <notebookbackend/inotebookbackend.h>
#include <notebook/notebookparameters.h>
#include <notebook/vxnode.h>
#include <notebook/externalnode.h>
#include <notebook/bundlenotebook.h>
#include <utils/utils.h>
#include <utils/fileutils.h>
@ -205,11 +206,12 @@ void VXNotebookConfigMgr::createEmptyRootNode()
writeNodeConfig(c_nodeConfigName, node);
}
QSharedPointer<Node> VXNotebookConfigMgr::loadRootNode() const
QSharedPointer<Node> VXNotebookConfigMgr::loadRootNode()
{
auto nodeConfig = readNodeConfig("");
QSharedPointer<Node> root = nodeConfigToNode(*nodeConfig, "", nullptr);
root->setUse(Node::Use::Root);
root->setExists(true);
Q_ASSERT(root->isLoaded());
if (!markRecycleBinNode(root)) {
@ -219,11 +221,16 @@ QSharedPointer<Node> VXNotebookConfigMgr::loadRootNode() const
return root;
}
bool VXNotebookConfigMgr::markRecycleBinNode(const QSharedPointer<Node> &p_root) const
bool VXNotebookConfigMgr::markRecycleBinNode(const QSharedPointer<Node> &p_root)
{
auto node = p_root->findChild(c_recycleBinFolderName,
FileUtils::isPlatformNameCaseSensitive());
if (node) {
if (!node->exists()) {
removeNode(node, true, true);
return false;
}
node->setUse(Node::Use::RecycleBin);
markNodeReadOnly(node.data());
return true;
@ -239,7 +246,7 @@ void VXNotebookConfigMgr::markNodeReadOnly(Node *p_node) const
}
p_node->setReadOnly(true);
for (auto &child : p_node->getChildren()) {
for (const auto &child : p_node->getChildrenRef()) {
markNodeReadOnly(child.data());
}
}
@ -305,16 +312,30 @@ void VXNotebookConfigMgr::loadFolderNode(Node *p_node, const NodeConfig &p_confi
{
QVector<QSharedPointer<Node>> children;
children.reserve(p_config.m_files.size() + p_config.m_folders.size());
const auto basePath = p_node->fetchPath();
for (const auto &folder : p_config.m_folders) {
if (folder.m_name.isEmpty()) {
// Skip empty name node.
qWarning() << "skipped loading node with empty name under" << p_node->fetchPath();
continue;
}
auto folderNode = QSharedPointer<VXNode>::create(folder.m_name,
getNotebook(),
p_node);
inheritNodeFlags(p_node, folderNode.data());
folderNode->setExists(getBackend()->existsDir(PathUtils::concatenateFilePath(basePath, folder.m_name)));
children.push_back(folderNode);
}
for (const auto &file : p_config.m_files) {
if (file.m_name.isEmpty()) {
// Skip empty name node.
qWarning() << "skipped loading node with empty name under" << p_node->fetchPath();
continue;
}
auto fileNode = QSharedPointer<VXNode>::create(file.m_id,
file.m_name,
file.m_createdTimeUtc,
@ -324,6 +345,7 @@ void VXNotebookConfigMgr::loadFolderNode(Node *p_node, const NodeConfig &p_confi
getNotebook(),
p_node);
inheritNodeFlags(p_node, fileNode.data());
fileNode->setExists(getBackend()->existsFile(PathUtils::concatenateFilePath(basePath, file.m_name)));
children.push_back(fileNode);
}
@ -338,7 +360,7 @@ QSharedPointer<Node> VXNotebookConfigMgr::newNode(Node *p_parent,
Node::Flags p_flags,
const QString &p_name)
{
Q_ASSERT(p_parent && p_parent->isContainer());
Q_ASSERT(p_parent && p_parent->isContainer() && !p_name.isEmpty());
QSharedPointer<Node> node;
@ -359,6 +381,7 @@ QSharedPointer<Node> VXNotebookConfigMgr::addAsNode(Node *p_parent,
{
Q_ASSERT(p_parent && p_parent->isContainer());
// TODO: reuse the config if available.
QSharedPointer<Node> node;
if (p_flags & Node::Flag::Content) {
Q_ASSERT(!(p_flags & Node::Flag::Container));
@ -407,6 +430,9 @@ QSharedPointer<Node> VXNotebookConfigMgr::newFileNode(Node *p_parent,
// Write empty file.
if (p_create) {
getBackend()->writeFile(node->fetchPath(), QString());
node->setExists(true);
} else {
node->setExists(getBackend()->existsFile(node->fetchPath()));
}
addChildNode(p_parent, node);
@ -433,6 +459,9 @@ QSharedPointer<Node> VXNotebookConfigMgr::newFolderNode(Node *p_parent,
// Make folder.
if (p_create) {
getBackend()->makePath(node->fetchPath());
node->setExists(true);
} else {
node->setExists(getBackend()->existsDir(node->fetchPath()));
}
writeNodeConfig(node.data());
@ -452,7 +481,7 @@ QSharedPointer<VXNotebookConfigMgr::NodeConfig> VXNotebookConfigMgr::nodeToNodeC
p_node->getCreatedTimeUtc(),
p_node->getModifiedTimeUtc());
for (const auto &child : p_node->getChildren()) {
for (const auto &child : p_node->getChildrenRef()) {
if (child->hasContent()) {
NodeFileConfig fileConfig;
fileConfig.m_name = child->getName();
@ -477,7 +506,7 @@ QSharedPointer<VXNotebookConfigMgr::NodeConfig> VXNotebookConfigMgr::nodeToNodeC
void VXNotebookConfigMgr::loadNode(Node *p_node) const
{
if (p_node->isLoaded()) {
if (p_node->isLoaded() || !p_node->exists()) {
return;
}
@ -513,7 +542,7 @@ void VXNotebookConfigMgr::addChildNode(Node *p_parent, const QSharedPointer<Node
{
if (p_child->isContainer()) {
int idx = 0;
auto children = p_parent->getChildren();
const auto &children = p_parent->getChildrenRef();
for (; idx < children.size(); ++idx) {
if (!children[idx]->isContainer()) {
break;
@ -558,6 +587,13 @@ QSharedPointer<Node> VXNotebookConfigMgr::copyNodeAsChildOf(const QSharedPointer
{
Q_ASSERT(p_dest->isContainer());
if (!p_src->exists()) {
if (p_move) {
p_src->getNotebook()->removeNode(p_src);
}
return nullptr;
}
QSharedPointer<Node> node;
if (p_src->isContainer()) {
node = copyFolderNodeAsChildOf(p_src, p_dest, p_move);
@ -605,6 +641,7 @@ QSharedPointer<Node> VXNotebookConfigMgr::copyFileNodeAsChildOf(const QSharedPoi
attachmentFolder,
notebook,
p_dest);
destNode->setExists(true);
addChildNode(p_dest, destNode);
writeNodeConfig(p_dest);
@ -643,6 +680,7 @@ QSharedPointer<Node> VXNotebookConfigMgr::copyFolderNodeAsChildOf(const QSharedP
p_src->getModifiedTimeUtc(),
QStringList(),
QVector<QSharedPointer<Node>>());
destNode->setExists(true);
writeNodeConfig(destNode.data());
@ -650,7 +688,8 @@ QSharedPointer<Node> VXNotebookConfigMgr::copyFolderNodeAsChildOf(const QSharedP
writeNodeConfig(p_dest);
// Copy children node.
for (const auto &childNode : p_src->getChildren()) {
auto children = p_src->getChildren();
for (const auto &childNode : children) {
copyNodeAsChildOf(childNode, destNode.data(), p_move);
}
@ -664,13 +703,18 @@ QSharedPointer<Node> VXNotebookConfigMgr::copyFolderNodeAsChildOf(const QSharedP
void VXNotebookConfigMgr::removeNode(const QSharedPointer<Node> &p_node, bool p_force, bool p_configOnly)
{
auto parentNode = p_node->getParent();
if (!p_configOnly) {
if (!p_configOnly && p_node->exists()) {
// Remove all children.
for (auto &childNode : p_node->getChildren()) {
auto children = p_node->getChildren();
for (const auto &childNode : children) {
removeNode(childNode, p_force, p_configOnly);
}
removeFilesOfNode(p_node.data(), p_force);
try {
removeFilesOfNode(p_node.data(), p_force);
} catch (Exception &p_e) {
qWarning() << "failed to remove files of node" << p_node->fetchPath() << p_e.what();
}
}
if (parentNode) {
@ -771,7 +815,9 @@ bool VXNotebookConfigMgr::isBuiltInFolder(const Node *p_node, const QString &p_n
const auto name = p_name.toLower();
if (name == c_recycleBinFolderName
|| name == getNotebook()->getImageFolder().toLower()
|| name == getNotebook()->getAttachmentFolder().toLower()) {
|| name == getNotebook()->getAttachmentFolder().toLower()
|| name == QStringLiteral("_v_images")
|| name == QStringLiteral("_v_attachments")) {
return true;
}
return BundleNotebookConfigMgr::isBuiltInFolder(p_node, p_name);
@ -779,25 +825,36 @@ bool VXNotebookConfigMgr::isBuiltInFolder(const Node *p_node, const QString &p_n
QSharedPointer<Node> VXNotebookConfigMgr::copyFileAsChildOf(const QString &p_srcPath, Node *p_dest)
{
// Copy source file itself.
auto destFilePath = PathUtils::concatenateFilePath(p_dest->fetchPath(),
// Skip copy if it already locates in dest folder.
auto destFilePath = PathUtils::concatenateFilePath(p_dest->fetchAbsolutePath(),
PathUtils::fileName(p_srcPath));
destFilePath = getBackend()->renameIfExistsCaseInsensitive(destFilePath);
getBackend()->copyFile(p_srcPath, destFilePath);
if (!PathUtils::areSamePaths(p_srcPath, destFilePath)) {
// Copy source file itself.
destFilePath = getBackend()->renameIfExistsCaseInsensitive(destFilePath);
getBackend()->copyFile(p_srcPath, destFilePath);
// Copy media files fetched from content.
ContentMediaUtils::copyMediaFiles(p_srcPath, getBackend().data(), destFilePath);
// Copy media files fetched from content.
ContentMediaUtils::copyMediaFiles(p_srcPath, getBackend().data(), destFilePath);
}
const auto name = PathUtils::fileName(destFilePath);
auto destNode = p_dest->findChild(name, true);
if (destNode) {
// Already have the node.
return destNode;
}
// Create a file node.
auto currentTime = QDateTime::currentDateTimeUtc();
auto destNode = QSharedPointer<VXNode>::create(getNotebook()->getAndUpdateNextNodeId(),
PathUtils::fileName(destFilePath),
currentTime,
currentTime,
QStringList(),
QString(),
getNotebook(),
p_dest);
destNode = QSharedPointer<VXNode>::create(getNotebook()->getAndUpdateNextNodeId(),
name,
currentTime,
currentTime,
QStringList(),
QString(),
getNotebook(),
p_dest);
destNode->setExists(true);
addChildNode(p_dest, destNode);
writeNodeConfig(p_dest);
@ -806,24 +863,33 @@ QSharedPointer<Node> VXNotebookConfigMgr::copyFileAsChildOf(const QString &p_src
QSharedPointer<Node> VXNotebookConfigMgr::copyFolderAsChildOf(const QString &p_srcPath, Node *p_dest)
{
auto destFolderPath = PathUtils::concatenateFilePath(p_dest->fetchPath(),
// Skip copy if it already locates in dest folder.
auto destFolderPath = PathUtils::concatenateFilePath(p_dest->fetchAbsolutePath(),
PathUtils::fileName(p_srcPath));
destFolderPath = getBackend()->renameIfExistsCaseInsensitive(destFolderPath);
if (!PathUtils::areSamePaths(p_srcPath, destFolderPath)) {
destFolderPath = getBackend()->renameIfExistsCaseInsensitive(destFolderPath);
// Copy folder.
getBackend()->copyDir(p_srcPath, destFolderPath);
// Copy folder.
getBackend()->copyDir(p_srcPath, destFolderPath);
}
const auto name = PathUtils::fileName(destFolderPath);
auto destNode = p_dest->findChild(name, true);
if (destNode) {
// Already have the node.
return destNode;
}
// Create a folder node.
auto notebook = getNotebook();
auto destNode = QSharedPointer<VXNode>::create(PathUtils::fileName(destFolderPath),
notebook,
p_dest);
destNode = QSharedPointer<VXNode>::create(name, notebook, p_dest);
auto currentTime = QDateTime::currentDateTimeUtc();
destNode->loadCompleteInfo(notebook->getAndUpdateNextNodeId(),
currentTime,
currentTime,
QStringList(),
QVector<QSharedPointer<Node>>());
destNode->setExists(true);
writeNodeConfig(destNode.data());
@ -839,3 +905,50 @@ void VXNotebookConfigMgr::inheritNodeFlags(const Node *p_node, Node *p_child) co
markNodeReadOnly(p_child);
}
}
QVector<QSharedPointer<ExternalNode>> VXNotebookConfigMgr::fetchExternalChildren(Node *p_node) const
{
Q_ASSERT(p_node->isContainer());
QVector<QSharedPointer<ExternalNode>> externalNodes;
auto dir = p_node->toDir();
// Folders.
{
const auto folders = dir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
for (const auto &folder : folders) {
if (isBuiltInFolder(p_node, folder)) {
continue;
}
if (p_node->containsContainerChild(folder)) {
continue;
}
externalNodes.push_back(QSharedPointer<ExternalNode>::create(p_node, folder, ExternalNode::Type::Folder));
}
}
// Files.
{
const auto files = dir.entryList(QDir::Files);
for (const auto &file : files) {
if (isBuiltInFile(p_node, file)) {
continue;
}
if (p_node->containsContentChild(file)) {
continue;
}
externalNodes.push_back(QSharedPointer<ExternalNode>::create(p_node, file, ExternalNode::Type::File));
}
}
return externalNodes;
}
void VXNotebookConfigMgr::reloadNode(Node *p_node)
{
// TODO.
}

View File

@ -31,7 +31,7 @@ namespace vnotex
void createEmptySkeleton(const NotebookParameters &p_paras) Q_DECL_OVERRIDE;
QSharedPointer<Node> loadRootNode() const Q_DECL_OVERRIDE;
QSharedPointer<Node> loadRootNode() Q_DECL_OVERRIDE;
void loadNode(Node *p_node) const Q_DECL_OVERRIDE;
void saveNode(const Node *p_node) Q_DECL_OVERRIDE;
@ -68,6 +68,10 @@ namespace vnotex
QString fetchNodeAttachmentFolderPath(Node *p_node) Q_DECL_OVERRIDE;
QVector<QSharedPointer<ExternalNode>> fetchExternalChildren(Node *p_node) const Q_DECL_OVERRIDE;
void reloadNode(Node *p_node) Q_DECL_OVERRIDE;
private:
// Config of a file child.
struct NodeFileConfig
@ -173,7 +177,7 @@ namespace vnotex
void removeFilesOfNode(Node *p_node, bool p_force);
bool markRecycleBinNode(const QSharedPointer<Node> &p_root) const;
bool markRecycleBinNode(const QSharedPointer<Node> &p_root);
void markNodeReadOnly(Node *p_node) const;

View File

@ -91,6 +91,9 @@ namespace vnotex
// @m_response of @p_event: true to continue the rename, false to cancel the rename.
void nodeAboutToRename(Node *p_node, const QSharedPointer<Event> &p_event);
// @m_response of @p_event: true to continue the reload, false to cancel the reload.
void nodeAboutToReload(Node *p_node, const QSharedPointer<Event> &p_event);
// Requested to open @p_filePath.
void openFileRequested(const QString &p_filePath, const QSharedPointer<FileOpenParameters> &p_paras);

View File

@ -24,8 +24,10 @@ void WidgetConfig::init(const QJsonObject &p_app,
m_findAndReplaceOptions = static_cast<FindOptions>(READINT(QStringLiteral("find_and_replace_options")));
m_noteExplorerViewOrder = READINT(QStringLiteral("note_explorer_view_order"));
m_noteExplorerRecycleBinNodeShown = READBOOL(QStringLiteral("note_explorer_recycle_bin_node_shown"));
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"));
}
QJsonObject WidgetConfig::toJson() const
@ -33,8 +35,10 @@ QJsonObject WidgetConfig::toJson() const
QJsonObject obj;
obj[QStringLiteral("outline_auto_expanded_level")] = m_outlineAutoExpandedLevel;
obj[QStringLiteral("find_and_replace_options")] = static_cast<int>(m_findAndReplaceOptions);
obj[QStringLiteral("note_explorer_view_order")] = m_noteExplorerViewOrder;
obj[QStringLiteral("note_explorer_recycle_bin_node_shown")] = m_noteExplorerRecycleBinNodeShown;
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;
return obj;
}
@ -58,22 +62,42 @@ void WidgetConfig::setFindAndReplaceOptions(FindOptions p_options)
updateConfig(m_findAndReplaceOptions, p_options, this);
}
int WidgetConfig::getNoteExplorerViewOrder() const
int WidgetConfig::getNodeExplorerViewOrder() const
{
return m_noteExplorerViewOrder;
return m_nodeExplorerViewOrder;
}
void WidgetConfig::setNoteExplorerViewOrder(int p_viewOrder)
void WidgetConfig::setNodeExplorerViewOrder(int p_viewOrder)
{
updateConfig(m_noteExplorerViewOrder, p_viewOrder, this);
updateConfig(m_nodeExplorerViewOrder, p_viewOrder, this);
}
bool WidgetConfig::isNoteExplorerRecycleBinNodeShown() const
bool WidgetConfig::isNodeExplorerRecycleBinNodeVisible() const
{
return m_noteExplorerRecycleBinNodeShown;
return m_nodeExplorerRecycleBinNodeVisible;
}
void WidgetConfig::setNoteExplorerRecycleBinNodeShown(bool p_shown)
void WidgetConfig::setNodeExplorerRecycleBinNodeVisible(bool p_visible)
{
updateConfig(m_noteExplorerRecycleBinNodeShown, p_shown, this);
updateConfig(m_nodeExplorerRecycleBinNodeVisible, p_visible, this);
}
bool WidgetConfig::isNodeExplorerExternalFilesVisible() const
{
return m_nodeExplorerExternalFilesVisible;
}
void WidgetConfig::setNodeExplorerExternalFilesVisible(bool p_visible)
{
updateConfig(m_nodeExplorerExternalFilesVisible, p_visible, this);
}
bool WidgetConfig::getNodeExplorerAutoImportExternalFilesEnabled() const
{
return m_nodeExplorerAutoImportExternalFilesEnabled;
}
void WidgetConfig::setNodeExplorerAutoImportExternalFilesEnabled(bool p_enabled)
{
updateConfig(m_nodeExplorerAutoImportExternalFilesEnabled, p_enabled, this);
}

View File

@ -24,20 +24,30 @@ namespace vnotex
FindOptions getFindAndReplaceOptions() const;
void setFindAndReplaceOptions(FindOptions p_options);
int getNoteExplorerViewOrder() const;
void setNoteExplorerViewOrder(int p_viewOrder);
int getNodeExplorerViewOrder() const;
void setNodeExplorerViewOrder(int p_viewOrder);
bool isNoteExplorerRecycleBinNodeShown() const;
void setNoteExplorerRecycleBinNodeShown(bool p_shown);
bool isNodeExplorerRecycleBinNodeVisible() const;
void setNodeExplorerRecycleBinNodeVisible(bool p_visible);
bool isNodeExplorerExternalFilesVisible() const;
void setNodeExplorerExternalFilesVisible(bool p_visible);
bool getNodeExplorerAutoImportExternalFilesEnabled() const;
void setNodeExplorerAutoImportExternalFilesEnabled(bool p_enabled);
private:
int m_outlineAutoExpandedLevel = 6;
FindOptions m_findAndReplaceOptions = FindOption::None;
int m_noteExplorerViewOrder = 0;
int m_nodeExplorerViewOrder = 0;
bool m_noteExplorerRecycleBinNodeShown = false;
bool m_nodeExplorerRecycleBinNodeVisible = false;
bool m_nodeExplorerExternalFilesVisible = true;
bool m_nodeExplorerAutoImportExternalFilesEnabled = true;
};
}

View File

@ -286,8 +286,10 @@
"outline_auto_expanded_level" : 6,
"//comment" : "Default find options in FindAndReplace",
"find_and_replace_options" : 16,
"//comment" : "View order of the note explorer",
"note_explorer_view_order" : 0,
"note_explorer_recycle_bin_node_shown" : false
"//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
}
}

View File

@ -224,7 +224,13 @@
},
"notebookexplorer" : {
"node_icon" : {
"fg" : "@base#icon#fg"
"fg" : "@base#icon#fg",
"invalid" : {
"fg" : "@base#icon#warning#fg"
}
},
"external_node_icon" : {
"fg" : "@base#icon#inactive#fg"
}
},
"viewsplit" : {

View File

@ -83,7 +83,13 @@
},
"notebookexplorer" : {
"node_icon" : {
"fg" : "@base#icon#fg"
"fg" : "@base#icon#fg",
"invalid" : {
"fg" : "@base#icon#warning#fg"
}
},
"external_node_icon" : {
"fg" : "@base#icon#disabled#fg"
}
},
"viewsplit" : {

View File

@ -220,7 +220,13 @@
},
"notebookexplorer" : {
"node_icon" : {
"fg" : "@base#icon#fg"
"fg" : "@base#icon#fg",
"invalid" : {
"fg" : "@base#icon#warning#fg"
}
},
"external_node_icon" : {
"fg" : "@base#icon#inactive#fg"
}
},
"viewsplit" : {

View File

@ -119,7 +119,7 @@ QStringList Exporter::doExport(const ExportOption &p_option, const QString &p_ou
}
p_folder->load();
const auto &children = p_folder->getChildren();
const auto &children = p_folder->getChildrenRef();
emit progressUpdated(0, children.size());
for (int i = 0; i < children.size(); ++i) {
if (checkAskedToStop()) {
@ -192,7 +192,7 @@ QStringList Exporter::doExport(const ExportOption &p_option, Notebook *p_noteboo
auto rootNode = p_notebook->getRootNode();
Q_ASSERT(rootNode->isLoaded());
const auto &children = rootNode->getChildren();
const auto &children = rootNode->getChildrenRef();
emit progressUpdated(0, children.size());
for (int i = 0; i < children.size(); ++i) {
if (checkAskedToStop()) {

View File

@ -61,6 +61,7 @@ bool WebViewExporter::doExport(const ExportOption &p_option,
m_webViewStates = WebViewState::Started;
auto baseUrl = PathUtils::pathToUrl(p_file->getContentPath());
m_viewer->adapter()->reset();
m_viewer->setHtml(m_htmlTemplate, baseUrl);
auto textContent = p_file->read();
@ -93,7 +94,7 @@ bool WebViewExporter::doExport(const ExportOption &p_option,
switch (p_option.m_targetFormat) {
case ExportFormat::HTML:
// TODO: not supported yet.
// TODO: MIME HTML format is not supported yet.
Q_ASSERT(!p_option.m_htmlOption.m_useMimeHtmlFormat);
ret = doExportHtml(p_option.m_htmlOption, p_outputFile, baseUrl);
break;

View File

@ -48,7 +48,7 @@ bool PathUtils::isEmptyDir(const QString &p_path)
QString PathUtils::concatenateFilePath(const QString &p_dirPath, const QString &p_name)
{
auto dirPath = cleanPath(p_dirPath);
QString dirPath = cleanPath(p_dirPath);
if (p_name.isEmpty()) {
return dirPath;
}
@ -57,7 +57,7 @@ QString PathUtils::concatenateFilePath(const QString &p_dirPath, const QString &
return p_name;
}
return dirPath + '/' + p_name;
return dirPath + "/" + p_name;
}
QString PathUtils::dirName(const QString &p_path)

View File

@ -356,3 +356,14 @@ void MarkdownViewerAdapter::setSavedContent(const QString &p_headContent,
{
emit contentReady(p_headContent, p_styleContent, p_content, p_bodyClassList);
}
void MarkdownViewerAdapter::reset()
{
m_revision = 0;
m_viewerReady = false;
m_pendingData.reset();
m_topLineNumber = -1;
m_headings.clear();
m_currentHeadingIndex = -1;
m_crossCopyTargets.clear();
}

View File

@ -123,6 +123,9 @@ namespace vnotex
void saveContent();
// Should be called before WebViewer.setHtml().
void reset();
// Functions to be called from web side.
public slots:
void setReady(bool p_ready);

View File

@ -497,10 +497,12 @@ void MarkdownViewWindow::syncViewerFromBuffer(bool p_syncPositionFromEditMode)
// TODO: Check buffer for last position recover.
// Use getPath() instead of getBasePath() to make in-page anchor work.
adapter()->reset();
m_viewer->setHtml(HtmlTemplateHelper::getMarkdownViewerTemplate(),
PathUtils::pathToUrl(buffer->getContentPath()));
adapter()->setText(m_bufferRevision, buffer->getContent(), lineNumber);
} else {
adapter()->reset();
m_viewer->setHtml("");
adapter()->setText(0, "", -1);
}

View File

@ -63,10 +63,15 @@ void NotebookExplorer::setupUI()
});
mainLayout->addWidget(m_selector);
const auto &widgetConfig = ConfigMgr::getInst().getWidgetConfig();
m_nodeExplorer = new NotebookNodeExplorer(this);
m_nodeExplorer->setRecycleBinNodeVisible(ConfigMgr::getInst().getWidgetConfig().isNoteExplorerRecycleBinNodeShown());
m_nodeExplorer->setRecycleBinNodeVisible(widgetConfig.isNodeExplorerRecycleBinNodeVisible());
m_nodeExplorer->setViewOrder(widgetConfig.getNodeExplorerViewOrder());
m_nodeExplorer->setExternalFilesVisible(widgetConfig.isNodeExplorerExternalFilesVisible());
connect(m_nodeExplorer, &NotebookNodeExplorer::nodeActivated,
&VNoteX::getInst(), &VNoteX::openNodeRequested);
connect(m_nodeExplorer, &NotebookNodeExplorer::fileActivated,
&VNoteX::getInst(), &VNoteX::openFileRequested);
connect(m_nodeExplorer, &NotebookNodeExplorer::nodeAboutToMove,
&VNoteX::getInst(), &VNoteX::nodeAboutToMove);
connect(m_nodeExplorer, &NotebookNodeExplorer::nodeAboutToRemove,
@ -78,6 +83,8 @@ void NotebookExplorer::setupUI()
TitleBar *NotebookExplorer::setupTitleBar(QWidget *p_parent)
{
const auto &widgetConfig = ConfigMgr::getInst().getWidgetConfig();
auto titleBar = new TitleBar(tr("Notebook"),
TitleBar::Action::Menu,
p_parent);
@ -95,11 +102,11 @@ 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(ConfigMgr::getInst().getWidgetConfig().isNoteExplorerRecycleBinNodeShown());
btn->defaultAction()->setChecked(widgetConfig.isNodeExplorerRecycleBinNodeVisible());
connect(btn, &QToolButton::triggered,
this, [this](QAction *p_act) {
const bool checked = p_act->isChecked();
ConfigMgr::getInst().getWidgetConfig().setNoteExplorerRecycleBinNodeShown(checked);
ConfigMgr::getInst().getWidgetConfig().setNodeExplorerRecycleBinNodeVisible(checked);
m_nodeExplorer->setRecycleBinNodeVisible(checked);
});
}
@ -113,6 +120,33 @@ TitleBar *NotebookExplorer::setupTitleBar(QWidget *p_parent)
dialog.exec();
});
titleBar->addMenuSeparator();
// External Files menu.
{
auto subMenu = titleBar->addMenuSubMenu(tr("External Files"));
auto showAct = titleBar->addMenuAction(
subMenu,
tr("Show External Files"),
titleBar,
[this](bool p_checked) {
ConfigMgr::getInst().getWidgetConfig().setNodeExplorerExternalFilesVisible(p_checked);
m_nodeExplorer->setExternalFilesVisible(p_checked);
});
showAct->setCheckable(true);
showAct->setChecked(widgetConfig.isNodeExplorerExternalFilesVisible());
auto importAct = titleBar->addMenuAction(
subMenu,
tr("Import External Files When Activated"),
titleBar,
[this](bool p_checked) {
ConfigMgr::getInst().getWidgetConfig().setNodeExplorerAutoImportExternalFilesEnabled(p_checked);
});
importAct->setCheckable(true);
importAct->setChecked(widgetConfig.getNodeExplorerAutoImportExternalFilesEnabled());
}
return titleBar;
}
@ -206,21 +240,7 @@ void NotebookExplorer::newNote()
Node *NotebookExplorer::currentExploredFolderNode() const
{
if (!m_currentNotebook) {
return nullptr;
}
auto node = m_nodeExplorer->getCurrentNode();
if (node) {
if (!node->isContainer()) {
node = node->getParent();
}
Q_ASSERT(node && node->isContainer());
} else {
node = m_currentNotebook->getRootNode().data();
}
return node;
return m_nodeExplorer->currentExploredFolderNode();
}
Node *NotebookExplorer::checkNotebookAndGetCurrentExploredFolderNode() const
@ -372,7 +392,7 @@ void NotebookExplorer::setupViewMenu(QMenu *p_menu)
act->setData(NotebookNodeExplorer::ViewOrder::OrderedByModifiedTimeReversed);
p_menu->addAction(act);
int viewOrder = ConfigMgr::getInst().getWidgetConfig().getNoteExplorerViewOrder();
int viewOrder = ConfigMgr::getInst().getWidgetConfig().getNodeExplorerViewOrder();
for (const auto &act : ag->actions()) {
if (act->data().toInt() == viewOrder) {
act->setChecked(true);
@ -382,8 +402,7 @@ void NotebookExplorer::setupViewMenu(QMenu *p_menu)
connect(ag, &QActionGroup::triggered,
this, [this](QAction *p_action) {
int order = p_action->data().toInt();
ConfigMgr::getInst().getWidgetConfig().setNoteExplorerViewOrder(order);
m_nodeExplorer->reload();
ConfigMgr::getInst().getWidgetConfig().setNodeExplorerViewOrder(order);
m_nodeExplorer->setViewOrder(order);
});
}

View File

@ -6,9 +6,11 @@
#include <QTreeWidget>
#include <QMenu>
#include <QAction>
#include <QSet>
#include <notebook/notebook.h>
#include <notebook/node.h>
#include <notebook/externalnode.h>
#include "exception.h"
#include "messageboxhelper.h"
#include "vnotex.h"
@ -29,18 +31,23 @@
#include <core/fileopenparameters.h>
#include <core/events.h>
#include <core/configmgr.h>
#include <core/widgetconfig.h>
using namespace vnotex;
const QString NotebookNodeExplorer::c_nodeIconForegroundName = "widgets#notebookexplorer#node_icon#fg";
QIcon NotebookNodeExplorer::s_folderNodeIcon;
QIcon NotebookNodeExplorer::s_fileNodeIcon;
QIcon NotebookNodeExplorer::s_invalidFolderNodeIcon;
QIcon NotebookNodeExplorer::s_invalidFileNodeIcon;
QIcon NotebookNodeExplorer::s_recycleBinNodeIcon;
QIcon NotebookNodeExplorer::s_externalFolderNodeIcon;
QIcon NotebookNodeExplorer::s_externalFileNodeIcon;
NotebookNodeExplorer::NodeData::NodeData()
{
}
@ -52,9 +59,9 @@ NotebookNodeExplorer::NodeData::NodeData(Node *p_node, bool p_loaded)
{
}
NotebookNodeExplorer::NodeData::NodeData(const QString &p_name)
: m_type(NodeType::Attachment),
m_name(p_name),
NotebookNodeExplorer::NodeData::NodeData(const QSharedPointer<ExternalNode> &p_externalNode)
: m_type(NodeType::ExternalNode),
m_externalNode(p_externalNode),
m_loaded(true)
{
}
@ -67,13 +74,12 @@ NotebookNodeExplorer::NodeData::NodeData(const NodeData &p_other)
m_node = p_other.m_node;
break;
case NodeType::Attachment:
m_name = p_other.m_name;
case NodeType::ExternalNode:
m_externalNode = p_other.m_externalNode;
break;
default:
m_node = p_other.m_node;
m_name = p_other.m_name;
Q_ASSERT(false);
break;
}
@ -96,13 +102,12 @@ NotebookNodeExplorer::NodeData &NotebookNodeExplorer::NodeData::operator=(const
m_node = p_other.m_node;
break;
case NodeType::Attachment:
m_name = p_other.m_name;
case NodeType::ExternalNode:
m_externalNode = p_other.m_externalNode;
break;
default:
m_node = p_other.m_node;
m_name = p_other.m_name;
Q_ASSERT(false);
break;
}
@ -121,9 +126,9 @@ bool NotebookNodeExplorer::NodeData::isNode() const
return m_type == NodeType::Node;
}
bool NotebookNodeExplorer::NodeData::isAttachment() const
bool NotebookNodeExplorer::NodeData::isExternalNode() const
{
return m_type == NodeType::Attachment;
return m_type == NodeType::ExternalNode;
}
NotebookNodeExplorer::NodeData::NodeType NotebookNodeExplorer::NodeData::getType() const
@ -137,17 +142,17 @@ Node *NotebookNodeExplorer::NodeData::getNode() const
return m_node;
}
const QString &NotebookNodeExplorer::NodeData::getName() const
ExternalNode *NotebookNodeExplorer::NodeData::getExternalNode() const
{
Q_ASSERT(isAttachment());
return m_name;
Q_ASSERT(isExternalNode());
return m_externalNode.data();
}
void NotebookNodeExplorer::NodeData::clear()
{
m_type = NodeType::Invalid;
m_node = nullptr;
m_name.clear();
m_externalNode.clear();
m_loaded = false;
}
@ -180,15 +185,26 @@ void NotebookNodeExplorer::initNodeIcons() const
return;
}
const QString nodeIconFgName = "widgets#notebookexplorer#node_icon#fg";
const QString invalidNodeIconFgName = "widgets#notebookexplorer#node_icon#invalid#fg";
const QString externalNodeIconFgName = "widgets#notebookexplorer#external_node_icon#fg";
const auto &themeMgr = VNoteX::getInst().getThemeMgr();
const auto fg = themeMgr.paletteColor(c_nodeIconForegroundName);
const auto fg = themeMgr.paletteColor(nodeIconFgName);
const auto invalidFg = themeMgr.paletteColor(invalidNodeIconFgName);
const auto externalFg = themeMgr.paletteColor(externalNodeIconFgName);
const QString folderIconName("folder_node.svg");
const QString fileIconName("file_node.svg");
const QString recycleBinIconName("recycle_bin.svg");
s_folderNodeIcon = IconUtils::fetchIcon(themeMgr.getIconFile(folderIconName), fg);
s_fileNodeIcon = IconUtils::fetchIcon(themeMgr.getIconFile(fileIconName), fg);
s_invalidFolderNodeIcon = IconUtils::fetchIcon(themeMgr.getIconFile(folderIconName), invalidFg);
s_invalidFileNodeIcon = IconUtils::fetchIcon(themeMgr.getIconFile(fileIconName), invalidFg);
s_recycleBinNodeIcon = IconUtils::fetchIcon(themeMgr.getIconFile(recycleBinIconName), fg);
s_externalFolderNodeIcon = IconUtils::fetchIcon(themeMgr.getIconFile(folderIconName), externalFg);
s_externalFileNodeIcon = IconUtils::fetchIcon(themeMgr.getIconFile(fileIconName), externalFg);
}
void NotebookNodeExplorer::setupUI()
@ -244,8 +260,8 @@ void NotebookNodeExplorer::setupMasterExplorer(QWidget *p_parent)
if (data.isNode()) {
createContextMenuOnNode(menu.data(), data.getNode());
} else if (data.isAttachment()) {
createContextMenuOnAttachment(menu.data(), data.getName());
} else if (data.isExternalNode()) {
createContextMenuOnExternalNode(menu.data(), data.getExternalNode());
}
}
@ -263,9 +279,23 @@ void NotebookNodeExplorer::setupMasterExplorer(QWidget *p_parent)
}
if (data.isNode()) {
if (checkInvalidNode(data.getNode())) {
return;
}
emit nodeActivated(data.getNode(), QSharedPointer<FileOpenParameters>::create());
} else if (data.isAttachment()) {
// TODO.
} else if (data.isExternalNode()) {
// Import to config first.
if (m_autoImportExternalFiles) {
auto importedNode = importToIndex(data.getExternalNode());
if (importedNode) {
emit nodeActivated(importedNode.data(), QSharedPointer<FileOpenParameters>::create());
}
return;
}
// Just open it.
emit fileActivated(data.getExternalNode()->fetchAbsolutePath(),
QSharedPointer<FileOpenParameters>::create());
}
});
}
@ -333,7 +363,7 @@ void NotebookNodeExplorer::generateNodeTree()
void NotebookNodeExplorer::loadRootNode(const Node *p_node) const
{
Q_ASSERT(p_node->isLoaded());
Q_ASSERT(p_node->isLoaded() && p_node->isContainer());
// Render recycle bin node first.
auto recycleBinNode = m_notebook->getRecycleBinNode();
@ -341,9 +371,20 @@ void NotebookNodeExplorer::loadRootNode(const Node *p_node) const
loadRecycleBinNode(recycleBinNode.data());
}
// External children.
if (m_externalFilesVisible) {
auto externalChildren = p_node->fetchExternalChildren();
// TODO: Sort external children.
for (const auto &child : externalChildren) {
auto item = new QTreeWidgetItem(m_masterExplorer);
loadNode(item, child);
}
}
// Children.
auto children = p_node->getChildren();
sortNodes(children);
for (auto &child : children) {
for (const auto &child : children) {
if (recycleBinNode == child) {
continue;
}
@ -378,15 +419,34 @@ void NotebookNodeExplorer::loadNode(QTreeWidgetItem *p_item, Node *p_node, int p
}
}
void NotebookNodeExplorer::loadNode(QTreeWidgetItem *p_item, const QSharedPointer<ExternalNode> &p_node) const
{
clearTreeWigetItemChildren(p_item);
fillTreeItem(p_item, p_node);
// No children for external node.
}
void NotebookNodeExplorer::loadChildren(QTreeWidgetItem *p_item, Node *p_node, int p_level) const
{
if (p_level < 0) {
return;
}
// External children.
if (m_externalFilesVisible && p_node->isContainer()) {
auto externalChildren = p_node->fetchExternalChildren();
// TODO: Sort external children.
for (const auto &child : externalChildren) {
auto item = new QTreeWidgetItem(p_item);
loadNode(item, child);
}
}
auto children = p_node->getChildren();
sortNodes(children);
for (auto &child : children) {
for (const auto &child : children) {
auto item = new QTreeWidgetItem(p_item);
loadNode(item, child.data(), p_level);
}
@ -434,22 +494,33 @@ void NotebookNodeExplorer::fillTreeItem(QTreeWidgetItem *p_item, Node *p_node, b
setItemNodeData(p_item, NodeData(p_node, p_loaded));
p_item->setText(Column::Name, p_node->getName());
p_item->setIcon(Column::Name, getNodeItemIcon(p_node));
p_item->setToolTip(Column::Name, p_node->getName());
p_item->setToolTip(Column::Name, p_node->exists() ? p_node->getName() : (tr("[Invalid] %1").arg(p_node->getName())));
}
QIcon NotebookNodeExplorer::getNodeItemIcon(const Node *p_node) const
void NotebookNodeExplorer::fillTreeItem(QTreeWidgetItem *p_item, const QSharedPointer<ExternalNode> &p_node) const
{
setItemNodeData(p_item, NodeData(p_node));
p_item->setText(Column::Name, p_node->getName());
p_item->setIcon(Column::Name, getNodeItemIcon(p_node.data()));
p_item->setToolTip(Column::Name, tr("[External] %1").arg(p_node->getName()));
}
const QIcon &NotebookNodeExplorer::getNodeItemIcon(const Node *p_node) const
{
if (p_node->hasContent()) {
return s_fileNodeIcon;
return p_node->exists() ? s_fileNodeIcon : s_invalidFileNodeIcon;
} else {
if (p_node->getUse() == Node::Use::RecycleBin) {
return s_recycleBinNodeIcon;
}
return s_folderNodeIcon;
return p_node->exists() ? s_folderNodeIcon : s_invalidFolderNodeIcon;
}
}
return QIcon();
const QIcon &NotebookNodeExplorer::getNodeItemIcon(const ExternalNode *p_node) const
{
return p_node->isFolder() ? s_externalFolderNodeIcon : s_externalFileNodeIcon;
}
Node *NotebookNodeExplorer::getCurrentNode() const
@ -457,7 +528,7 @@ Node *NotebookNodeExplorer::getCurrentNode() const
auto item = m_masterExplorer->currentItem();
if (item) {
auto data = getItemNodeData(item);
while (data.isAttachment()) {
while (item && !data.isNode()) {
item = item->parent();
if (item) {
data = getItemNodeData(item);
@ -687,6 +758,9 @@ void NotebookNodeExplorer::createContextMenuOnRoot(QMenu *p_menu)
p_menu->addSeparator();
act = createAction(Action::Reload, p_menu);
p_menu->addAction(act);
act = createAction(Action::OpenLocation, p_menu);
p_menu->addAction(act);
}
@ -698,6 +772,9 @@ void NotebookNodeExplorer::createContextMenuOnNode(QMenu *p_menu, const Node *p_
if (m_notebook->isRecycleBinNode(p_node)) {
// Recycle bin node.
act = createAction(Action::Reload, p_menu);
p_menu->addAction(act);
if (selectedSize == 1) {
act = createAction(Action::EmptyRecycleBin, p_menu);
p_menu->addAction(act);
@ -707,12 +784,22 @@ void NotebookNodeExplorer::createContextMenuOnNode(QMenu *p_menu, const Node *p_
}
} else if (m_notebook->isNodeInRecycleBin(p_node)) {
// Node in recycle bin.
act = createAction(Action::Open, p_menu);
p_menu->addAction(act);
p_menu->addSeparator();
act = createAction(Action::Cut, p_menu);
p_menu->addAction(act);
act = createAction(Action::DeleteFromRecycleBin, p_menu);
p_menu->addAction(act);
p_menu->addSeparator();
act = createAction(Action::Reload, p_menu);
p_menu->addAction(act);
if (selectedSize == 1) {
p_menu->addSeparator();
@ -723,6 +810,11 @@ void NotebookNodeExplorer::createContextMenuOnNode(QMenu *p_menu, const Node *p_
p_menu->addAction(act);
}
} else {
act = createAction(Action::Open, p_menu);
p_menu->addAction(act);
p_menu->addSeparator();
act = createAction(Action::NewNote, p_menu);
p_menu->addAction(act);
@ -750,6 +842,9 @@ void NotebookNodeExplorer::createContextMenuOnNode(QMenu *p_menu, const Node *p_
p_menu->addSeparator();
act = createAction(Action::Reload, p_menu);
p_menu->addAction(act);
act = createAction(Action::Sort, p_menu);
p_menu->addAction(act);
@ -768,10 +863,28 @@ void NotebookNodeExplorer::createContextMenuOnNode(QMenu *p_menu, const Node *p_
}
}
void NotebookNodeExplorer::createContextMenuOnAttachment(QMenu *p_menu, const QString &p_name)
void NotebookNodeExplorer::createContextMenuOnExternalNode(QMenu *p_menu, const ExternalNode *p_node)
{
Q_UNUSED(p_menu);
Q_UNUSED(p_name);
Q_UNUSED(p_node);
const int selectedSize = m_masterExplorer->selectedItems().size();
QAction *act = nullptr;
act = createAction(Action::Open, p_menu);
p_menu->addAction(act);
act = createAction(Action::ImportToConfig, p_menu);
p_menu->addAction(act);
if (selectedSize == 1) {
p_menu->addSeparator();
act = createAction(Action::CopyPath, p_menu);
p_menu->addAction(act);
act = createAction(Action::OpenLocation, p_menu);
p_menu->addAction(act);
}
}
static QIcon generateMenuActionIcon(const QString &p_name)
@ -811,7 +924,7 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent)
connect(act, &QAction::triggered,
this, [this]() {
auto node = getCurrentNode();
if (!node) {
if (checkInvalidNode(node)) {
return;
}
@ -834,15 +947,32 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent)
act = new QAction(tr("Open &Location"), p_parent);
connect(act, &QAction::triggered,
this, [this]() {
auto item = m_masterExplorer->currentItem();
if (!item) {
if (m_notebook) {
auto locationPath = m_notebook->getRootFolderAbsolutePath();
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(locationPath));
}
return;
}
auto data = getItemNodeData(item);
QString locationPath;
auto node = getCurrentNode();
if (node) {
if (data.isNode()) {
auto node = data.getNode();
if (checkInvalidNode(node)) {
return;
}
locationPath = node->fetchAbsolutePath();
if (!node->isContainer()) {
locationPath = PathUtils::parentDirPath(locationPath);
}
} else if (m_notebook) {
locationPath = m_notebook->getRootFolderAbsolutePath();
} else if (data.isExternalNode()) {
auto externalNode = data.getExternalNode();
locationPath = externalNode->fetchAbsolutePath();
if (!externalNode->isFolder()) {
locationPath = PathUtils::parentDirPath(locationPath);
}
}
if (!locationPath.isEmpty()) {
@ -855,9 +985,22 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent)
act = new QAction(tr("Cop&y Path"), p_parent);
connect(act, &QAction::triggered,
this, [this]() {
auto node = getCurrentNode();
if (node) {
auto nodePath = node->fetchAbsolutePath();
auto item = m_masterExplorer->currentItem();
if (!item) {
return;
}
auto data = getItemNodeData(item);
QString nodePath;
if (data.isNode()) {
auto node = data.getNode();
if (checkInvalidNode(node)) {
return;
}
nodePath = node->fetchAbsolutePath();
} else if (data.isExternalNode()) {
nodePath = data.getExternalNode()->fetchAbsolutePath();
}
if (!nodePath.isEmpty()) {
ClipboardUtils::setTextToClipboard(nodePath);
VNoteX::getInst().showStatusMessageShort(tr("Copied path: %1").arg(nodePath));
}
@ -906,9 +1049,8 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent)
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());
tr("Failed to empty recycle bin (%1) (%2).").arg(rbNodePath, p_e.what()),
VNoteX::getInst().getMainWindow());
}
updateNode(rbNode);
@ -935,18 +1077,42 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent)
case Action::RemoveFromConfig:
act = new QAction(tr("&Remove From Index"), p_parent);
connect(act, &QAction::triggered,
this, [this]() {
removeSelectedNodesFromConfig();
});
this, &NotebookNodeExplorer::removeSelectedNodesFromConfig);
break;
case Action::Sort:
act = new QAction(generateMenuActionIcon("sort.svg"), tr("&Sort"), p_parent);
connect(act, &QAction::triggered,
this, &NotebookNodeExplorer::manualSort);
break;
case Action::Reload:
act = new QAction(tr("Re&load"), p_parent);
connect(act, &QAction::triggered,
this, [this]() {
manualSort();
auto node = currentExploredFolderNode();
if (m_notebook && node) {
// TODO: emit signals to notify other components.
m_notebook->reloadNode(node);
}
updateNode(node);
});
break;
case Action::ImportToConfig:
act = new QAction(tr("&Import To Index"), p_parent);
connect(act, &QAction::triggered,
this, [this]() {
auto nodes = getSelectedNodes().second;
importToIndex(nodes);
});
break;
case Action::Open:
act = new QAction(tr("&Open"), p_parent);
connect(act, &QAction::triggered,
this, &NotebookNodeExplorer::openSelectedNodes);
break;
}
return act;
@ -954,7 +1120,7 @@ QAction *NotebookNodeExplorer::createAction(Action p_act, QObject *p_parent)
void NotebookNodeExplorer::copySelectedNodes(bool p_move)
{
auto nodes = getSelectedNodes();
auto nodes = getSelectedNodes().first;
if (nodes.isEmpty()) {
return;
}
@ -964,6 +1130,10 @@ void NotebookNodeExplorer::copySelectedNodes(bool p_move)
ClipboardData cdata(VNoteX::getInst().getInstanceId(),
p_move ? ClipboardData::MoveNode : ClipboardData::CopyNode);
for (auto node : nodes) {
if (checkInvalidNode(node)) {
continue;
}
auto item = QSharedPointer<NodeClipboardDataItem>::create(node->getNotebook()->getId(),
node->fetchPath());
cdata.addItem(item);
@ -976,15 +1146,17 @@ void NotebookNodeExplorer::copySelectedNodes(bool p_move)
VNoteX::getInst().showStatusMessageShort(tr("Copied %n item(s)", "", static_cast<int>(nrItems)));
}
QVector<Node *> NotebookNodeExplorer::getSelectedNodes() const
QPair<QVector<Node *>, QVector<ExternalNode *>> NotebookNodeExplorer::getSelectedNodes() const
{
QVector<Node *> nodes;
QPair<QVector<Node *>, QVector<ExternalNode *>> nodes;
auto items = m_masterExplorer->selectedItems();
for (auto &item : items) {
auto data = getItemNodeData(item);
if (data.isNode()) {
nodes.push_back(data.getNode());
nodes.first.push_back(data.getNode());
} else if (data.isExternalNode()) {
nodes.second.push_back(data.getExternalNode());
}
}
@ -1051,6 +1223,8 @@ void NotebookNodeExplorer::pasteNodesFromClipboard()
// Current node may be a file node.
if (!destNode->isContainer()) {
destNode = destNode->getParent();
} else if (checkInvalidNode(destNode)) {
return;
}
}
@ -1088,6 +1262,8 @@ void NotebookNodeExplorer::pasteNodesFromClipboard()
QVector<const Node *> pastedNodes;
QSet<Node *> nodesNeedUpdate;
for (auto srcNode : srcNodes) {
Q_ASSERT(srcNode->exists());
if (isMove) {
// Notice the view area to close any opened view windows.
auto event = QSharedPointer<Event>::create();
@ -1176,7 +1352,7 @@ QVector<Node *> NotebookNodeExplorer::confirmSelectedNodes(const QString &p_titl
const QString &p_text,
const QString &p_info) const
{
auto nodes = getSelectedNodes();
auto nodes = getSelectedNodes().first;
if (nodes.isEmpty()) {
return nodes;
}
@ -1270,7 +1446,7 @@ void NotebookNodeExplorer::removeSelectedNodesFromConfig()
void NotebookNodeExplorer::filterAwayChildrenNodes(QVector<Node *> &p_nodes)
{
for (int i = p_nodes.size() - 1; i > 0; --i) {
for (int i = p_nodes.size() - 1; i >= 0; --i) {
// Check if j is i's ancestor.
for (int j = p_nodes.size() - 1; j >= 0; --j) {
if (i == j) {
@ -1351,8 +1527,7 @@ void NotebookNodeExplorer::focusNormalNode()
void NotebookNodeExplorer::sortNodes(QVector<QSharedPointer<Node>> &p_nodes) const
{
int viewOrder = ConfigMgr::getInst().getWidgetConfig().getNoteExplorerViewOrder();
if (viewOrder == ViewOrder::OrderedByConfiguration) {
if (m_viewOrder == ViewOrder::OrderedByConfiguration) {
return;
}
@ -1369,10 +1544,10 @@ void NotebookNodeExplorer::sortNodes(QVector<QSharedPointer<Node>> &p_nodes) con
}
// Sort containers.
sortNodes(p_nodes, 0, firstFileIndex, viewOrder);
sortNodes(p_nodes, 0, firstFileIndex, m_viewOrder);
// Sort non-containers.
sortNodes(p_nodes, firstFileIndex, p_nodes.size(), viewOrder);
sortNodes(p_nodes, firstFileIndex, p_nodes.size(), m_viewOrder);
}
void NotebookNodeExplorer::sortNodes(QVector<QSharedPointer<Node>> &p_nodes, int p_start, int p_end, int p_viewOrder) const
@ -1437,6 +1612,25 @@ void NotebookNodeExplorer::setRecycleBinNodeVisible(bool p_visible)
reload();
}
void NotebookNodeExplorer::setExternalFilesVisible(bool p_visible)
{
if (m_externalFilesVisible == p_visible) {
return;
}
m_externalFilesVisible = p_visible;
reload();
}
void NotebookNodeExplorer::setAutoImportExternalFiles(bool p_enabled)
{
if (m_autoImportExternalFiles == p_enabled) {
return;
}
m_autoImportExternalFiles = p_enabled;
}
void NotebookNodeExplorer::manualSort()
{
auto node = getCurrentNode();
@ -1465,7 +1659,7 @@ void NotebookNodeExplorer::manualSort()
treeWidget->setColumnCount(2);
treeWidget->setHeaderLabels({tr("Name"), tr("Created Time"), tr("Modified Time")});
const auto &children = parentNode->getChildren();
const auto &children = parentNode->getChildrenRef();
for (int i = 0; i < children.size(); ++i) {
const auto &child = children[i];
if (m_notebook->isRecycleBinNode(child.data())) {
@ -1498,3 +1692,107 @@ void NotebookNodeExplorer::manualSort()
updateNode(parentNode);
}
}
Node *NotebookNodeExplorer::currentExploredFolderNode() const
{
if (!m_notebook) {
return nullptr;
}
auto node = getCurrentNode();
if (node) {
if (!node->isContainer()) {
node = node->getParent();
}
Q_ASSERT(node && node->isContainer());
} else {
node = m_notebook->getRootNode().data();
}
return node;
}
void NotebookNodeExplorer::setViewOrder(int p_order)
{
if (m_viewOrder == p_order) {
return;
}
m_viewOrder = p_order;
reload();
}
void NotebookNodeExplorer::openSelectedNodes()
{
// Support nodes and external nodes.
// Do nothing for folders.
auto selectedNodes = getSelectedNodes();
for (const auto &externalNode : selectedNodes.second) {
if (!externalNode->isFolder()) {
emit fileActivated(externalNode->fetchAbsolutePath(), QSharedPointer<FileOpenParameters>::create());
}
}
for (const auto &node : selectedNodes.first) {
if (checkInvalidNode(node)) {
continue;
}
if (node->hasContent()) {
emit nodeActivated(node, QSharedPointer<FileOpenParameters>::create());
}
}
}
QSharedPointer<Node> NotebookNodeExplorer::importToIndex(const ExternalNode *p_node)
{
auto node = m_notebook->addAsNode(p_node->getNode(),
p_node->isFolder() ? Node::Flag::Container : Node::Flag::Content,
p_node->getName(),
NodeParameters());
updateNode(p_node->getNode());
if (node) {
setCurrentNode(node.data());
}
return node;
}
void NotebookNodeExplorer::importToIndex(const QVector<ExternalNode *> &p_nodes)
{
QSet<Node *> nodesToUpdate;
Node *currentNode = nullptr;
for (auto externalNode : p_nodes) {
auto node = m_notebook->addAsNode(externalNode->getNode(),
externalNode->isFolder() ? Node::Flag::Container : Node::Flag::Content,
externalNode->getName(),
NodeParameters());
nodesToUpdate.insert(externalNode->getNode());
currentNode = node.data();
}
for (auto node : nodesToUpdate) {
updateNode(node);
}
if (currentNode) {
setCurrentNode(currentNode);
}
}
bool NotebookNodeExplorer::checkInvalidNode(const Node *p_node) const
{
if (!p_node) {
return true;
}
if (!p_node->exists()) {
MessageBoxHelper::notify(MessageBoxHelper::Warning,
tr("Invalid node (%1).").arg(p_node->getName()),
tr("Please check if the node exists on the disk."),
p_node->fetchAbsolutePath(),
VNoteX::getInst().getMainWindow());
return true;
}
return false;
}

View File

@ -5,6 +5,7 @@
#include <QSharedPointer>
#include <QHash>
#include <QScopedPointer>
#include <QPair>
#include "qtreewidgetstatecache.h"
#include "clipboarddata.h"
@ -22,6 +23,7 @@ namespace vnotex
class TreeWidget;
struct FileOpenParameters;
class Event;
class ExternalNode;
class NotebookNodeExplorer : public QWidget
{
@ -32,13 +34,13 @@ namespace vnotex
class NodeData
{
public:
enum class NodeType { Node, Attachment, Invalid };
enum class NodeType { Node, ExternalNode, Invalid };
NodeData();
explicit NodeData(Node *p_node, bool p_loaded);
explicit NodeData(const QString &p_name);
explicit NodeData(const QSharedPointer<ExternalNode> &p_externalNode);
NodeData(const NodeData &p_other);
@ -50,13 +52,13 @@ namespace vnotex
bool isNode() const;
bool isAttachment() const;
bool isExternalNode() const;
NodeData::NodeType getType() const;
Node *getNode() const;
const QString &getName() const;
ExternalNode *getExternalNode() const;
void clear();
@ -67,11 +69,9 @@ namespace vnotex
private:
NodeType m_type = NodeType::Invalid;
union
{
Node *m_node = nullptr;
QString m_name;
};
Node *m_node = nullptr;
QSharedPointer<ExternalNode> m_externalNode;
bool m_loaded = false;
};
@ -104,10 +104,18 @@ namespace vnotex
void setRecycleBinNodeVisible(bool p_visible);
void setViewOrder(int p_order);
void setExternalFilesVisible(bool p_visible);
void setAutoImportExternalFiles(bool p_enabled);
Node *currentExploredFolderNode() const;
signals:
void nodeActivated(Node *p_node, const QSharedPointer<FileOpenParameters> &p_paras);
void fileActivated(const QString &p_path);
void fileActivated(const QString &p_path, const QSharedPointer<FileOpenParameters> &p_paras);
// @m_response of @p_event: true to continue the move, false to cancel the move.
void nodeAboutToMove(Node *p_node, const QSharedPointer<Event> &p_event);
@ -132,7 +140,10 @@ namespace vnotex
Delete,
DeleteFromRecycleBin,
RemoveFromConfig,
Sort
Sort,
Reload,
ImportToConfig,
Open
};
void setupUI();
@ -149,13 +160,19 @@ namespace vnotex
void loadChildren(QTreeWidgetItem *p_item, Node *p_node, int p_level) const;
void loadNode(QTreeWidgetItem *p_item, const QSharedPointer<ExternalNode> &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;
QIcon getNodeItemIcon(const Node *p_node) const;
void fillTreeItem(QTreeWidgetItem *p_item, const QSharedPointer<ExternalNode> &p_node) const;
const QIcon &getNodeItemIcon(const Node *p_node) const;
const QIcon &getNodeItemIcon(const ExternalNode *p_node) const;
void initNodeIcons() const;
@ -177,7 +194,7 @@ namespace vnotex
void createContextMenuOnNode(QMenu *p_menu, const Node *p_node);
void createContextMenuOnAttachment(QMenu *p_menu, const QString &p_name);
void createContextMenuOnExternalNode(QMenu *p_menu, const ExternalNode *p_node);
// Factory function to create action.
QAction *createAction(Action p_act, QObject *p_parent);
@ -186,8 +203,7 @@ namespace vnotex
void pasteNodesFromClipboard();
// Only return selected Nodes.
QVector<Node *> getSelectedNodes() const;
QPair<QVector<Node *>, QVector<ExternalNode *>> getSelectedNodes() const;
void removeSelectedNodes(bool p_skipRecycleBin);
@ -226,6 +242,16 @@ namespace vnotex
// Sort nodes in config file.
void manualSort();
void openSelectedNodes();
QSharedPointer<Node> importToIndex(const ExternalNode *p_node);
void importToIndex(const QVector<ExternalNode *> &p_nodes);
// Check whether @p_node is a valid node. Will notify user.
// Return true if it is invalid.
bool checkInvalidNode(const Node *p_node) const;
static NotebookNodeExplorer::NodeData getItemNodeData(const QTreeWidgetItem *p_item);
static void setItemNodeData(QTreeWidgetItem *p_item, const NodeData &p_data);
@ -242,11 +268,25 @@ namespace vnotex
bool m_recycleBinNodeVisible = false;
int m_viewOrder = ViewOrder::OrderedByConfiguration;
bool m_externalFilesVisible = true;
bool m_autoImportExternalFiles = true;
static QIcon s_folderNodeIcon;
static QIcon s_fileNodeIcon;
static QIcon s_invalidFolderNodeIcon;
static QIcon s_invalidFileNodeIcon;
static QIcon s_recycleBinNodeIcon;
static const QString c_nodeIconForegroundName;
static QIcon s_externalFolderNodeIcon;
static QIcon s_externalFileNodeIcon;
};
}

View File

@ -127,6 +127,11 @@ QAction *TitleBar::addMenuAction(const QString &p_iconName, const QString &p_tex
return act;
}
QMenu *TitleBar::addMenuSubMenu(const QString &p_text)
{
return m_menu->addMenu(p_text);
}
void TitleBar::addMenuSeparator()
{
Q_ASSERT(m_menu);

View File

@ -39,6 +39,11 @@ namespace vnotex
template <typename Functor>
QAction *addMenuAction(const QString &p_text, const QObject *p_context, Functor p_functor);
template <typename Functor>
QAction *addMenuAction(QMenu *p_subMenu, const QString &p_text, const QObject *p_context, Functor p_functor);
QMenu *addMenuSubMenu(const QString &p_text);
void addMenuSeparator();
protected:
@ -91,6 +96,14 @@ namespace vnotex
auto act = m_menu->addAction(p_text, p_context, p_functor);
return act;
}
template <typename Functor>
QAction *TitleBar::addMenuAction(QMenu *p_subMenu, const QString &p_text, const QObject *p_context, Functor p_functor)
{
Q_ASSERT(p_subMenu->parent() == m_menu);
auto act = p_subMenu->addAction(p_text, p_context, p_functor);
return act;
}
} // ns vnotex
#endif // TITLEBAR_H