add VFileList to browse the files in current directory

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-10-06 12:55:00 +08:00
parent 0f7f433833
commit 9dd22760fc
9 changed files with 448 additions and 15 deletions

View File

@ -18,14 +18,18 @@ SOURCES += main.cpp\
vnote.cpp \
vnotebook.cpp \
vnewdirdialog.cpp \
vconfigmanager.cpp
vconfigmanager.cpp \
vfilelist.cpp \
vnewfiledialog.cpp
HEADERS += vmainwindow.h \
vdirectorytree.h \
vnote.h \
vnotebook.h \
vnewdirdialog.h \
vconfigmanager.h
vconfigmanager.h \
vfilelist.h \
vnewfiledialog.h
RESOURCES += \
vnote.qrc

View File

@ -1,5 +1,4 @@
#include <QtWidgets>
#include <QJsonObject>
#include "vdirectorytree.h"
#include "vnewdirdialog.h"
#include "vconfigmanager.h"
@ -10,15 +9,17 @@ VDirectoryTree::VDirectoryTree(QWidget *parent)
setColumnCount(1);
setHeaderHidden(true);
setContextMenuPolicy(Qt::CustomContextMenu);
initialActions();
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::initialActions()
void VDirectoryTree::initActions()
{
newRootDirAct = new QAction(tr("New &root directory"), this);
newRootDirAct->setStatusTip(tr("Create a new root directory in current notebook"));
@ -51,6 +52,9 @@ void VDirectoryTree::setTreePath(const QString& path)
qDebug() << "set directory tree path:" << path;
updateDirectoryTree();
if (topLevelItemCount() > 0) {
setCurrentItem(topLevelItem(0));
}
}
bool VDirectoryTree::validatePath(const QString &path)
@ -180,7 +184,7 @@ void VDirectoryTree::updateDirectoryTreeOne(QTreeWidgetItem &parent, int depth)
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 path \"%1\" does not contain a valid configuration file.")
msgBox.setInformativeText(QString("Notebook directory \"%1\" does not contain a valid configuration file.")
.arg(path));
msgBox.exec();
return;
@ -356,7 +360,7 @@ QTreeWidgetItem* VDirectoryTree::createDirectoryAndUpdateTree(QTreeWidgetItem *p
QString path = QDir(treePath).filePath(relativePath);
QDir dir(path);
if (!dir.mkdir(name)) {
qDebug() << "warning: fail to create directory" << name << "under" << path;
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));
@ -458,3 +462,12 @@ bool VDirectoryTree::isConflictNameWithChildren(const QTreeWidgetItem *parent, c
}
return false;
}
void VDirectoryTree::currentDirectoryItemChanged(QTreeWidgetItem *currentItem)
{
QJsonObject itemJson = currentItem->data(0, Qt::UserRole).toJsonObject();
Q_ASSERT(!itemJson.isEmpty());
itemJson["root_path"] = treePath;
qDebug() << "click dir:" << itemJson;
emit currentDirectoryChanged(itemJson);
}

View File

