mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
implement VDirectoryTree logics
Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
parent
d8c5114c1b
commit
09f6415536
@ -16,12 +16,14 @@ SOURCES += main.cpp\
|
|||||||
vmainwindow.cpp \
|
vmainwindow.cpp \
|
||||||
vdirectorytree.cpp \
|
vdirectorytree.cpp \
|
||||||
vnote.cpp \
|
vnote.cpp \
|
||||||
vnotebook.cpp
|
vnotebook.cpp \
|
||||||
|
vnewdirdialog.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
vnote.h \
|
vnote.h \
|
||||||
vnotebook.h
|
vnotebook.h \
|
||||||
|
vnewdirdialog.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc
|
vnote.qrc
|
||||||
|
@ -1,12 +1,462 @@
|
|||||||
#include <QtGui>
|
#include <QtWidgets>
|
||||||
|
#include <QJsonObject>
|
||||||
#include "vdirectorytree.h"
|
#include "vdirectorytree.h"
|
||||||
|
#include "vnewdirdialog.h"
|
||||||
|
|
||||||
VDirectoryTree::VDirectoryTree(QWidget *parent) : QTreeWidget(parent)
|
VDirectoryTree::VDirectoryTree(const QString &dirConfigFileName, QWidget *parent)
|
||||||
|
: QTreeWidget(parent), dirConfigFileName(dirConfigFileName)
|
||||||
{
|
{
|
||||||
|
setColumnCount(1);
|
||||||
|
setHeaderHidden(true);
|
||||||
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
initialActions();
|
||||||
|
|
||||||
|
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
|
||||||
|
this, SLOT(updateItemSubtree(QTreeWidgetItem*)));
|
||||||
|
connect(this, SIGNAL(customContextMenuRequested(QPoint)),
|
||||||
|
this, SLOT(contextMenuRequested(QPoint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::initialActions()
|
||||||
|
{
|
||||||
|
newRootDirAct = new QAction(tr("New &root directory"), this);
|
||||||
|
newRootDirAct->setStatusTip(tr("Create a new root directory in current notebook"));
|
||||||
|
connect(newRootDirAct, &QAction::triggered,
|
||||||
|
this, &VDirectoryTree::newRootDirectory);
|
||||||
|
|
||||||
|
newSiblingDirAct = new QAction(tr("New &sibling directory"), this);
|
||||||
|
newSiblingDirAct->setStatusTip(tr("Create a new sibling directory at current level"));
|
||||||
|
connect(newSiblingDirAct, &QAction::triggered,
|
||||||
|
this, &VDirectoryTree::newSiblingDirectory);
|
||||||
|
|
||||||
|
newSubDirAct = new QAction(tr("&New sub-directory"), this);
|
||||||
|
newSubDirAct->setStatusTip(tr("Create a new sub-directory"));
|
||||||
|
connect(newSubDirAct, &QAction::triggered,
|
||||||
|
this, &VDirectoryTree::newSubDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VDirectoryTree::setTreePath(const QString& path)
|
void VDirectoryTree::setTreePath(const QString& path)
|
||||||
{
|
{
|
||||||
|
if (path == treePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
treePath = path;
|
treePath = path;
|
||||||
qDebug() << "set directory tree path:" << path;
|
qDebug() << "set directory tree path:" << path;
|
||||||
|
|
||||||
|
updateDirectoryTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VDirectoryTree::validatePath(const QString &path)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString configFile = dir.filePath(dirConfigFileName);
|
||||||
|
QFileInfo fileInfo(configFile);
|
||||||
|
return fileInfo.exists() && fileInfo.isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::updateDirectoryTree()
|
||||||
|
{
|
||||||
|
updateDirectoryTreeTopLevel();
|
||||||
|
|
||||||
|
int nrTopLevelItems = topLevelItemCount();
|
||||||
|
for (int i = 0; i < nrTopLevelItems; ++i) {
|
||||||
|
QTreeWidgetItem *item = topLevelItem(i);
|
||||||
|
Q_ASSERT(item);
|
||||||
|
updateDirectoryTreeOne(*item, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// QJsonObject stored in each item's data[UserRole]:
|
||||||
|
// 1. @item's related item in its parent's [sub_directories] section;
|
||||||
|
// 2. "relative_path": the path where this item exists, relative to the treePath.
|
||||||
|
void VDirectoryTree::fillDirectoryTreeItem(QTreeWidgetItem &item, QJsonObject itemJson, const QString &relativePath)
|
||||||
|
{
|
||||||
|
item.setText(0, itemJson["name"].toString());
|
||||||
|
QString description = itemJson["description"].toString();
|
||||||
|
if (!description.isEmpty()) {
|
||||||
|
item.setToolTip(0, description);
|
||||||
|
}
|
||||||
|
itemJson["relative_path"] = relativePath;
|
||||||
|
item.setData(0, Qt::UserRole, itemJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem* VDirectoryTree::insertDirectoryTreeItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding,
|
||||||
|
const QJsonObject &newItem)
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
QString relativePath;
|
||||||
|
if (parent) {
|
||||||
|
if (preceding) {
|
||||||
|
item = new QTreeWidgetItem(parent, preceding);
|
||||||
|
} else {
|
||||||
|
item = new QTreeWidgetItem(parent);
|
||||||
|
}
|
||||||
|
QJsonObject parentJson = parent->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
Q_ASSERT(!parentJson.isEmpty());
|
||||||
|
QString parentRelativePath = parentJson["relative_path"].toString();
|
||||||
|
QString parentName = parentJson["name"].toString();
|
||||||
|
relativePath = QDir(parentRelativePath).filePath(parentName);
|
||||||
|
} else {
|
||||||
|
if (preceding) {
|
||||||
|
item = new QTreeWidgetItem(this, preceding);
|
||||||
|
} else {
|
||||||
|
item = new QTreeWidgetItem(this);
|
||||||
|
}
|
||||||
|
relativePath = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
fillDirectoryTreeItem(*item, newItem, relativePath);
|
||||||
|
qDebug() << "insert new Item name:" << newItem["name"].toString()
|
||||||
|
<< "relative_path:" << relativePath;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::updateDirectoryTreeTopLevel()
|
||||||
|
{
|
||||||
|
const QString &path = treePath;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (!validatePath(path)) {
|
||||||
|
qDebug() << "invalid notebook path:" << path;
|
||||||
|
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook path."));
|
||||||
|
msgBox.setInformativeText(QString("Notebook path \"%1\" either does not exist or is not valid.")
|
||||||
|
.arg(path));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject configJson = readDirectoryConfig(path);
|
||||||
|
if (!validateDirConfigFile(configJson)) {
|
||||||
|
qDebug() << "invalid notebook configuration for path:" << path;
|
||||||
|
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook configuration."));
|
||||||
|
msgBox.setInformativeText(QString("Notebook path \"%1\" does not contain a valid configuration file.")
|
||||||
|
.arg(path));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle sub_directories section
|
||||||
|
QJsonArray dirJson = configJson["sub_directories"].toArray();
|
||||||
|
QTreeWidgetItem *preItem = NULL;
|
||||||
|
for (int i = 0; i < dirJson.size(); ++i) {
|
||||||
|
QJsonObject dirItem = dirJson[i].toObject();
|
||||||
|
QTreeWidgetItem *treeItem = insertDirectoryTreeItem(NULL, preItem, dirItem);
|
||||||
|
preItem = treeItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "updated" << dirJson.size() << "top-level items";
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::updateDirectoryTreeOne(QTreeWidgetItem &parent, int depth)
|
||||||
|
{
|
||||||
|
Q_ASSERT(parent.childCount() == 0);
|
||||||
|
// Going deep enough
|
||||||
|
if (depth <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QJsonObject parentJson = parent.data(0, Qt::UserRole).toJsonObject();
|
||||||
|
QString relativePath = QDir(parentJson["relative_path"].toString()).filePath(parentJson["name"].toString());
|
||||||
|
QString path(QDir::cleanPath(treePath + QDir::separator() + relativePath));
|
||||||
|
if (!validatePath(path)) {
|
||||||
|
qDebug() << "invalide notebook directory:" << path;
|
||||||
|
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory."));
|
||||||
|
msgBox.setInformativeText(QString("Notebook directory \"%1\" either does not exist or is not a valid notebook directory.")
|
||||||
|
.arg(path));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject configJson = readDirectoryConfig(path);
|
||||||
|
if (!validateDirConfigFile(configJson)) {
|
||||||
|
qDebug() << "invalid notebook configuration for directory:" << path;
|
||||||
|
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory configuration."));
|
||||||
|
msgBox.setInformativeText(QString("Notebook path \"%1\" does not contain a valid configuration file.")
|
||||||
|
.arg(path));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle sub_directories section
|
||||||
|
QJsonArray dirJson = configJson["sub_directories"].toArray();
|
||||||
|
QTreeWidgetItem *preItem = NULL;
|
||||||
|
for (int i = 0; i < dirJson.size(); ++i) {
|
||||||
|
QJsonObject dirItem = dirJson[i].toObject();
|
||||||
|
QTreeWidgetItem *treeItem = insertDirectoryTreeItem(&parent, preItem, dirItem);
|
||||||
|
preItem = treeItem;
|
||||||
|
|
||||||
|
// Update its sub-directory recursively
|
||||||
|
updateDirectoryTreeOne(*treeItem, depth - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject VDirectoryTree::readDirectoryConfig(const QString &path)
|
||||||
|
{
|
||||||
|
QString configFile = QDir(path).filePath(dirConfigFileName);
|
||||||
|
|
||||||
|
qDebug() << "read config file:" << configFile;
|
||||||
|
QFile config(configFile);
|
||||||
|
if (!config.open(QIODevice::ReadOnly)) {
|
||||||
|
qWarning() << "error: fail to read directory configuration file:"
|
||||||
|
<< configFile;
|
||||||
|
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"),
|
||||||
|
QString("Could not read directory configuration file \"%1\"")
|
||||||
|
.arg(dirConfigFileName));
|
||||||
|
msgBox.setInformativeText(QString("Notebook directory \"%1\" may be corrupted").arg(path));
|
||||||
|
msgBox.exec();
|
||||||
|
return QJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray configData = config.readAll();
|
||||||
|
return QJsonDocument::fromJson(configData).object();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VDirectoryTree::writeDirectoryConfig(const QString &path, const QJsonObject &configJson)
|
||||||
|
{
|
||||||
|
QString configFile = QDir(path).filePath(dirConfigFileName);
|
||||||
|
|
||||||
|
qDebug() << "write config file:" << configFile;
|
||||||
|
QFile config(configFile);
|
||||||
|
if (!config.open(QIODevice::WriteOnly)) {
|
||||||
|
qWarning() << "error: fail to open directory configuration file for write:"
|
||||||
|
<< configFile;
|
||||||
|
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"),
|
||||||
|
QString("Could not write directory configuration file \"%1\"")
|
||||||
|
.arg(dirConfigFileName));
|
||||||
|
msgBox.exec();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonDocument configDoc(configJson);
|
||||||
|
config.write(configDoc.toJson());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VDirectoryTree::deleteDirectoryConfig(const QString &path)
|
||||||
|
{
|
||||||
|
QString configFile = QDir(path).filePath(dirConfigFileName);
|
||||||
|
|
||||||
|
QFile config(configFile);
|
||||||
|
if (!config.remove()) {
|
||||||
|
qWarning() << "error: fail to delete directory configuration file:"
|
||||||
|
<< configFile;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
qDebug() << "delete config file:" << configFile;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VDirectoryTree::validateDirConfigFile(const QJsonObject &configJson)
|
||||||
|
{
|
||||||
|
if (configJson.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!configJson.contains("version") || !configJson.contains("name")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::updateItemSubtree(QTreeWidgetItem *item)
|
||||||
|
{
|
||||||
|
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
Q_ASSERT(!itemJson.isEmpty());
|
||||||
|
int nrChild = item->childCount();
|
||||||
|
if (nrChild == 0) {
|
||||||
|
updateDirectoryTreeOne(*item, 2);
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < nrChild; ++i) {
|
||||||
|
QTreeWidgetItem *childItem = item->child(i);
|
||||||
|
if (childItem->childCount() > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
updateDirectoryTreeOne(*childItem, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::contextMenuRequested(QPoint pos)
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *item = itemAt(pos);
|
||||||
|
QMenu menu(this);
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
// Context menu on the free space of the QTreeWidget
|
||||||
|
menu.addAction(newRootDirAct);
|
||||||
|
} else {
|
||||||
|
// Context menu on a QTreeWidgetItem
|
||||||
|
if (item->parent()) {
|
||||||
|
// Low-level item
|
||||||
|
menu.addAction(newSubDirAct);
|
||||||
|
menu.addAction(newSiblingDirAct);
|
||||||
|
} else {
|
||||||
|
// Top-level item
|
||||||
|
menu.addAction(newRootDirAct);
|
||||||
|
menu.addAction(newSubDirAct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menu.exec(mapToGlobal(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::newSiblingDirectory()
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *parentItem = currentItem()->parent();
|
||||||
|
Q_ASSERT(parentItem);
|
||||||
|
QJsonObject parentItemJson = parentItem->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
QString parentItemName = parentItemJson["name"].toString();
|
||||||
|
|
||||||
|
QString text("&Directory name:");
|
||||||
|
QString defaultText("new_directory");
|
||||||
|
QString defaultDescription("");
|
||||||
|
do {
|
||||||
|
VNewDirDialog dialog(QString("Create a new directory under %1").arg(parentItemName), text,
|
||||||
|
defaultText, tr("&Description:"), defaultDescription, this);
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
QString name = dialog.getNameInput();
|
||||||
|
QString description = dialog.getDescriptionInput();
|
||||||
|
if (isConflictNameWithChildren(parentItem, name)) {
|
||||||
|
text = "Name already exists.\nPlease choose another name:";
|
||||||
|
defaultText = name;
|
||||||
|
defaultDescription = description;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(parentItem, name, description);
|
||||||
|
if (newItem) {
|
||||||
|
this->setCurrentItem(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::newSubDirectory()
|
||||||
|
{
|
||||||
|
QTreeWidgetItem *curItem = currentItem();
|
||||||
|
QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
QString curItemName = curItemJson["name"].toString();
|
||||||
|
|
||||||
|
QString text("&Directory name:");
|
||||||
|
QString defaultText("new_directory");
|
||||||
|
QString defaultDescription("");
|
||||||
|
do {
|
||||||
|
VNewDirDialog dialog(QString("Create a new directory under %1").arg(curItemName), text,
|
||||||
|
defaultText, tr("&Description:"), defaultDescription, this);
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
QString name = dialog.getNameInput();
|
||||||
|
QString description = dialog.getDescriptionInput();
|
||||||
|
if (isConflictNameWithChildren(curItem, name)) {
|
||||||
|
text = "Name already exists.\nPlease choose another name:";
|
||||||
|
defaultText = name;
|
||||||
|
defaultDescription = description;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(curItem, name, description);
|
||||||
|
if (newItem) {
|
||||||
|
this->setCurrentItem(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDirectoryTree::newRootDirectory()
|
||||||
|
{
|
||||||
|
QString text("&Directory name:");
|
||||||
|
QString defaultText("new_directory");
|
||||||
|
QString defaultDescription("");
|
||||||
|
do {
|
||||||
|
VNewDirDialog dialog(tr("Create a new root directory"), text,
|
||||||
|
defaultText, tr("&Description:"), defaultDescription, this);
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
QString name = dialog.getNameInput();
|
||||||
|
QString description = dialog.getDescriptionInput();
|
||||||
|
if (isConflictNameWithChildren(NULL, name)) {
|
||||||
|
text = "Name already exists.\nPlease choose another name:";
|
||||||
|
defaultText = name;
|
||||||
|
defaultDescription = description;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(NULL, name, description);
|
||||||
|
if (newItem) {
|
||||||
|
this->setCurrentItem(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem* VDirectoryTree::createDirectoryAndUpdateTree(QTreeWidgetItem *parent,
|
||||||
|
const QString &name, const QString &description)
|
||||||
|
{
|
||||||
|
QString relativePath("");
|
||||||
|
QJsonObject parentJson;
|
||||||
|
if (parent) {
|
||||||
|
parentJson = parent->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
relativePath = QDir(parentJson["relative_path"].toString()).filePath(parentJson["name"].toString());
|
||||||
|
}
|
||||||
|
QString path = QDir(treePath).filePath(relativePath);
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.mkdir(name)) {
|
||||||
|
qDebug() << "warning: fail to create directory" << name << "under" << path;
|
||||||
|
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Could not create directory \"%1\" under \"%2\".")
|
||||||
|
.arg(name).arg(path));
|
||||||
|
msgBox.setInformativeText(QString("Please check if there already exists a directory named \"%1\".").arg(name));
|
||||||
|
msgBox.exec();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject configJson;
|
||||||
|
configJson["version"] = "1";
|
||||||
|
configJson["name"] = name;
|
||||||
|
configJson["sub_directories"] = QJsonArray();
|
||||||
|
configJson["files"] = QJsonArray();
|
||||||
|
|
||||||
|
if (!writeDirectoryConfig(QDir(path).filePath(name), configJson)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update parent's config file to include this new directory
|
||||||
|
configJson = readDirectoryConfig(path);
|
||||||
|
QJsonObject itemJson;
|
||||||
|
itemJson["name"] = name;
|
||||||
|
itemJson["description"] = description;
|
||||||
|
QJsonArray subDirArray = configJson["sub_directories"].toArray();
|
||||||
|
subDirArray.append(itemJson);
|
||||||
|
configJson["sub_directories"] = subDirArray;
|
||||||
|
if (!writeDirectoryConfig(path, configJson)) {
|
||||||
|
deleteDirectoryConfig(QDir(path).filePath(name));
|
||||||
|
dir.rmdir(name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return insertDirectoryTreeItem(parent, NULL, itemJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VDirectoryTree::isConflictNameWithChildren(const QTreeWidgetItem *parent, const QString &name)
|
||||||
|
{
|
||||||
|
if (parent) {
|
||||||
|
int nrChild = parent->childCount();
|
||||||
|
for (int i = 0; i < nrChild; ++i) {
|
||||||
|
QJsonObject childItemJson = parent->child(i)->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
Q_ASSERT(!childItemJson.isEmpty());
|
||||||
|
if (childItemJson["name"].toString() == name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int nrTopLevelItems = topLevelItemCount();
|
||||||
|
for (int i = 0; i < nrTopLevelItems; ++i) {
|
||||||
|
QJsonObject itemJson = topLevelItem(i)->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
Q_ASSERT(!itemJson.isEmpty());
|
||||||
|
if (itemJson["name"].toString() == name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,66 @@
|
|||||||
|
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
|
|
||||||
|
class QJsonObject;
|
||||||
|
|
||||||
class VDirectoryTree : public QTreeWidget
|
class VDirectoryTree : public QTreeWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit VDirectoryTree(QWidget *parent = 0);
|
VDirectoryTree(const QString &dirConfigFileName, QWidget *parent = 0);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setTreePath(const QString& path);
|
void setTreePath(const QString& path);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
// Read config file and pdate the subtree of @item in the directory tree.
|
||||||
|
// If @item has no child, we will call updateDirectoryTreeOne() to update it.
|
||||||
|
// Otherwise, we will loop all its direct-children and try to populate it if
|
||||||
|
// it has not been populated yet.
|
||||||
|
void updateItemSubtree(QTreeWidgetItem *item);
|
||||||
|
void contextMenuRequested(QPoint pos);
|
||||||
|
void newSiblingDirectory();
|
||||||
|
void newSubDirectory();
|
||||||
|
void newRootDirectory();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Clean and pdate the TreeWidget according to treePath
|
||||||
|
void updateDirectoryTree();
|
||||||
|
// Update the top-level items of the directory tree. Will not clean the tree at first.
|
||||||
|
void updateDirectoryTreeTopLevel();
|
||||||
|
// Update one directory, going into @depth levels. Not cleaning the tree item at first,
|
||||||
|
// so you must ensure @parent has no child before calling this function.
|
||||||
|
void updateDirectoryTreeOne(QTreeWidgetItem &parent, int depth);
|
||||||
|
// Validate if a directory is valid
|
||||||
|
bool validatePath(const QString &path);
|
||||||
|
// Validate if a directory config file is valid
|
||||||
|
bool validateDirConfigFile(const QJsonObject &configJson);
|
||||||
|
// Fill the QTreeWidgetItem according to its QJsonObject.
|
||||||
|
// @relative_path is the path related to treePath.
|
||||||
|
void fillDirectoryTreeItem(QTreeWidgetItem &item, QJsonObject itemJson, const QString &relativePath);
|
||||||
|
void initialActions();
|
||||||
|
QTreeWidgetItem* createDirectoryAndUpdateTree(QTreeWidgetItem *parent, const QString &name,
|
||||||
|
const QString &description);
|
||||||
|
// If @name conflict with the children's names of @parent.
|
||||||
|
bool isConflictNameWithChildren(const QTreeWidgetItem *parent, const QString &name);
|
||||||
|
// Read config from the directory config json file into a QJsonObject
|
||||||
|
QJsonObject readDirectoryConfig(const QString &path);
|
||||||
|
bool writeDirectoryConfig(const QString &path, const QJsonObject &configJson);
|
||||||
|
bool deleteDirectoryConfig(const QString &path);
|
||||||
|
QTreeWidgetItem* insertDirectoryTreeItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding,
|
||||||
|
const QJsonObject &newItem);
|
||||||
|
|
||||||
// The path of the directory tree root
|
// The path of the directory tree root
|
||||||
QString treePath;
|
QString treePath;
|
||||||
|
// The name of the config file in each subdirectory
|
||||||
|
QString dirConfigFileName;
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
QAction *newRootDirAct;
|
||||||
|
QAction *newSiblingDirAct;
|
||||||
|
QAction *newSubDirAct;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VDIRECTORYTREE_H
|
#endif // VDIRECTORYTREE_H
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <QtGui>
|
#include <QtWidgets>
|
||||||
#include "vmainwindow.h"
|
#include "vmainwindow.h"
|
||||||
#include "vdirectorytree.h"
|
#include "vdirectorytree.h"
|
||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
@ -23,7 +23,7 @@ void VMainWindow::setupUI()
|
|||||||
// Notebook directory browser tree
|
// Notebook directory browser tree
|
||||||
notebookLabel = new QLabel(tr("Notebook"));
|
notebookLabel = new QLabel(tr("Notebook"));
|
||||||
notebookComboBox = new QComboBox();
|
notebookComboBox = new QComboBox();
|
||||||
directoryTree = new VDirectoryTree();
|
directoryTree = new VDirectoryTree(VNote::dirConfigFileName);
|
||||||
|
|
||||||
QHBoxLayout *nbTopLayout = new QHBoxLayout;
|
QHBoxLayout *nbTopLayout = new QHBoxLayout;
|
||||||
notebookComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
notebookComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
@ -46,6 +46,7 @@ void VMainWindow::setupUI()
|
|||||||
// Editor tab widget
|
// Editor tab widget
|
||||||
editorTabWidget = new QTabWidget();
|
editorTabWidget = new QTabWidget();
|
||||||
editorTabWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
editorTabWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
editorTabWidget->setTabBarAutoHide(true);
|
||||||
QFile welcomeFile(":/resources/welcome.html");
|
QFile welcomeFile(":/resources/welcome.html");
|
||||||
QString welcomeText("Welcome to VNote!");
|
QString welcomeText("Welcome to VNote!");
|
||||||
if (welcomeFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (welcomeFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
@ -72,6 +73,8 @@ void VMainWindow::setupUI()
|
|||||||
SLOT(setTreePath(const QString&)));
|
SLOT(setTreePath(const QString&)));
|
||||||
|
|
||||||
setCentralWidget(mainSplitter);
|
setCentralWidget(mainSplitter);
|
||||||
|
// Create and show the status bar
|
||||||
|
statusBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMainWindow::updateNotebookComboBox()
|
void VMainWindow::updateNotebookComboBox()
|
||||||
@ -83,10 +86,9 @@ void VMainWindow::updateNotebookComboBox()
|
|||||||
notebookComboBox->addItem(notebooks[i].getName());
|
notebookComboBox->addItem(notebooks[i].getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
notebookComboBox->setCurrentIndex(vnote->getCurNotebookIndex());
|
|
||||||
|
|
||||||
qDebug() << "update notebook combobox with" << notebookComboBox->count()
|
qDebug() << "update notebook combobox with" << notebookComboBox->count()
|
||||||
<< "items";
|
<< "items";
|
||||||
|
notebookComboBox->setCurrentIndex(vnote->getCurNotebookIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMainWindow::setCurNotebookIndex(int index)
|
void VMainWindow::setCurNotebookIndex(int index)
|
||||||
|
64
vnewdirdialog.cpp
Normal file
64
vnewdirdialog.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include <QtWidgets>
|
||||||
|
#include "vnewdirdialog.h"
|
||||||
|
|
||||||
|
VNewDirDialog::VNewDirDialog(const QString &title, const QString &name, const QString &defaultName,
|
||||||
|
const QString &description, const QString &defaultDescription,
|
||||||
|
QWidget *parent)
|
||||||
|
: QDialog(parent), title(title), name(name), defaultName(defaultName),
|
||||||
|
description(description), defaultDescription(defaultDescription)
|
||||||
|
{
|
||||||
|
setupUI();
|
||||||
|
|
||||||
|
connect(nameEdit, &QLineEdit::textChanged, this, &VNewDirDialog::enableOkButton);
|
||||||
|
connect(okBtn, &QPushButton::clicked, this, &VNewDirDialog::accept);
|
||||||
|
connect(cancelBtn, &QPushButton::clicked, this, &VNewDirDialog::reject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNewDirDialog::setupUI()
|
||||||
|
{
|
||||||
|
nameLabel = new QLabel(name);
|
||||||
|
nameEdit = new QLineEdit(defaultName);
|
||||||
|
nameEdit->selectAll();
|
||||||
|
nameLabel->setBuddy(nameEdit);
|
||||||
|
|
||||||
|
descriptionLabel = new QLabel(description);
|
||||||
|
descriptionEdit = new QLineEdit(defaultDescription);
|
||||||
|
descriptionLabel->setBuddy(descriptionEdit);
|
||||||
|
|
||||||
|
okBtn = new QPushButton(tr("&OK"));
|
||||||
|
okBtn->setDefault(true);
|
||||||
|
cancelBtn = new QPushButton(tr("&Cancel"));
|
||||||
|
|
||||||
|
QVBoxLayout *topLayout = new QVBoxLayout();
|
||||||
|
topLayout->addWidget(nameLabel);
|
||||||
|
topLayout->addWidget(nameEdit);
|
||||||
|
topLayout->addWidget(descriptionLabel);
|
||||||
|
topLayout->addWidget(descriptionEdit);
|
||||||
|
|
||||||
|
QHBoxLayout *btmLayout = new QHBoxLayout();
|
||||||
|
btmLayout->addStretch();
|
||||||
|
btmLayout->addWidget(okBtn);
|
||||||
|
btmLayout->addWidget(cancelBtn);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||||
|
mainLayout->addLayout(topLayout);
|
||||||
|
mainLayout->addLayout(btmLayout);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
setWindowTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNewDirDialog::enableOkButton(const QString &editText)
|
||||||
|
{
|
||||||
|
okBtn->setEnabled(!editText.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VNewDirDialog::getNameInput() const
|
||||||
|
{
|
||||||
|
return nameEdit->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VNewDirDialog::getDescriptionInput() const
|
||||||
|
{
|
||||||
|
return descriptionEdit->text();
|
||||||
|
}
|
40
vnewdirdialog.h
Normal file
40
vnewdirdialog.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef VNEWDIRDIALOG_H
|
||||||
|
#define VNEWDIRDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QLineEdit;
|
||||||
|
class QPushButton;
|
||||||
|
class QString;
|
||||||
|
|
||||||
|
class VNewDirDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VNewDirDialog(const QString &title, const QString &name, const QString &defaultName,
|
||||||
|
const QString &description, const QString &defaultDescription, QWidget *parent = 0);
|
||||||
|
QString getNameInput() const;
|
||||||
|
QString getDescriptionInput() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void enableOkButton(const QString &editText);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupUI();
|
||||||
|
|
||||||
|
QLabel *nameLabel;
|
||||||
|
QLabel *descriptionLabel;
|
||||||
|
QLineEdit *nameEdit;
|
||||||
|
QLineEdit *descriptionEdit;
|
||||||
|
QPushButton *okBtn;
|
||||||
|
QPushButton *cancelBtn;
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
QString name;
|
||||||
|
QString defaultName;
|
||||||
|
QString description;
|
||||||
|
QString defaultDescription;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VNEWDIRDIALOG_H
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
const QString VNote::orgName = QString("tamlok");
|
const QString VNote::orgName = QString("tamlok");
|
||||||
const QString VNote::appName = QString("VNote");
|
const QString VNote::appName = QString("VNote");
|
||||||
|
const QString VNote::dirConfigFileName = QString(".vnote.json");
|
||||||
|
|
||||||
VNote::VNote()
|
VNote::VNote()
|
||||||
: curNotebookIndex(0)
|
: curNotebookIndex(0)
|
||||||
|
8
vnote.h
8
vnote.h
@ -16,6 +16,12 @@ public:
|
|||||||
const QVector<VNotebook>& getNotebooks();
|
const QVector<VNotebook>& getNotebooks();
|
||||||
int getCurNotebookIndex() const;
|
int getCurNotebookIndex() const;
|
||||||
void setCurNotebookIndex(int index);
|
void setCurNotebookIndex(int index);
|
||||||
|
|
||||||
|
// The name of the config file in each subdirectory
|
||||||
|
static const QString dirConfigFileName;
|
||||||
|
static const QString orgName;
|
||||||
|
static const QString appName;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Write notebooks section of global config
|
// Write notebooks section of global config
|
||||||
void writeGlobalConfigNotebooks(QSettings &settings);
|
void writeGlobalConfigNotebooks(QSettings &settings);
|
||||||
@ -24,8 +30,6 @@ private:
|
|||||||
|
|
||||||
QVector<VNotebook> notebooks;
|
QVector<VNotebook> notebooks;
|
||||||
int curNotebookIndex;
|
int curNotebookIndex;
|
||||||
static const QString orgName;
|
|
||||||
static const QString appName;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VNOTE_H
|
#endif // VNOTE_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user