| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- #include <QtWidgets>
- #include "vdirectorytree.h"
- #include "vnewdirdialog.h"
- #include "vconfigmanager.h"
- VDirectoryTree::VDirectoryTree(QWidget *parent)
- : QTreeWidget(parent)
- {
- setColumnCount(1);
- setHeaderHidden(true);
- setContextMenuPolicy(Qt::CustomContextMenu);
- initActions();
- connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
- this, SLOT(updateItemSubtree(QTreeWidgetItem*)));
- connect(this, SIGNAL(customContextMenuRequested(QPoint)),
- this, SLOT(contextMenuRequested(QPoint)));
- connect(this, &VDirectoryTree::currentItemChanged,
- this, &VDirectoryTree::currentDirectoryItemChanged);
- }
- void VDirectoryTree::initActions()
- {
- 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);
- deleteDirAct = new QAction(tr("&Delete"), this);
- deleteDirAct->setStatusTip(tr("Delete selected directory"));
- connect(deleteDirAct, &QAction::triggered,
- this, &VDirectoryTree::deleteDirectory);
- }
- void VDirectoryTree::setTreePath(const QString& path)
- {
- if (path == treePath) {
- return;
- }
- treePath = path;
- qDebug() << "set directory tree path:" << path;
- updateDirectoryTree();
- if (topLevelItemCount() > 0) {
- setCurrentItem(topLevelItem(0));
- }
- }
- bool VDirectoryTree::validatePath(const QString &path)
- {
- return QDir(path).exists();
- }
- 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::removeDirectoryTreeItem(QTreeWidgetItem *item)
- {
- delete 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 = VConfigManager::readDirectoryConfig(path);
- if (configJson.isEmpty()) {
- 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 = VConfigManager::readDirectoryConfig(path);
- if (configJson.isEmpty()) {
- qDebug() << "invalid notebook configuration for directory:" << path;
- QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory configuration."));
- msgBox.setInformativeText(QString("Notebook directory \"%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);
- }
- }
- 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.addAction(deleteDirAct);
- }
- 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);
- }
- void VDirectoryTree::deleteDirectory()
- {
- QTreeWidgetItem *curItem = currentItem();
- QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject();
- QString curItemName = curItemJson["name"].toString();
- QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Are you sure you want to delete directory \"%1\"?")
- .arg(curItemName));
- msgBox.setInformativeText(tr("This will delete any files under this directory."));
- msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
- msgBox.setDefaultButton(QMessageBox::Ok);
- if (msgBox.exec() == QMessageBox::Ok) {
- deleteDirectoryAndUpdateTree(curItem);
- }
- }
- 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)) {
- qWarning() << "error: 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 (!VConfigManager::writeDirectoryConfig(QDir(path).filePath(name), configJson)) {
- return NULL;
- }
- // Update parent's config file to include this new directory
- configJson = VConfigManager::readDirectoryConfig(path);
- Q_ASSERT(!configJson.isEmpty());
- QJsonObject itemJson;
- itemJson["name"] = name;
- itemJson["description"] = description;
- QJsonArray subDirArray = configJson["sub_directories"].toArray();
- subDirArray.append(itemJson);
- configJson["sub_directories"] = subDirArray;
- if (!VConfigManager::writeDirectoryConfig(path, configJson)) {
- VConfigManager::deleteDirectoryConfig(QDir(path).filePath(name));
- dir.rmdir(name);
- return NULL;
- }
- return insertDirectoryTreeItem(parent, NULL, itemJson);
- }
- void VDirectoryTree::deleteDirectoryAndUpdateTree(QTreeWidgetItem *item)
- {
- QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
- QString itemName = itemJson["name"].toString();
- QString relativePath = itemJson["relative_path"].toString();
- // Update parent's config file to exclude this directory
- QString path = QDir(treePath).filePath(relativePath);
- QJsonObject configJson = VConfigManager::readDirectoryConfig(path);
- Q_ASSERT(!configJson.isEmpty());
- QJsonArray subDirArray = configJson["sub_directories"].toArray();
- bool deleted = false;
- for (int i = 0; i < subDirArray.size(); ++i) {
- QJsonObject ele = subDirArray[i].toObject();
- if (ele["name"].toString() == itemName) {
- subDirArray.removeAt(i);
- deleted = true;
- break;
- }
- }
- if (!deleted) {
- qWarning() << "error: fail to find" << itemName << "to delete in its parent's configuration file";
- return;
- }
- configJson["sub_directories"] = subDirArray;
- if (!VConfigManager::writeDirectoryConfig(path, configJson)) {
- qWarning() << "error: fail to update parent's configuration file to delete" << itemName;
- return;
- }
- // Delete the entire directory
- QString dirName = QDir(path).filePath(itemName);
- QDir dir(dirName);
- if (!dir.removeRecursively()) {
- qWarning() << "error: fail to delete" << dirName << "recursively";
- } else {
- qDebug() << "delete" << dirName << "recursively";
- }
- // Update the tree
- removeDirectoryTreeItem(item);
- }
- 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;
- }
- void VDirectoryTree::currentDirectoryItemChanged(QTreeWidgetItem *currentItem)
- {
- if (!currentItem) {
- emit currentDirectoryChanged(QJsonObject());
- return;
- }
- QJsonObject itemJson = currentItem->data(0, Qt::UserRole).toJsonObject();
- Q_ASSERT(!itemJson.isEmpty());
- itemJson["root_path"] = treePath;
- qDebug() << "click dir:" << itemJson;
- emit currentDirectoryChanged(itemJson);
- }
|