@ -2,16 +2,16 @@
#define VDIRECTORYTREE_H
#include <QTreeWidget>
class QJsonObject;
#include <QJsonObject>
class VDirectoryTree : public QTreeWidget
{
Q_OBJECT
public:
VDirectoryTree(QWidget *parent = 0);
explicit VDirectoryTree(QWidget *parent = 0);
signals:
void currentDirectoryChanged(QJsonObject itemJson);
public slots:
void setTreePath(const QString& path);
@ -27,6 +27,7 @@ private slots:
void newSubDirectory();
void newRootDirectory();
void deleteDirectory();
void currentDirectoryItemChanged(QTreeWidgetItem *currentItem);
private:
// Clean and pdate the TreeWidget according to treePath
@ -41,7 +42,7 @@ private:
// 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();
void initActions();
QTreeWidgetItem* createDirectoryAndUpdateTree(QTreeWidgetItem *parent, const QString &name,
const QString &description);
void deleteDirectoryAndUpdateTree(QTreeWidgetItem *item);

261
vfilelist.cpp Normal file
View File

@ -0,0 +1,261 @@
#include <QtDebug>
#include <QtWidgets>
#include "vfilelist.h"
#include "vconfigmanager.h"
#include "vnewfiledialog.h"
VFileList::VFileList(QWidget *parent)
: QListWidget(parent)
{
setContextMenuPolicy(Qt::CustomContextMenu);
initActions();
connect(this, &VFileList::customContextMenuRequested,
this, &VFileList::contextMenuRequested);
connect(this, &VFileList::currentItemChanged,
this, &VFileList::currentFileItemChanged);
}
void VFileList::initActions()
{
newFileAct = new QAction(tr("&New note"), this);
newFileAct->setStatusTip(tr("Create a new note in current directory"));
connect(newFileAct, &QAction::triggered,
this, &VFileList::newFile);
deleteFileAct = new QAction(tr("&Delete"), this);
deleteFileAct->setStatusTip(tr("Delete selected note"));
connect(deleteFileAct, &QAction::triggered,
this, &VFileList::deleteFile);
}
void VFileList::setDirectory(QJsonObject dirJson)
{
Q_ASSERT(!dirJson.isEmpty());
directoryName = dirJson["name"].toString();
rootPath = dirJson["root_path"].toString();
relativePath = QDir(dirJson["relative_path"].toString()).filePath(directoryName);
qDebug() << "FileList update:" << rootPath << relativePath << directoryName;
updateFileList();
}
void VFileList::updateFileList()
{
clear();
QString path = QDir(rootPath).filePath(relativePath);
if (!QDir(path).exists()) {
qDebug() << "invalid 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 valid.")
.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 files section
QJsonArray filesJson = configJson["files"].toArray();
for (int i = 0; i < filesJson.size(); ++i) {
QJsonObject fileItem = filesJson[i].toObject();
insertFileListItem(fileItem);
}
}
QListWidgetItem* VFileList::insertFileListItem(QJsonObject fileJson, bool atFront)
{
Q_ASSERT(!fileJson.isEmpty());
QListWidgetItem *item = new QListWidgetItem(fileJson["name"].toString());
if (!fileJson["description"].toString().isEmpty()) {
item->setToolTip(fileJson["description"].toString());
}
item->setData(Qt::UserRole, fileJson);
if (atFront) {
insertItem(0, item);
} else {
addItem(item);
}
qDebug() << "add new list item:" << fileJson["name"].toString();
return item;
}
void VFileList::removeFileListItem(QListWidgetItem *item)
{
// Qt ensures it will be removed from QListWidget automatically
delete item;
}
void VFileList::newFile()
{
QString text("&Note name:");
QString defaultText("new_note");
QString defaultDescription("");
do {
VNewFileDialog dialog(QString("Create a new note under %1").arg(directoryName), text,
defaultText, tr("&Description:"), defaultDescription, this);
if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput();
QString description = dialog.getDescriptionInput();
if (isConflictNameWithExisting(name)) {
text = "Name already exists.\nPlease choose another name:";
defaultText = name;
defaultDescription = description;
continue;
}
QListWidgetItem *newItem = createFileAndUpdateList(name, description);
if (newItem) {
this->setCurrentItem(newItem);
}
}
break;
} while (true);
}
void VFileList::deleteFile()
{
QListWidgetItem *curItem = currentItem();
QJsonObject curItemJson = curItem->data(Qt::UserRole).toJsonObject();
QString curItemName = curItemJson["name"].toString();
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"),
QString("Are you sure you want to delete note \"%1\"?")
.arg(curItemName));
msgBox.setInformativeText(tr("This may be not recoverable."));
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
if (msgBox.exec() == QMessageBox::Ok) {
deleteFileAndUpdateList(curItem);
}
}
void VFileList::contextMenuRequested(QPoint pos)
{
QListWidgetItem *item = itemAt(pos);
QMenu menu(this);
if (directoryName.isEmpty()) {
return;
}
menu.addAction(newFileAct);
if (item) {
menu.addAction(deleteFileAct);
}
menu.exec(mapToGlobal(pos));
}
bool VFileList::isConflictNameWithExisting(const QString &name)
{
int nrChild = this->count();
for (int i = 0; i < nrChild; ++i) {
QListWidgetItem *item = this->item(i);
QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject();
Q_ASSERT(!itemJson.isEmpty());
if (itemJson["name"].toString() == name) {
return true;
}
}
return false;
}
QListWidgetItem* VFileList::createFileAndUpdateList(const QString &name,
const QString &description)
{
QString path = QDir(rootPath).filePath(relativePath);
QString filePath = QDir(path).filePath(name);
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly)) {
qWarning() << "error: fail to create file:" << filePath;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Could not create file \"%1\" under \"%2\".")
.arg(name).arg(path));
msgBox.setInformativeText(QString("Please check if there already exists a file named \"%1\".").arg(name));
msgBox.exec();
return NULL;
}
file.close();
qDebug() << "create file:" << filePath;
// Update current directory's config file to include this new file
QJsonObject dirJson = VConfigManager::readDirectoryConfig(path);
Q_ASSERT(!dirJson.isEmpty());
QJsonObject fileJson;
fileJson["name"] = name;
fileJson["description"] = description;
QJsonArray fileArray = dirJson["files"].toArray();
fileArray.push_front(fileJson);
dirJson["files"] = fileArray;
if (!VConfigManager::writeDirectoryConfig(path, dirJson)) {
qWarning() << "error: fail to update directory's configuration file to add a new file"
<< name;
file.remove();
return NULL;
}
return insertFileListItem(fileJson, true);
}
void VFileList::deleteFileAndUpdateList(QListWidgetItem *item)
{
Q_ASSERT(item);
QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject();
QString path = QDir(rootPath).filePath(relativePath);
QString fileName = itemJson["name"].toString();
QString filePath = QDir(path).filePath(fileName);
// Update current directory's config file to exclude this file
QJsonObject dirJson = VConfigManager::readDirectoryConfig(path);
Q_ASSERT(!dirJson.isEmpty());
QJsonArray fileArray = dirJson["files"].toArray();
bool deleted = false;
for (int i = 0; i < fileArray.size(); ++i) {
QJsonObject ele = fileArray[i].toObject();
if (ele["name"].toString() == fileName) {
fileArray.removeAt(i);
deleted = true;
break;
}
}
if (!deleted) {
qWarning() << "error: fail to find" << fileName << "to delete";
return;
}
dirJson["files"] = fileArray;
if (!VConfigManager::writeDirectoryConfig(path, dirJson)) {
qWarning() << "error: fail to update directory's configuration file to delete"
<< fileName;
return;
}
// Delete the file
QFile file(filePath);
if (!file.remove()) {
qWarning() << "error: fail to delete" << filePath;
} else {
qDebug() << "delete" << filePath;
}
removeFileListItem(item);
}
void VFileList::currentFileItemChanged(QListWidgetItem *currentItem)
{
QJsonObject itemJson = currentItem->data(Qt::UserRole).toJsonObject();
Q_ASSERT(!itemJson.isEmpty());
itemJson["path"] = QDir::cleanPath(QDir(rootPath).filePath(relativePath));
qDebug() << "click file:" << itemJson;
emit currentFileChanged(itemJson);
}

46
vfilelist.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef VFILELIST_H
#define VFILELIST_H
#include <QListWidget>
#include <QJsonObject>
class QAction;
class VFileList : public QListWidget
{
Q_OBJECT
public:
explicit VFileList(QWidget *parent = 0);
signals:
void currentFileChanged(QJsonObject fileJson);
private slots:
void newFile();
void deleteFile();
void contextMenuRequested(QPoint pos);
void currentFileItemChanged(QListWidgetItem *currentItem);
public slots:
void setDirectory(QJsonObject dirJson);
private:
void updateFileList();
QListWidgetItem *insertFileListItem(QJsonObject fileJson, bool atFront = false);
void removeFileListItem(QListWidgetItem *item);
void initActions();
bool isConflictNameWithExisting(const QString &name);
QListWidgetItem *createFileAndUpdateList(const QString &name,
const QString &description);
void deleteFileAndUpdateList(QListWidgetItem *item);
QString rootPath;
QString relativePath;
QString directoryName;
// Actions
QAction *newFileAct;
QAction *deleteFileAct;
};
#endif // VFILELIST_H

View File

@ -2,6 +2,7 @@
#include "vmainwindow.h"
#include "vdirectorytree.h"
#include "vnote.h"
#include "vfilelist.h"
VMainWindow::VMainWindow(QWidget *parent)
: QMainWindow(parent)
@ -40,8 +41,8 @@ void VMainWindow::setupUI()
nbContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
// File list widget
fileListWidget = new QListWidget();
fileListWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
fileList = new VFileList();
fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
// Editor tab widget
editorTabWidget = new QTabWidget();
@ -60,7 +61,7 @@ void VMainWindow::setupUI()
// Main Splitter
mainSplitter = new QSplitter();
mainSplitter->addWidget(nbContainer);
mainSplitter->addWidget(fileListWidget);
mainSplitter->addWidget(fileList);
mainSplitter->addWidget(editorTabWidget);
mainSplitter->setStretchFactor(0, 1);
mainSplitter->setStretchFactor(1, 1);
@ -71,6 +72,8 @@ void VMainWindow::setupUI()
SLOT(setCurNotebookIndex(int)));
connect(this, SIGNAL(curNotebookIndexChanged(const QString&)), directoryTree,
SLOT(setTreePath(const QString&)));
connect(directoryTree, &VDirectoryTree::currentDirectoryChanged,
fileList, &VFileList::setDirectory);
setCentralWidget(mainSplitter);
// Create and show the status bar

View File

@ -10,6 +10,7 @@ class QSplitter;
class QListWidget;
class QTabWidget;
class VNote;
class VFileList;
class VMainWindow : public QMainWindow
{
@ -34,7 +35,7 @@ private:
QLabel *notebookLabel;
QComboBox *notebookComboBox;
VDirectoryTree *directoryTree;
QListWidget *fileListWidget;
VFileList *fileList;
QTabWidget *editorTabWidget;
QSplitter *mainSplitter;
VNote *vnote;

64
vnewfiledialog.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <QtWidgets>
#include "vnewfiledialog.h"
VNewFileDialog::VNewFileDialog(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, &VNewFileDialog::enableOkButton);
connect(okBtn, &QPushButton::clicked, this, &VNewFileDialog::accept);
connect(cancelBtn, &QPushButton::clicked, this, &VNewFileDialog::reject);
}
void VNewFileDialog::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 VNewFileDialog::enableOkButton(const QString &editText)
{
okBtn->setEnabled(!editText.isEmpty());
}
QString VNewFileDialog::getNameInput() const
{
return nameEdit->text();
}
QString VNewFileDialog::getDescriptionInput() const
{
return descriptionEdit->text();
}

40
vnewfiledialog.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef VNEWFILEDIALOG_H
#define VNEWFILEDIALOG_H
#include <QDialog>
class QLabel;
class QLineEdit;
class QPushButton;
class QString;
class VNewFileDialog : public QDialog
{
Q_OBJECT
public:
VNewFileDialog(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 // VNEWFILEDIALOG_H