From e7c42ba5be6c23078af14623eb48cca2c0aed7a8 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Tue, 29 Nov 2016 23:38:17 +0800 Subject: [PATCH] refactor: add VFile and VDirectory Signed-off-by: Le Tan --- src/dialog/vdirinfodialog.cpp | 10 +- src/dialog/vfileinfodialog.cpp | 8 +- src/dialog/vnewdirdialog.cpp | 14 +- src/dialog/vnewdirdialog.h | 3 +- src/dialog/vnewfiledialog.cpp | 16 +- src/dialog/vnewfiledialog.h | 5 +- src/src.pro | 12 +- src/utils/vutils.cpp | 36 +- src/utils/vutils.h | 5 + src/vconstants.h | 1 + src/vdirectory.cpp | 468 +++++++++++++++++++++++++ src/vdirectory.h | 109 ++++++ src/vdirectorytree.cpp | 573 ++++++++++--------------------- src/vdirectorytree.h | 54 ++- src/vedit.cpp | 60 ++-- src/vedit.h | 12 +- src/veditarea.cpp | 76 ++--- src/veditarea.h | 20 +- src/veditoperations.cpp | 8 +- src/veditoperations.h | 10 +- src/vedittab.cpp | 121 +++---- src/vedittab.h | 17 +- src/veditwindow.cpp | 240 ++++++------- src/veditwindow.h | 43 ++- src/vfile.cpp | 114 +++++++ src/vfile.h | 125 +++++++ src/vfilelist.cpp | 606 +++++++++------------------------ src/vfilelist.h | 71 ++-- src/vfilelocation.cpp | 10 - src/vfilelocation.h | 15 - src/vmainwindow.cpp | 64 ++-- src/vmainwindow.h | 14 +- src/vmdeditoperations.cpp | 28 +- src/vmdeditoperations.h | 2 +- src/vnote.h | 3 +- src/vnotebook.cpp | 21 +- src/vnotebook.h | 17 +- src/vnotefile.cpp | 9 - src/vnotefile.h | 20 -- 39 files changed, 1624 insertions(+), 1416 deletions(-) create mode 100644 src/vdirectory.cpp create mode 100644 src/vdirectory.h create mode 100644 src/vfile.cpp create mode 100644 src/vfile.h delete mode 100644 src/vfilelocation.cpp delete mode 100644 src/vfilelocation.h delete mode 100644 src/vnotefile.cpp delete mode 100644 src/vnotefile.h diff --git a/src/dialog/vdirinfodialog.cpp b/src/dialog/vdirinfodialog.cpp index 7cf7aca5..05af15fa 100644 --- a/src/dialog/vdirinfodialog.cpp +++ b/src/dialog/vdirinfodialog.cpp @@ -20,7 +20,7 @@ void VDirInfoDialog::setupUI() if (!info.isEmpty()) { infoLabel = new QLabel(info); } - nameLabel = new QLabel(tr("&Name")); + nameLabel = new QLabel(tr("&Name:")); nameEdit = new QLineEdit(defaultName); nameEdit->selectAll(); nameLabel->setBuddy(nameEdit); @@ -29,10 +29,7 @@ void VDirInfoDialog::setupUI() okBtn->setDefault(true); cancelBtn = new QPushButton(tr("&Cancel")); - QVBoxLayout *topLayout = new QVBoxLayout(); - if (infoLabel) { - topLayout->addWidget(infoLabel); - } + QHBoxLayout *topLayout = new QHBoxLayout(); topLayout->addWidget(nameLabel); topLayout->addWidget(nameEdit); @@ -42,6 +39,9 @@ void VDirInfoDialog::setupUI() btmLayout->addWidget(cancelBtn); QVBoxLayout *mainLayout = new QVBoxLayout(); + if (infoLabel) { + mainLayout->addWidget(infoLabel); + } mainLayout->addLayout(topLayout); mainLayout->addLayout(btmLayout); setLayout(mainLayout); diff --git a/src/dialog/vfileinfodialog.cpp b/src/dialog/vfileinfodialog.cpp index d37b4f40..1209777a 100644 --- a/src/dialog/vfileinfodialog.cpp +++ b/src/dialog/vfileinfodialog.cpp @@ -29,10 +29,7 @@ void VFileInfoDialog::setupUI() okBtn->setDefault(true); cancelBtn = new QPushButton(tr("&Cancel")); - QVBoxLayout *topLayout = new QVBoxLayout(); - if (infoLabel) { - topLayout->addWidget(infoLabel); - } + QHBoxLayout *topLayout = new QHBoxLayout(); topLayout->addWidget(nameLabel); topLayout->addWidget(nameEdit); @@ -42,6 +39,9 @@ void VFileInfoDialog::setupUI() btmLayout->addWidget(cancelBtn); QVBoxLayout *mainLayout = new QVBoxLayout(); + if (infoLabel) { + mainLayout->addWidget(infoLabel); + } mainLayout->addLayout(topLayout); mainLayout->addLayout(btmLayout); setLayout(mainLayout); diff --git a/src/dialog/vnewdirdialog.cpp b/src/dialog/vnewdirdialog.cpp index d893b4f4..1755a1a4 100644 --- a/src/dialog/vnewdirdialog.cpp +++ b/src/dialog/vnewdirdialog.cpp @@ -1,9 +1,9 @@ #include #include "vnewdirdialog.h" -VNewDirDialog::VNewDirDialog(const QString &title, const QString &name, const QString &defaultName, +VNewDirDialog::VNewDirDialog(const QString &title, const QString &info, const QString &name, const QString &defaultName, QWidget *parent) - : QDialog(parent), title(title), name(name), defaultName(defaultName) + : QDialog(parent), title(title), info(info), name(name), defaultName(defaultName) { setupUI(); @@ -14,6 +14,11 @@ VNewDirDialog::VNewDirDialog(const QString &title, const QString &name, const QS void VNewDirDialog::setupUI() { + QLabel *infoLabel = NULL; + if (!info.isEmpty()) { + infoLabel = new QLabel(info); + } + nameLabel = new QLabel(name); nameEdit = new QLineEdit(defaultName); nameEdit->selectAll(); @@ -23,7 +28,7 @@ void VNewDirDialog::setupUI() okBtn->setDefault(true); cancelBtn = new QPushButton(tr("&Cancel")); - QVBoxLayout *topLayout = new QVBoxLayout(); + QHBoxLayout *topLayout = new QHBoxLayout(); topLayout->addWidget(nameLabel); topLayout->addWidget(nameEdit); @@ -33,6 +38,9 @@ void VNewDirDialog::setupUI() btmLayout->addWidget(cancelBtn); QVBoxLayout *mainLayout = new QVBoxLayout(); + if (infoLabel) { + mainLayout->addWidget(infoLabel); + } mainLayout->addLayout(topLayout); mainLayout->addLayout(btmLayout); setLayout(mainLayout); diff --git a/src/dialog/vnewdirdialog.h b/src/dialog/vnewdirdialog.h index 89663852..cf3e8e29 100644 --- a/src/dialog/vnewdirdialog.h +++ b/src/dialog/vnewdirdialog.h @@ -12,7 +12,7 @@ class VNewDirDialog : public QDialog { Q_OBJECT public: - VNewDirDialog(const QString &title, const QString &name, + VNewDirDialog(const QString &title, const QString &info, const QString &name, const QString &defaultName, QWidget *parent = 0); QString getNameInput() const; @@ -28,6 +28,7 @@ private: QPushButton *cancelBtn; QString title; + QString info; QString name; QString defaultName; }; diff --git a/src/dialog/vnewfiledialog.cpp b/src/dialog/vnewfiledialog.cpp index 21ec5d9a..5a11e8ed 100644 --- a/src/dialog/vnewfiledialog.cpp +++ b/src/dialog/vnewfiledialog.cpp @@ -1,9 +1,9 @@ #include #include "vnewfiledialog.h" -VNewFileDialog::VNewFileDialog(const QString &title, const QString &name, const QString &defaultName, - QWidget *parent) - : QDialog(parent), title(title), name(name), defaultName(defaultName) +VNewFileDialog::VNewFileDialog(const QString &title, const QString &info, const QString &name, + const QString &defaultName, QWidget *parent) + : QDialog(parent), title(title), info(info), name(name), defaultName(defaultName) { setupUI(); @@ -14,6 +14,11 @@ VNewFileDialog::VNewFileDialog(const QString &title, const QString &name, const void VNewFileDialog::setupUI() { + QLabel *infoLabel = NULL; + if (!info.isEmpty()) { + infoLabel = new QLabel(info); + } + nameLabel = new QLabel(name); nameEdit = new QLineEdit(defaultName); nameEdit->selectAll(); @@ -23,7 +28,7 @@ void VNewFileDialog::setupUI() okBtn->setDefault(true); cancelBtn = new QPushButton(tr("&Cancel")); - QVBoxLayout *topLayout = new QVBoxLayout(); + QHBoxLayout *topLayout = new QHBoxLayout(); topLayout->addWidget(nameLabel); topLayout->addWidget(nameEdit); @@ -33,6 +38,9 @@ void VNewFileDialog::setupUI() btmLayout->addWidget(cancelBtn); QVBoxLayout *mainLayout = new QVBoxLayout(); + if (infoLabel) { + mainLayout->addWidget(infoLabel); + } mainLayout->addLayout(topLayout); mainLayout->addLayout(btmLayout); setLayout(mainLayout); diff --git a/src/dialog/vnewfiledialog.h b/src/dialog/vnewfiledialog.h index 8906fb2a..b5e4e4aa 100644 --- a/src/dialog/vnewfiledialog.h +++ b/src/dialog/vnewfiledialog.h @@ -12,8 +12,8 @@ class VNewFileDialog : public QDialog { Q_OBJECT public: - VNewFileDialog(const QString &title, const QString &name, const QString &defaultName, - QWidget *parent = 0); + VNewFileDialog(const QString &title, const QString &info, const QString &name, + const QString &defaultName, QWidget *parent = 0); QString getNameInput() const; private slots: @@ -28,6 +28,7 @@ private: QPushButton *cancelBtn; QString title; + QString info; QString name; QString defaultName; }; diff --git a/src/src.pro b/src/src.pro index f79e117b..2fa50886 100644 --- a/src/src.pro +++ b/src/src.pro @@ -23,7 +23,6 @@ SOURCES += main.cpp\ vfilelist.cpp \ dialog/vnewfiledialog.cpp \ vedit.cpp \ - vnotefile.cpp \ vdocument.cpp \ utils/vutils.cpp \ vpreviewpage.cpp \ @@ -43,8 +42,9 @@ SOURCES += main.cpp\ vedittab.cpp \ voutline.cpp \ vtoc.cpp \ - vfilelocation.cpp \ - vsingleinstanceguard.cpp + vsingleinstanceguard.cpp \ + vdirectory.cpp \ + vfile.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -56,7 +56,6 @@ HEADERS += vmainwindow.h \ dialog/vnewfiledialog.h \ vedit.h \ vconstants.h \ - vnotefile.h \ vdocument.h \ utils/vutils.h \ vpreviewpage.h \ @@ -76,8 +75,9 @@ HEADERS += vmainwindow.h \ vedittab.h \ voutline.h \ vtoc.h \ - vfilelocation.h \ - vsingleinstanceguard.h + vsingleinstanceguard.h \ + vdirectory.h \ + vfile.h RESOURCES += \ vnote.qrc diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index f6d5a509..406fd841 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -9,7 +9,6 @@ #include #include #include - VUtils::VUtils() { } @@ -202,3 +201,38 @@ bool VUtils::copyFile(const QString &p_srcFilePath, const QString &p_destFilePat } return true; } + +int VUtils::showMessage(QMessageBox::Icon p_icon, const QString &p_title, const QString &p_text, const QString &p_infoText, + QMessageBox::StandardButtons p_buttons, QMessageBox::StandardButton p_defaultBtn, QWidget *p_parent) +{ + QMessageBox msgBox(p_icon, p_title, p_text, p_buttons, p_parent); + msgBox.setInformativeText(p_infoText); + msgBox.setDefaultButton(p_defaultBtn); + return msgBox.exec(); +} + +QString VUtils::generateCopiedFileName(const QString &p_dirPath, const QString &p_fileName) +{ + QString suffix; + QString base = p_fileName; + int dotIdx = p_fileName.lastIndexOf('.'); + if (dotIdx != -1) { + // .md + suffix = p_fileName.right(p_fileName.size() - dotIdx); + base = p_fileName.left(dotIdx); + } + QDir dir(p_dirPath); + QString name = p_fileName; + QString filePath = dir.filePath(name); + int index = 0; + while (QFile(filePath).exists()) { + QString seq; + if (index > 0) { + seq = QString::number(index); + } + index++; + name = QString("%1_copy%2%3").arg(base).arg(seq).arg(suffix); + filePath = dir.filePath(name); + } + return name; +} diff --git a/src/utils/vutils.h b/src/utils/vutils.h index adc6ecd0..3823551b 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "vconfigmanager.h" #include "vconstants.h" @@ -19,6 +20,7 @@ public: static QRgb QRgbFromString(const QString &str); static QString generateImageFileName(const QString &path, const QString &title, const QString &format = "png"); + static QString generateCopiedFileName(const QString &p_dirPath, const QString &p_fileName); static void processStyle(QString &style, const QVector > &varMap); static bool isMarkdown(const QString &fileName); static inline QString directoryNameFromPath(const QString& path); @@ -28,6 +30,9 @@ public: static void makeDirectory(const QString &path); static ClipboardOpType opTypeInClipboard(); static bool copyFile(const QString &p_srcFilePath, const QString &p_destFilePath, bool p_isCut); + static int showMessage(QMessageBox::Icon p_icon, const QString &p_title, const QString &p_text, + const QString &p_infoText, QMessageBox::StandardButtons p_buttons, + QMessageBox::StandardButton p_defaultBtn, QWidget *p_parent); }; inline QString VUtils::directoryNameFromPath(const QString &path) diff --git a/src/vconstants.h b/src/vconstants.h index eadcd9e2..925e7829 100644 --- a/src/vconstants.h +++ b/src/vconstants.h @@ -3,5 +3,6 @@ enum class DocType { Html, Markdown }; enum class ClipboardOpType { Invalid, CopyFile, CopyDir }; +enum class OpenFileMode {Read = 0, Edit}; #endif diff --git a/src/vdirectory.cpp b/src/vdirectory.cpp new file mode 100644 index 00000000..ace3a144 --- /dev/null +++ b/src/vdirectory.cpp @@ -0,0 +1,468 @@ +#include "vdirectory.h" +#include +#include +#include +#include +#include "vconfigmanager.h" +#include "vfile.h" +#include "utils/vutils.h" + +VDirectory::VDirectory(VNotebook *p_notebook, + const QString &p_name, QObject *p_parent) + : QObject(p_parent), m_notebook(p_notebook), m_name(p_name), m_opened(false) +{ +} + +bool VDirectory::open() +{ + if (m_opened) { + return true; + } + Q_ASSERT(m_subDirs.isEmpty() && m_files.isEmpty()); + QString path = retrivePath(); + QJsonObject configJson = VConfigManager::readDirectoryConfig(path); + if (configJson.isEmpty()) { + qWarning() << "invalid directory configuration in path" << path; + return false; + } + + // [sub_directories] section + QJsonArray dirJson = configJson["sub_directories"].toArray(); + for (int i = 0; i < dirJson.size(); ++i) { + QJsonObject dirItem = dirJson[i].toObject(); + VDirectory *dir = new VDirectory(m_notebook, dirItem["name"].toString(), this); + m_subDirs.append(dir); + } + + // [files] section + QJsonArray fileJson = configJson["files"].toArray(); + for (int i = 0; i < fileJson.size(); ++i) { + QJsonObject fileItem = fileJson[i].toObject(); + VFile *file = new VFile(fileItem["name"].toString(), this); + m_files.append(file); + } + m_opened = true; + qDebug() << "dir" << m_name << "open" << m_subDirs.size() << "sub directories" << m_files.size() << "files"; + return true; +} + +void VDirectory::close() +{ + if (!m_opened) { + return; + } + + for (int i = 0; i < m_subDirs.size(); ++i) { + VDirectory *dir = m_subDirs[i]; + dir->close(); + delete dir; + } + m_subDirs.clear(); + + for (int i = 0; i < m_files.size(); ++i) { + VFile *file = m_files[i]; + file->close(); + delete file; + } + m_files.clear(); + + m_opened = false; +} + +QString VDirectory::retrivePath(const VDirectory *p_dir) const +{ + if (!p_dir) { + return ""; + } + VDirectory *parentDir = (VDirectory *)p_dir->parent(); + if (parentDir) { + // Not the root directory + return QDir(retrivePath(parentDir)).filePath(p_dir->getName()); + } else { + return m_notebook->getPath(); + } +} + +QString VDirectory::retriveRelativePath(const VDirectory *p_dir) const +{ + if (!p_dir) { + return ""; + } + VDirectory *parentDir = (VDirectory *)p_dir->parent(); + if (parentDir) { + // Not the root directory + return QDir(retriveRelativePath(parentDir)).filePath(p_dir->getName()); + } else { + return ""; + } +} + +QJsonObject VDirectory::createDirectoryJson() const +{ + QJsonObject dirJson; + dirJson["version"] = "1"; + dirJson["sub_directories"] = QJsonArray(); + dirJson["files"] = QJsonArray(); + return dirJson; +} + +VDirectory *VDirectory::createSubDirectory(const QString &p_name) +{ + Q_ASSERT(!p_name.isEmpty()); + // First open current directory + if (!open()) { + return NULL; + } + QString path = retrivePath(); + QDir dir(path); + if (!dir.mkdir(p_name)) { + qWarning() << "failed to create directory" << p_name << "under" << path; + return NULL; + } + + QJsonObject subJson = createDirectoryJson(); + if (!VConfigManager::writeDirectoryConfig(QDir::cleanPath(QDir(path).filePath(p_name)), subJson)) { + dir.rmdir(p_name); + return NULL; + } + + // Update parent's config file to include this new directory + QJsonObject dirJson = VConfigManager::readDirectoryConfig(path); + Q_ASSERT(!dirJson.isEmpty()); + QJsonObject itemJson; + itemJson["name"] = p_name; + QJsonArray subDirArray = dirJson["sub_directories"].toArray(); + subDirArray.append(itemJson); + dirJson["sub_directories"] = subDirArray; + if (!VConfigManager::writeDirectoryConfig(path, dirJson)) { + VConfigManager::deleteDirectoryConfig(QDir(path).filePath(p_name)); + dir.rmdir(p_name); + return NULL; + } + + VDirectory *ret = new VDirectory(m_notebook, p_name, this); + m_subDirs.append(ret); + return ret; +} + +VDirectory *VDirectory::findSubDirectory(const QString &p_name) +{ + if (!m_opened && !open()) { + return NULL; + } + for (int i = 0; i < m_subDirs.size(); ++i) { + if (p_name == m_subDirs[i]->getName()) { + return m_subDirs[i]; + } + } + return NULL; +} + +VFile *VDirectory::findFile(const QString &p_name) +{ + if (!m_opened && !open()) { + return NULL; + } + for (int i = 0; i < m_files.size(); ++i) { + if (p_name == m_files[i]->getName()) { + return m_files[i]; + } + } + return NULL; +} + +VFile *VDirectory::createFile(const QString &p_name) +{ + Q_ASSERT(!p_name.isEmpty()); + if (!open()) { + return NULL; + } + QString path = retrivePath(); + QString filePath = QDir(path).filePath(p_name); + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly)) { + qWarning() << "failed to create file" << p_name; + return NULL; + } + file.close(); + qDebug() << "file created" << p_name; + + if (!createFileInConfig(p_name)) { + file.remove(); + return NULL; + } + + VFile *ret = new VFile(p_name, this); + m_files.append(ret); + return ret; +} + +bool VDirectory::createFileInConfig(const QString &p_name, int p_index) +{ + QString path = retrivePath(); + QJsonObject dirJson = VConfigManager::readDirectoryConfig(path); + Q_ASSERT(!dirJson.isEmpty()); + QJsonObject itemJson; + itemJson["name"] = p_name; + QJsonArray fileArray = dirJson["files"].toArray(); + if (p_index == -1) { + fileArray.append(itemJson); + } else { + fileArray.insert(p_index, itemJson); + } + dirJson["files"] = fileArray; + if (!VConfigManager::writeDirectoryConfig(path, dirJson)) { + return false; + } + return true; +} + +VFile *VDirectory::addFile(VFile *p_file, int p_index) +{ + if (!open()) { + return NULL; + } + if (!createFileInConfig(p_file->getName(), p_index)) { + return NULL; + } + if (p_index == -1) { + m_files.append(p_file); + } else { + m_files.insert(p_index, p_file); + } + p_file->setParent(this); + return p_file; +} + +VFile *VDirectory::addFile(const QString &p_name, int p_index) +{ + if (!open()) { + return NULL; + } + if (!createFileInConfig(p_name, p_index)) { + return NULL; + } + VFile *file = new VFile(p_name, this); + if (!file) { + return NULL; + } + if (p_index == -1) { + m_files.append(file); + } else { + m_files.insert(p_index, file); + } + return file; +} + +void VDirectory::deleteSubDirectory(VDirectory *p_subDir) +{ + if (!open()) { + return; + } + QString path = retrivePath(); + + int index; + for (index = 0; index < m_subDirs.size(); ++index) { + if (m_subDirs[index] == p_subDir) { + break; + } + } + if (index == m_subDirs.size()) { + return; + } + m_subDirs.remove(index); + QString name = p_subDir->getName(); + // Update config to exclude this directory + QJsonObject dirJson = VConfigManager::readDirectoryConfig(path); + QJsonArray subDirArray = dirJson["sub_directories"].toArray(); + bool deleted = false; + for (int i = 0; i < subDirArray.size(); ++i) { + QJsonObject ele = subDirArray[i].toObject(); + if (ele["name"].toString() == name) { + subDirArray.removeAt(i); + deleted = true; + break; + } + } + Q_ASSERT(deleted); + dirJson["sub_directories"] = subDirArray; + if (!VConfigManager::writeDirectoryConfig(path, dirJson)) { + qWarning() << "failed to update configuration in" << path; + } + + // Delete the entire directory + p_subDir->close(); + delete p_subDir; + QString dirName = QDir(path).filePath(name); + QDir dir(dirName); + if (!dir.removeRecursively()) { + qWarning() << "failed to delete" << dirName << "recursively"; + } else { + qDebug() << "deleted" << dirName; + } +} + +// After calling this, p_file->parent() remain the same. +int VDirectory::removeFile(VFile *p_file) +{ + Q_ASSERT(m_opened); + Q_ASSERT(p_file); + + QString path = retrivePath(); + + int index; + for (index = 0; index < m_files.size(); ++index) { + if (m_files[index] == p_file) { + break; + } + } + Q_ASSERT(index != m_files.size()); + m_files.remove(index); + QString name = p_file->getName(); + + // Update config to exclude this file + QJsonObject dirJson = VConfigManager::readDirectoryConfig(path); + QJsonArray subFileArray = dirJson["files"].toArray(); + bool deleted = false; + for (int i = 0; i < subFileArray.size(); ++i) { + QJsonObject ele = subFileArray[i].toObject(); + if (ele["name"].toString() == name) { + subFileArray.removeAt(i); + deleted = true; + index = i; + break; + } + } + Q_ASSERT(deleted); + dirJson["files"] = subFileArray; + if (!VConfigManager::writeDirectoryConfig(path, dirJson)) { + qWarning() << "failed to update configuration in" << path; + } + return index; +} + +void VDirectory::deleteFile(VFile *p_file) +{ + removeFile(p_file); + + // Delete the file + Q_ASSERT(!p_file->isOpened()); + p_file->deleteDiskFile(); + delete p_file; +} + +bool VDirectory::rename(const QString &p_name) +{ + if (m_name == p_name) { + return true; + } + VDirectory *parentDir = getParentDirectory(); + Q_ASSERT(parentDir); + QString parentPath = parentDir->retrivePath(); + QDir dir(parentPath); + QString name = m_name; + if (!dir.rename(m_name, p_name)) { + qWarning() << "failed to rename directory" << m_name << "to" << p_name; + return false; + } + m_name = p_name; + + // Update parent's config file + QJsonObject dirJson = VConfigManager::readDirectoryConfig(parentPath); + QJsonArray subDirArray = dirJson["sub_directories"].toArray(); + int index = 0; + for (index = 0; index < subDirArray.size(); ++index) { + QJsonObject ele = subDirArray[index].toObject(); + if (ele["name"].toString() == name) { + ele["name"] = p_name; + subDirArray[index] = ele; + break; + } + } + Q_ASSERT(index != subDirArray.size()); + dirJson["sub_directories"] = subDirArray; + if (!VConfigManager::writeDirectoryConfig(parentPath, dirJson)) { + return false; + } + return true; +} + +VFile *VDirectory::copyFile(VDirectory *p_destDir, const QString &p_destName, + VFile *p_srcFile, bool p_cut) +{ + QString srcPath = QDir::cleanPath(p_srcFile->retrivePath()); + QString destPath = QDir::cleanPath(QDir(p_destDir->retrivePath()).filePath(p_destName)); + if (srcPath == destPath) { + return p_srcFile; + } + VDirectory *srcDir = p_srcFile->getDirectory(); + DocType docType = p_srcFile->getDocType(); + DocType newDocType = VUtils::isMarkdown(destPath) ? DocType::Markdown : DocType::Html; + + QVector images; + if (docType == DocType::Markdown) { + images = VUtils::imagesFromMarkdownFile(srcPath); + } + + // Copy the file + if (!VUtils::copyFile(srcPath, destPath, p_cut)) { + return NULL; + } + + // Handle VDirectory and VFile + int index = -1; + VFile *destFile = NULL; + if (p_cut) { + // Remove the file from config + index = srcDir->removeFile(p_srcFile); + p_srcFile->setName(p_destName); + if (srcDir != p_destDir) { + index = -1; + } + // Add the file to new dir's config + destFile = p_destDir->addFile(p_srcFile, index); + } else { + destFile = p_destDir->addFile(p_destName, -1); + } + if (!destFile) { + return NULL; + } + + if (docType != newDocType) { + destFile->convert(docType, newDocType); + } + + // We need to copy images when it is still markdown + if (!images.isEmpty()) { + if (newDocType == DocType::Markdown) { + QString dirPath = destFile->retriveImagePath(); + VUtils::makeDirectory(dirPath); + int nrPasted = 0; + for (int i = 0; i < images.size(); ++i) { + if (!QFile(images[i]).exists()) { + continue; + } + + QString destImagePath = QDir(dirPath).filePath(VUtils::fileNameFromPath(images[i])); + // Copy or Cut the images accordingly. + if (VUtils::copyFile(images[i], destImagePath, p_cut)) { + nrPasted++; + } else { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Failed to copy image %1.").arg(images[i]), + tr("Please check if there already exists a file with the same name and manually copy it"), + QMessageBox::Ok, QMessageBox::Ok, NULL); + } + } + qDebug() << "pasted" << nrPasted << "images sucessfully"; + } else { + // Delete the images + for (int i = 0; i < images.size(); ++i) { + QFile file(images[i]); + file.remove(); + } + } + } + + return destFile; +} diff --git a/src/vdirectory.h b/src/vdirectory.h new file mode 100644 index 00000000..e0352d92 --- /dev/null +++ b/src/vdirectory.h @@ -0,0 +1,109 @@ +#ifndef VDIRECTORY_H +#define VDIRECTORY_H + +#include +#include +#include +#include +#include +#include "vnotebook.h" + +class VFile; + +class VDirectory : public QObject +{ + Q_OBJECT +public: + VDirectory(VNotebook *p_notebook, + const QString &p_name, QObject *p_parent = 0); + bool open(); + void close(); + VDirectory *createSubDirectory(const QString &p_name); + VDirectory *findSubDirectory(const QString &p_name); + VFile *findFile(const QString &p_name); + VFile *createFile(const QString &p_name); + void deleteSubDirectory(VDirectory *p_subDir); + // Remove the file in the config and m_files without deleting it in the disk. + int removeFile(VFile *p_file); + // Add the file in the config and m_files. If @p_index is -1, add it at the end. + // Return the VFile if succeed. + VFile *addFile(VFile *p_file, int p_index); + VFile *addFile(const QString &p_name, int p_index); + void deleteFile(VFile *p_file); + bool rename(const QString &p_name); + + inline const QVector &getSubDirs() const; + inline const QString &getName() const; + inline bool isOpened() const; + inline VDirectory *getParentDirectory(); + inline const QVector &getFiles() const; + inline QString retrivePath() const; + inline QString retriveRelativePath() const; + inline QString retriveNotebook() const; + + // Copy @p_srcFile to @p_destDir, setting new name to @p_destName. + // @p_cut: copy or cut. Returns the dest VFile. + static VFile *copyFile(VDirectory *p_destDir, const QString &p_destName, + VFile *p_srcFile, bool p_cut); +signals: + +public slots: + +private: + // Get the path of @p_dir recursively + QString retrivePath(const VDirectory *p_dir) const; + // Get teh relative path of @p_dir recursively related to the notebook path + QString retriveRelativePath(const VDirectory *p_dir) const; + QJsonObject createDirectoryJson() const; + bool createFileInConfig(const QString &p_name, int p_index = -1); + + QPointer m_notebook; + QString m_name; + // Owner of the sub-directories + QVector m_subDirs; + // Owner of the files + QVector m_files; + bool m_opened; +}; + +inline const QVector &VDirectory::getSubDirs() const +{ + return m_subDirs; +} + +inline const QString &VDirectory::getName() const +{ + return m_name; +} + +inline bool VDirectory::isOpened() const +{ + return m_opened; +} + +inline VDirectory *VDirectory::getParentDirectory() +{ + return (VDirectory *)this->parent(); +} + +inline const QVector &VDirectory::getFiles() const +{ + return m_files; +} + +inline QString VDirectory::retriveNotebook() const +{ + return m_notebook->getName(); +} + +inline QString VDirectory::retrivePath() const +{ + return retrivePath(this); +} + +inline QString VDirectory::retriveRelativePath() const +{ + return retriveRelativePath(this); +} + +#endif // VDIRECTORY_H diff --git a/src/vdirectorytree.cpp b/src/vdirectorytree.cpp index 4b23453f..c34fa543 100644 --- a/src/vdirectorytree.cpp +++ b/src/vdirectorytree.cpp @@ -4,6 +4,8 @@ #include "vconfigmanager.h" #include "dialog/vdirinfodialog.h" #include "vnote.h" +#include "vdirectory.h" +#include "utils/vutils.h" VDirectoryTree::VDirectoryTree(VNote *vnote, QWidget *parent) : QTreeWidget(parent), vnote(vnote) @@ -14,7 +16,7 @@ VDirectoryTree::VDirectoryTree(VNote *vnote, QWidget *parent) initActions(); connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), - this, SLOT(updateItemSubtree(QTreeWidgetItem*))); + this, SLOT(updateChildren(QTreeWidgetItem*))); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); connect(this, &VDirectoryTree::currentItemChanged, @@ -29,11 +31,6 @@ void VDirectoryTree::initActions() 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, @@ -52,208 +49,159 @@ void VDirectoryTree::initActions() this, &VDirectoryTree::editDirectoryInfo); } -void VDirectoryTree::setNotebook(const QString& notebookName) +void VDirectoryTree::setNotebook(VNotebook *p_notebook) { - if (notebook == notebookName) { + if (m_notebook == p_notebook) { return; } - notebook = notebookName; - treePath = ""; - if (notebook.isEmpty()) { + if (m_notebook) { + // Disconnect + disconnect((VNotebook *)m_notebook, &VNotebook::contentChanged, + this, &VDirectoryTree::updateDirectoryTree); + } + m_notebook = p_notebook; + if (m_notebook) { + connect((VNotebook *)m_notebook, &VNotebook::contentChanged, + this, &VDirectoryTree::updateDirectoryTree); + } else { clear(); return; } - const QVector ¬ebooks = vnote->getNotebooks(); - for (int i = 0; i < notebooks.size(); ++i) { - if (notebooks[i]->getName() == notebook) { - treePath = notebooks[i]->getPath(); - break; - } + if (!m_notebook->open()) { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Failed to open notebook %1").arg(m_notebook->getName()), "", + QMessageBox::Ok, QMessageBox::Ok, this); + clear(); + return; } - Q_ASSERT(!treePath.isEmpty()); - updateDirectoryTree(); - if (topLevelItemCount() > 0) { - setCurrentItem(topLevelItem(0)); - } } -bool VDirectoryTree::validatePath(const QString &path) +void VDirectoryTree::fillTreeItem(QTreeWidgetItem &p_item, const QString &p_name, + VDirectory *p_directory, const QIcon &p_icon) { - return QDir(path).exists(); + p_item.setText(0, p_name); + p_item.setData(0, Qt::UserRole, QVariant::fromValue(p_directory)); + p_item.setIcon(0, p_icon); } void VDirectoryTree::updateDirectoryTree() { - updateDirectoryTreeTopLevel(); - - int nrTopLevelItems = topLevelItemCount(); - for (int i = 0; i < nrTopLevelItems; ++i) { - QTreeWidgetItem *item = topLevelItem(i); - Q_ASSERT(item); - QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject(); - Q_ASSERT(!itemJson.isEmpty()); - updateDirectoryTreeOne(*item, itemJson["name"].toString(), 1); - } -} - -void VDirectoryTree::fillDirectoryTreeItem(QTreeWidgetItem &item, QJsonObject itemJson) -{ - item.setText(0, itemJson["name"].toString()); - item.setData(0, Qt::UserRole, itemJson); - item.setIcon(0, QIcon(":/resources/icons/dir_item.svg")); -} - -QTreeWidgetItem* VDirectoryTree::insertDirectoryTreeItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding, - const QJsonObject &newItem) -{ - QTreeWidgetItem *item; - if (parent) { - if (preceding) { - item = new QTreeWidgetItem(parent, preceding); - } else { - item = new QTreeWidgetItem(parent); - } - } else { - if (preceding) { - item = new QTreeWidgetItem(this, preceding); - } else { - item = new QTreeWidgetItem(this); - } - } - - fillDirectoryTreeItem(*item, newItem); - return item; -} - -void VDirectoryTree::removeDirectoryTreeItem(QTreeWidgetItem *item) -{ - delete item; -} - -void VDirectoryTree::updateDirectoryTreeTopLevel() -{ - const QString &path = treePath; - clear(); + VDirectory *rootDir = m_notebook->getRootDir(); + const QVector &subDirs = rootDir->getSubDirs(); + for (int i = 0; i < subDirs.size(); ++i) { + VDirectory *dir = subDirs[i]; + QTreeWidgetItem *item = new QTreeWidgetItem(this); - if (!validatePath(path)) { - qDebug() << "invalid notebook path:" << path; - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook path."), - QMessageBox::Ok, this); - msgBox.setInformativeText(QString("Notebook path \"%1\" either does not exist or is not valid.") - .arg(path)); - msgBox.exec(); - return; + fillTreeItem(*item, dir->getName(), dir, + QIcon(":/resources/icons/dir_item.svg")); + + updateDirectoryTreeOne(item, 1); } - - 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."), - QMessageBox::Ok, this); - 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"; + setCurrentItem(topLevelItem(0)); } -void VDirectoryTree::updateDirectoryTreeOne(QTreeWidgetItem &parent, const QString &relativePath, - int depth) +void VDirectoryTree::updateDirectoryTreeOne(QTreeWidgetItem *p_parent, int depth) { - Q_ASSERT(parent.childCount() == 0); - // Going deep enough + Q_ASSERT(p_parent->childCount() == 0); if (depth <= 0) { return; } - - qDebug() << "update directory" << relativePath; - - 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."), - QMessageBox::Ok, this); - msgBox.setInformativeText(QString("Notebook directory \"%1\" either does not exist or is not a valid notebook directory.") - .arg(path)); - msgBox.exec(); + VDirectory *dir = getVDirectory(p_parent); + if (!dir->open()) { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Failed to open directory %1").arg(dir->getName()), "", + QMessageBox::Ok, QMessageBox::Ok, this); return; } + const QVector &subDirs = dir->getSubDirs(); + for (int i = 0; i < subDirs.size(); ++i) { + VDirectory *subDir = subDirs[i]; + QTreeWidgetItem *item = new QTreeWidgetItem(p_parent); - 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."), - QMessageBox::Ok, this); - msgBox.setInformativeText(QString("Notebook directory \"%1\" does not contain a valid configuration file.") - .arg(path)); - msgBox.exec(); - return; - } + fillTreeItem(*item, subDir->getName(), subDir, + QIcon(":/resources/icons/dir_item.svg")); - // 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, QDir::cleanPath(QDir(relativePath).filePath(dirItem["name"].toString())), depth - 1); + updateDirectoryTreeOne(item, depth - 1); } } -QString VDirectoryTree::calculateItemRelativePath(QTreeWidgetItem *item) +// Update @p_item's children items +void VDirectoryTree::updateChildren(QTreeWidgetItem *p_item) { - if (!item) { - return "."; - } - QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject(); - Q_ASSERT(!itemJson.isEmpty()); - QString name = itemJson["name"].toString(); - Q_ASSERT(!name.isEmpty()); - return QDir::cleanPath(calculateItemRelativePath(item->parent()) + - QDir::separator() + name); -} - -void VDirectoryTree::updateItemSubtree(QTreeWidgetItem *item) -{ - QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject(); - Q_ASSERT(!itemJson.isEmpty()); - QString relativePath = calculateItemRelativePath(item); - int nrChild = item->childCount(); + Q_ASSERT(p_item); + int nrChild = p_item->childCount(); if (nrChild == 0) { - updateDirectoryTreeOne(*item, relativePath, 2); + return; + } + for (int i = 0; i < nrChild; ++i) { + QTreeWidgetItem *childItem = p_item->child(i); + if (childItem->childCount() > 0) { + continue; + } + updateDirectoryTreeOne(childItem, 1); + } +} + +QVector VDirectoryTree::updateItemChildrenAdded(QTreeWidgetItem *p_item) +{ + QVector ret; + QPointer parentDir; + if (p_item) { + parentDir = getVDirectory(p_item); } else { - for (int i = 0; i < nrChild; ++i) { - QTreeWidgetItem *childItem = item->child(i); - if (childItem->childCount() > 0) { - continue; + parentDir = m_notebook->getRootDir(); + } + const QVector &subDirs = parentDir->getSubDirs(); + for (int i = 0; i < subDirs.size(); ++i) { + int nrChild; + if (p_item) { + nrChild = p_item->childCount(); + } else { + nrChild = this->topLevelItemCount(); + } + VDirectory *dir = subDirs[i]; + QTreeWidgetItem *item; + if (i >= nrChild) { + if (p_item) { + item = new QTreeWidgetItem(p_item); + } else { + item = new QTreeWidgetItem(this); + } + fillTreeItem(*item, dir->getName(), dir, QIcon(":/resources/icons/dir_item.svg")); + updateDirectoryTreeOne(item, 1); + ret.append(item); + } else { + VDirectory *itemDir; + if (p_item) { + itemDir = getVDirectory(p_item->child(i)); + } else { + itemDir = getVDirectory(topLevelItem(i)); + } + if (itemDir != dir) { + item = new QTreeWidgetItem(); + if (p_item) { + p_item->insertChild(i, item); + } else { + this->insertTopLevelItem(i, item); + } + fillTreeItem(*item, dir->getName(), dir, QIcon(":/resources/icons/dir_item.svg")); + updateDirectoryTreeOne(item, 1); + ret.append(item); } - QJsonObject childJson = childItem->data(0, Qt::UserRole).toJsonObject(); - Q_ASSERT(!childJson.isEmpty()); - updateDirectoryTreeOne(*childItem, - QDir::cleanPath(QDir(relativePath).filePath(childJson["name"].toString())), 1); } } + qDebug() << ret.size() << "items added"; + return ret; } void VDirectoryTree::contextMenuRequested(QPoint pos) { + if (!m_notebook) { + return; + } QTreeWidgetItem *item = itemAt(pos); QMenu menu(this); @@ -265,7 +213,6 @@ void VDirectoryTree::contextMenuRequested(QPoint pos) if (item->parent()) { // Low-level item menu.addAction(newSubDirAct); - menu.addAction(newSiblingDirAct); } else { // Top-level item menu.addAction(newRootDirAct); @@ -277,60 +224,40 @@ void VDirectoryTree::contextMenuRequested(QPoint pos) 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"); - do { - VNewDirDialog dialog(QString("Create a new directory under %1").arg(parentItemName), text, - defaultText, this); - if (dialog.exec() == QDialog::Accepted) { - QString name = dialog.getNameInput(); - if (isConflictNameWithChildren(parentItem, name)) { - text = "Name already exists.\nPlease choose another name:"; - defaultText = name; - continue; - } - QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(parentItem, name); - if (newItem) { - this->setCurrentItem(newItem); - } - } - break; - } while (true); -} - void VDirectoryTree::newSubDirectory() { + if (!m_notebook) { + return; + } QTreeWidgetItem *curItem = currentItem(); if (!curItem) { return; } - QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject(); - QString curItemName = curItemJson["name"].toString(); + VDirectory *curDir = getVDirectory(curItem); + QString info = QString("Create sub-directory under %1.").arg(curDir->getName()); QString text("&Directory name:"); QString defaultText("new_directory"); do { - VNewDirDialog dialog(QString("Create a new directory under %1").arg(curItemName), text, - defaultText, this); + VNewDirDialog dialog(tr("Create directory"), info, text, defaultText, this); if (dialog.exec() == QDialog::Accepted) { QString name = dialog.getNameInput(); - if (isConflictNameWithChildren(curItem, name)) { - text = "Name already exists.\nPlease choose another name:"; + if (curDir->findSubDirectory(name)) { + info = QString("Name already exists under %1.\nPlease choose another name.").arg(curDir->getName()); defaultText = name; continue; } - QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(curItem, name); - if (newItem) { - this->setCurrentItem(newItem); + VDirectory *subDir = curDir->createSubDirectory(name); + if (!subDir) { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Failed to create directory %1.").arg(name), "", + QMessageBox::Ok, QMessageBox::Ok, this); + return; } + QVector items = updateItemChildrenAdded(curItem); + Q_ASSERT(items.size() == 1); + setCurrentItem(items[0]); } break; } while (true); @@ -338,23 +265,32 @@ void VDirectoryTree::newSubDirectory() void VDirectoryTree::newRootDirectory() { + if (!m_notebook) { + return; + } + QString info = QString("Create root directory in notebook %1.").arg(m_notebook->getName()); QString text("&Directory name:"); QString defaultText("new_directory"); - + VDirectory *rootDir = m_notebook->getRootDir(); do { - VNewDirDialog dialog(tr("Create a new root directory"), text, - defaultText, this); + VNewDirDialog dialog(tr("Create root directory"), info, text, defaultText, this); if (dialog.exec() == QDialog::Accepted) { QString name = dialog.getNameInput(); - if (isConflictNameWithChildren(NULL, name)) { - text = "Name already exists.\nPlease choose another name:"; + if (rootDir->findSubDirectory(name)) { + info = QString("Name already exists in notebook %1.\nPlease choose another name.").arg(m_notebook->getName()); defaultText = name; continue; } - QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(NULL, name); - if (newItem) { - this->setCurrentItem(newItem); + VDirectory *subDir = rootDir->createSubDirectory(name); + if (!subDir) { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Failed to create directory %1.").arg(name), "", + QMessageBox::Ok, QMessageBox::Ok, this); + return; } + QVector items = updateItemChildrenAdded(NULL); + Q_ASSERT(items.size() == 1); + setCurrentItem(items[0]); } break; } while (true); @@ -366,140 +302,26 @@ void VDirectoryTree::deleteDirectory() if (!curItem) { return; } - 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), QMessageBox::Ok | QMessageBox::Cancel, this); - msgBox.setInformativeText(tr("This will delete any files under this directory.")); - msgBox.setDefaultButton(QMessageBox::Cancel); - if (msgBox.exec() == QMessageBox::Ok) { - deleteDirectoryAndUpdateTree(curItem); + VDirectory *curDir = getVDirectory(curItem); + int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Are you sure to delete directory %1?").arg(curDir->getName()), + tr("This will delete any files under this directory."), QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok, this); + if (ret == QMessageBox::Ok) { + VDirectory *parentDir = curDir->getParentDirectory(); + Q_ASSERT(parentDir); + parentDir->deleteSubDirectory(curDir); + delete curItem; } } -QTreeWidgetItem* VDirectoryTree::createDirectoryAndUpdateTree(QTreeWidgetItem *parent, - const QString &name) -{ - QString relativePath = calculateItemRelativePath(parent); - QString path = QDir::cleanPath(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), QMessageBox::Ok, this); - 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["sub_directories"] = QJsonArray(); - configJson["files"] = QJsonArray(); - - if (!VConfigManager::writeDirectoryConfig(QDir::cleanPath(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; - QJsonArray subDirArray = configJson["sub_directories"].toArray(); - subDirArray.append(itemJson); - configJson["sub_directories"] = subDirArray; - if (!VConfigManager::writeDirectoryConfig(path, configJson)) { - VConfigManager::deleteDirectoryConfig(QDir::cleanPath(QDir(path).filePath(name))); - dir.rmdir(name); - return NULL; - } - - return insertDirectoryTreeItem(parent, NULL, itemJson); -} - -void VDirectoryTree::deleteDirectoryAndUpdateTree(QTreeWidgetItem *item) -{ - if (!item) { - return; - } - QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject(); - QString itemName = itemJson["name"].toString(); - QString parentRelativePath = calculateItemRelativePath(item->parent()); - - // Update parent's config file to exclude this directory - QString path = QDir::cleanPath(QDir(treePath).filePath(parentRelativePath)); - 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::cleanPath(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()); + emit currentDirectoryChanged(NULL); return; } - QJsonObject itemJson = currentItem->data(0, Qt::UserRole).toJsonObject(); - Q_ASSERT(!itemJson.isEmpty()); - itemJson["notebook"] = notebook; - itemJson["relative_path"] = calculateItemRelativePath(currentItem); - emit currentDirectoryChanged(itemJson); + emit currentDirectoryChanged(getVDirectory(currentItem)); } void VDirectoryTree::editDirectoryInfo() @@ -508,80 +330,33 @@ void VDirectoryTree::editDirectoryInfo() if (!curItem) { return; } - QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject(); - QString curItemName = curItemJson["name"].toString(); + VDirectory *curDir = getVDirectory(curItem); + VDirectory *parentDir = curDir->getParentDirectory(); + QString curName = curDir->getName(); QString info; - QString defaultName = curItemName; + QString defaultName = curName; do { VDirInfoDialog dialog(tr("Directory Information"), info, defaultName, this); if (dialog.exec() == QDialog::Accepted) { QString name = dialog.getNameInput(); - if (name == curItemName) { + if (name == curName) { return; } - if (isConflictNameWithChildren(curItem->parent(), name)) { - info = "Name already exists.\nPlease choose another name:"; + if (parentDir->findSubDirectory(name)) { + info = "Name already exists.\nPlease choose another name."; defaultName = name; continue; } - renameDirectory(curItem, name); + if (!curDir->rename(name)) { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Failed to rename directory %1.").arg(curName), "", + QMessageBox::Ok, QMessageBox::Ok, this); + return; + } + curItem->setText(0, name); } break; } while (true); } - -void VDirectoryTree::renameDirectory(QTreeWidgetItem *item, const QString &newName) -{ - if (!item) { - return; - } - QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject(); - QString name = itemJson["name"].toString(); - - QTreeWidgetItem *parent = item->parent(); - QString parentRelativePath = calculateItemRelativePath(parent); - - QString path = QDir::cleanPath(QDir(treePath).filePath(parentRelativePath)); - QDir dir(path); - - if (!dir.rename(name, newName)) { - qWarning() << "error: fail to rename directory" << name << "under" << path; - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Could not rename directory \"%1\" under \"%2\".") - .arg(name).arg(path), QMessageBox::Ok, this); - msgBox.setInformativeText(QString("Please check if there already exists a directory named \"%1\".").arg(name)); - msgBox.exec(); - return; - } - - // Update parent's config file - QJsonObject configJson = VConfigManager::readDirectoryConfig(path); - Q_ASSERT(!configJson.isEmpty()); - QJsonArray subDirArray = configJson["sub_directories"].toArray(); - int index = 0; - for (index = 0; index < subDirArray.size(); ++index) { - QJsonObject tmp = subDirArray[index].toObject(); - if (tmp["name"].toString() == name) { - tmp["name"] = newName; - subDirArray[index] = tmp; - break; - } - } - Q_ASSERT(index != subDirArray.size()); - configJson["sub_directories"] = subDirArray; - if (!VConfigManager::writeDirectoryConfig(path, configJson)) { - dir.rename(newName, name); - return; - } - - // Update item - itemJson["name"] = newName; - item->setData(0, Qt::UserRole, itemJson); - item->setText(0, newName); - - QString oldPath = QDir::cleanPath(QDir(parentRelativePath).filePath(name)); - QString newPath = QDir::cleanPath(QDir(parentRelativePath).filePath(newName)); - qDebug() << "directory renamed" << oldPath << "to" << newPath; - emit directoryRenamed(notebook, oldPath, newPath); -} diff --git a/src/vdirectorytree.h b/src/vdirectorytree.h index 47acc5c1..6460376f 100644 --- a/src/vdirectorytree.h +++ b/src/vdirectorytree.h @@ -3,6 +3,9 @@ #include #include +#include +#include +#include "vdirectory.h" #include "vnotebook.h" class VNote; @@ -14,56 +17,35 @@ public: explicit VDirectoryTree(VNote *vnote, QWidget *parent = 0); signals: - void currentDirectoryChanged(QJsonObject itemJson); + void currentDirectoryChanged(VDirectory *p_directory); void directoryRenamed(const QString ¬ebook, const QString &oldRelativePath, const QString &newRelativePath); public slots: - void setNotebook(const QString ¬ebookName); + void setNotebook(VNotebook *p_notebook); void newRootDirectory(); void deleteDirectory(); void editDirectoryInfo(); + void updateDirectoryTree(); 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 updateChildren(QTreeWidgetItem *p_item); void contextMenuRequested(QPoint pos); - void newSiblingDirectory(); void newSubDirectory(); void currentDirectoryItemChanged(QTreeWidgetItem *currentItem); private: - QString calculateItemRelativePath(QTreeWidgetItem *item); - // 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(); - - // @relativePath is the relative path of the direcotry we are updating - void updateDirectoryTreeOne(QTreeWidgetItem &parent, const QString &relativePath, int depth); - // Validate if a directory is valid - bool validatePath(const QString &path); - - void fillDirectoryTreeItem(QTreeWidgetItem &item, QJsonObject itemJson); + void updateDirectoryTreeOne(QTreeWidgetItem *p_parent, int depth); + void fillTreeItem(QTreeWidgetItem &p_item, const QString &p_name, + VDirectory *p_directory, const QIcon &p_icon); void initActions(); - QTreeWidgetItem* createDirectoryAndUpdateTree(QTreeWidgetItem *parent, const QString &name); - void deleteDirectoryAndUpdateTree(QTreeWidgetItem *item); - // If @name conflict with the children's names of @parent. - bool isConflictNameWithChildren(const QTreeWidgetItem *parent, const QString &name); - - QTreeWidgetItem* insertDirectoryTreeItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding, - const QJsonObject &newItem); - void removeDirectoryTreeItem(QTreeWidgetItem *item); - void renameDirectory(QTreeWidgetItem *item, const QString &newName); + // New directories added to @p_item. Update @p_item's children items. + // If @p_item is NULL, then top level items added. + QVector updateItemChildrenAdded(QTreeWidgetItem *p_item); + inline QPointer getVDirectory(QTreeWidgetItem *p_item); VNote *vnote; - - QString notebook; - // Used for cache - QString treePath; + QPointer m_notebook; // Actions QAction *newRootDirAct; @@ -73,4 +55,10 @@ private: QAction *dirInfoAct; }; +inline QPointer VDirectoryTree::getVDirectory(QTreeWidgetItem *p_item) +{ + Q_ASSERT(p_item); + return p_item->data(0, Qt::UserRole).value(); +} + #endif // VDIRECTORYTREE_H diff --git a/src/vedit.cpp b/src/vedit.cpp index 024b247c..ef1ed064 100644 --- a/src/vedit.cpp +++ b/src/vedit.cpp @@ -10,18 +10,20 @@ extern VConfigManager vconfig; -VEdit::VEdit(VNoteFile *noteFile, QWidget *parent) - : QTextEdit(parent), noteFile(noteFile), mdHighlighter(NULL) +VEdit::VEdit(VFile *p_file, QWidget *p_parent) + : QTextEdit(p_parent), m_file(p_file), mdHighlighter(NULL) { - if (noteFile->docType == DocType::Markdown) { + connect(document(), &QTextDocument::modificationChanged, + (VFile *)m_file, &VFile::setModified); + + if (m_file->getDocType() == DocType::Markdown) { setAcceptRichText(false); mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(), 500, document()); connect(mdHighlighter, &HGMarkdownHighlighter::highlightCompleted, this, &VEdit::generateEditOutline); - editOps = new VMdEditOperations(this, noteFile); + editOps = new VMdEditOperations(this, m_file); } else { - setAutoFormatting(QTextEdit::AutoBulletList); editOps = NULL; } @@ -33,6 +35,10 @@ VEdit::VEdit(VNoteFile *noteFile, QWidget *parent) VEdit::~VEdit() { + if (m_file) { + disconnect(document(), &QTextDocument::modificationChanged, + (VFile *)m_file, &VFile::setModified); + } if (editOps) { delete editOps; editOps = NULL; @@ -41,7 +47,7 @@ VEdit::~VEdit() void VEdit::updateFontAndPalette() { - switch (noteFile->docType) { + switch (m_file->getDocType()) { case DocType::Markdown: setFont(vconfig.getMdEditFont()); setPalette(vconfig.getMdEditPalette()); @@ -51,14 +57,14 @@ void VEdit::updateFontAndPalette() setPalette(vconfig.getBaseEditPalette()); break; default: - qWarning() << "error: unknown doc type" << int(noteFile->docType); + qWarning() << "error: unknown doc type" << int(m_file->getDocType()); return; } } void VEdit::updateTabSettings() { - switch (noteFile->docType) { + switch (m_file->getDocType()) { case DocType::Markdown: if (vconfig.getTabStopWidth() > 0) { QFontMetrics metrics(vconfig.getMdEditFont()); @@ -72,7 +78,7 @@ void VEdit::updateTabSettings() } break; default: - qWarning() << "error: unknown doc type" << int(noteFile->docType); + qWarning() << "error: unknown doc type" << int(m_file->getDocType()); return; } @@ -84,27 +90,28 @@ void VEdit::updateTabSettings() void VEdit::beginEdit() { - setReadOnly(false); updateTabSettings(); updateFontAndPalette(); - switch (noteFile->docType) { + switch (m_file->getDocType()) { case DocType::Html: - setHtml(noteFile->content); + setHtml(m_file->getContent()); break; case DocType::Markdown: setFont(vconfig.getMdEditFont()); - setPlainText(noteFile->content); + setPlainText(m_file->getContent()); initInitImages(); break; default: - qWarning() << "error: unknown doc type" << int(noteFile->docType); + qWarning() << "error: unknown doc type" << int(m_file->getDocType()); } + setReadOnly(false); + setModified(false); } void VEdit::endEdit() { setReadOnly(true); - if (noteFile->docType == DocType::Markdown) { + if (m_file->getDocType() == DocType::Markdown) { clearUnusedImages(); } } @@ -115,31 +122,32 @@ void VEdit::saveFile() return; } - switch (noteFile->docType) { + switch (m_file->getDocType()) { case DocType::Html: - noteFile->content = toHtml(); + m_file->setContent(toHtml()); break; case DocType::Markdown: - noteFile->content = toPlainText(); + m_file->setContent(toPlainText()); break; default: - qWarning() << "error: unknown doc type" << int(noteFile->docType); + qWarning() << "error: unknown doc type" << int(m_file->getDocType()); } document()->setModified(false); } void VEdit::reloadFile() { - switch (noteFile->docType) { + switch (m_file->getDocType()) { case DocType::Html: - setHtml(noteFile->content); + setHtml(m_file->getContent()); break; case DocType::Markdown: - setPlainText(noteFile->content); + setPlainText(m_file->getContent()); break; default: - qWarning() << "error: unknown doc type" << int(noteFile->docType); + qWarning() << "error: unknown doc type" << int(m_file->getDocType()); } + setModified(false); } void VEdit::keyPressEvent(QKeyEvent *event) @@ -236,12 +244,12 @@ void VEdit::insertImage(const QString &name) void VEdit::initInitImages() { - m_initImages = VUtils::imagesFromMarkdownFile(QDir(noteFile->basePath).filePath(noteFile->fileName)); + m_initImages = VUtils::imagesFromMarkdownFile(m_file->retrivePath()); } void VEdit::clearUnusedImages() { - QVector images = VUtils::imagesFromMarkdownFile(QDir(noteFile->basePath).filePath(noteFile->fileName)); + QVector images = VUtils::imagesFromMarkdownFile(m_file->retrivePath()); if (!m_insertedImages.isEmpty()) { QVector imageNames(images.size()); @@ -249,7 +257,7 @@ void VEdit::clearUnusedImages() imageNames[i] = VUtils::fileNameFromPath(images[i]); } - QDir dir = QDir(QDir(noteFile->basePath).filePath("images")); + QDir dir = QDir(m_file->retriveImagePath()); for (int i = 0; i < m_insertedImages.size(); ++i) { QString name = m_insertedImages[i]; int j; diff --git a/src/vedit.h b/src/vedit.h index d726f504..fcba907f 100644 --- a/src/vedit.h +++ b/src/vedit.h @@ -3,9 +3,10 @@ #include #include +#include #include "vconstants.h" -#include "vnotefile.h" #include "vtoc.h" +#include "vfile.h" class HGMarkdownHighlighter; class VEditOperations; @@ -14,12 +15,12 @@ class VEdit : public QTextEdit { Q_OBJECT public: - VEdit(VNoteFile *noteFile, QWidget *parent = 0); + VEdit(VFile *p_file, QWidget *p_parent = 0); ~VEdit(); void beginEdit(); void endEdit(); - // Save buffer content to noteFile->content. + // Save buffer content to VFile. void saveFile(); inline void setModified(bool modified); @@ -48,9 +49,9 @@ private: void initInitImages(); void clearUnusedImages(); + QPointer m_file; bool isExpandTab; QString tabSpaces; - VNoteFile *noteFile; HGMarkdownHighlighter *mdHighlighter; VEditOperations *editOps; QVector headers; @@ -67,6 +68,9 @@ inline bool VEdit::isModified() const inline void VEdit::setModified(bool modified) { document()->setModified(modified); + if (m_file) { + m_file->setModified(modified); + } } #endif // VEDIT_H diff --git a/src/veditarea.cpp b/src/veditarea.cpp index 6a1eb9a9..c6715189 100644 --- a/src/veditarea.cpp +++ b/src/veditarea.cpp @@ -4,6 +4,7 @@ #include "vedittab.h" #include "vnote.h" #include "vconfigmanager.h" +#include "vfile.h" VEditArea::VEditArea(VNote *vnote, QWidget *parent) : QWidget(parent), vnote(vnote), curWindowIndex(0) @@ -79,25 +80,17 @@ void VEditArea::removeSplitWindow(VEditWindow *win) // A given file can be opened in multiple split windows. A given file could be // opened at most in one tab inside a window. -void VEditArea::openFile(QJsonObject fileJson) +void VEditArea::openFile(VFile *p_file, OpenFileMode p_mode) { - if (fileJson.isEmpty()) { + if (!p_file) { return; } - - QString notebook = fileJson["notebook"].toString(); - QString relativePath = fileJson["relative_path"].toString(); - int mode = OpenFileMode::Read; - if (fileJson.contains("mode")) { - mode = fileJson["mode"].toInt(); - } - - qDebug() << "open notebook" << notebook << "path" << relativePath << mode; + qDebug() << "VEditArea open" << p_file->getName() << (int)p_mode; // Find if it has been opened already int winIdx, tabIdx; bool setFocus = false; - auto tabs = findTabsByFile(notebook, relativePath); + auto tabs = findTabsByFile(p_file); if (!tabs.empty()) { // Current window first winIdx = tabs[0].first; @@ -115,19 +108,19 @@ void VEditArea::openFile(QJsonObject fileJson) // Open it in current window winIdx = curWindowIndex; - tabIdx = openFileInWindow(winIdx, notebook, relativePath, mode); + tabIdx = openFileInWindow(winIdx, p_file, p_mode); out: setCurrentTab(winIdx, tabIdx, setFocus); } -QVector > VEditArea::findTabsByFile(const QString ¬ebook, const QString &relativePath) +QVector > VEditArea::findTabsByFile(const VFile *p_file) { QVector > tabs; int nrWin = splitter->count(); for (int winIdx = 0; winIdx < nrWin; ++winIdx) { VEditWindow *win = getWindow(winIdx); - int tabIdx = win->findTabByFile(notebook, relativePath); + int tabIdx = win->findTabByFile(p_file); if (tabIdx != -1) { QPair match; match.first = winIdx; @@ -138,12 +131,11 @@ QVector > VEditArea::findTabsByFile(const QString ¬ebook, con return tabs; } -int VEditArea::openFileInWindow(int windowIndex, const QString ¬ebook, const QString &relativePath, - int mode) +int VEditArea::openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode) { Q_ASSERT(windowIndex < splitter->count()); VEditWindow *win = getWindow(windowIndex); - return win->openFile(notebook, relativePath, mode); + return win->openFile(p_file, p_mode); } void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus) @@ -178,21 +170,18 @@ void VEditArea::updateWindowStatus() win->requestUpdateCurHeader(); } -bool VEditArea::closeFile(QJsonObject fileJson) +bool VEditArea::closeFile(const VFile *p_file, bool p_forced) { - if (fileJson.isEmpty()) { + if (!p_file) { return true; } - QString notebook = fileJson["notebook"].toString(); - QString relativePath = fileJson["relative_path"].toString(); - bool isForced = fileJson["is_forced"].toBool(); - int nrWin = splitter->count(); bool ret = false; for (int i = 0; i < nrWin; ++i) { VEditWindow *win = getWindow(i); - ret = ret || win->closeFile(notebook, relativePath, isForced); + ret = ret || win->closeFile(p_file, p_forced); } + updateWindowStatus(); return ret; } @@ -206,6 +195,7 @@ bool VEditArea::closeAllFiles(bool p_forced) return false; } } + updateWindowStatus(); return true; } @@ -233,29 +223,6 @@ void VEditArea::saveAndReadFile() win->saveAndReadFile(); } -void VEditArea::handleDirectoryRenamed(const QString ¬ebook, const QString &oldRelativePath, - const QString &newRelativePath) -{ - int nrWin = splitter->count(); - for (int i = 0; i < nrWin; ++i) { - VEditWindow *win = getWindow(i); - win->handleDirectoryRenamed(notebook, oldRelativePath, newRelativePath); - } - updateWindowStatus(); -} - -void VEditArea::handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath, - const QString &p_destNotebook, const QString &p_destRelativePath) -{ - qDebug() << "fileRenamed" << p_srcNotebook << p_srcRelativePath << p_destNotebook << p_destRelativePath; - int nrWin = splitter->count(); - for (int i = 0; i < nrWin; ++i) { - VEditWindow *win = getWindow(i); - win->handleFileRenamed(p_srcNotebook, p_srcRelativePath, p_destNotebook, p_destRelativePath); - } - updateWindowStatus(); -} - void VEditArea::handleSplitWindowRequest(VEditWindow *curWindow) { if (!curWindow) { @@ -287,7 +254,6 @@ void VEditArea::handleRemoveSplitRequest(VEditWindow *curWindow) void VEditArea::mousePressEvent(QMouseEvent *event) { - return; qDebug() << "VEditArea press event" << event; QPoint pos = event->pos(); int nrWin = splitter->count(); @@ -335,7 +301,15 @@ void VEditArea::handleOutlineItemActivated(const VAnchor &anchor) getWindow(curWindowIndex)->scrollCurTab(anchor); } -bool VEditArea::isFileOpened(const QString ¬ebook, const QString &relativePath) +bool VEditArea::isFileOpened(const VFile *p_file) { - return !findTabsByFile(notebook, relativePath).isEmpty(); + return !findTabsByFile(p_file).isEmpty(); +} + +void VEditArea::handleFileUpdated(const VFile *p_file) +{ + int nrWin = splitter->count(); + for (int i = 0; i < nrWin; ++i) { + getWindow(i)->updateFileInfo(p_file); + } } diff --git a/src/veditarea.h b/src/veditarea.h index 206165ba..2e2875ff 100644 --- a/src/veditarea.h +++ b/src/veditarea.h @@ -15,18 +15,18 @@ #include "vtoc.h" class VNote; +class VFile; class VEditArea : public QWidget { Q_OBJECT public: explicit VEditArea(VNote *vnote, QWidget *parent = 0); - bool isFileOpened(const QString ¬ebook, const QString &relativePath); + bool isFileOpened(const VFile *p_file); bool closeAllFiles(bool p_forced); signals: - void curTabStatusChanged(const QString ¬ebook, const QString &relativePath, - bool editMode, bool modifiable, bool modified); + void curTabStatusChanged(const VFile *p_file, bool p_editMode); void outlineChanged(const VToc &toc); void curHeaderChanged(const VAnchor &anchor); @@ -34,17 +34,14 @@ protected: void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; public slots: - void openFile(QJsonObject fileJson); - bool closeFile(QJsonObject fileJson); + void openFile(VFile *p_file, OpenFileMode p_mode); + bool closeFile(const VFile *p_file, bool p_forced); void editFile(); void saveFile(); void readFile(); void saveAndReadFile(); void handleOutlineItemActivated(const VAnchor &anchor); - void handleDirectoryRenamed(const QString ¬ebook, - const QString &oldRelativePath, const QString &newRelativePath); - void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath, - const QString &p_destNotebook, const QString &p_destRelativePath); + void handleFileUpdated(const VFile *p_file); private slots: void handleSplitWindowRequest(VEditWindow *curWindow); @@ -55,9 +52,8 @@ private slots: private: void setupUI(); - QVector > findTabsByFile(const QString ¬ebook, const QString &relativePath); - int openFileInWindow(int windowIndex, const QString ¬ebook, const QString &relativePath, - int mode); + QVector > findTabsByFile(const VFile *p_file); + int openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode); void setCurrentTab(int windowIndex, int tabIndex, bool setFocus); void setCurrentWindow(int windowIndex, bool setFocus); inline VEditWindow *getWindow(int windowIndex) const; diff --git a/src/veditoperations.cpp b/src/veditoperations.cpp index 198cc78b..226fc1dc 100644 --- a/src/veditoperations.cpp +++ b/src/veditoperations.cpp @@ -3,15 +3,15 @@ #include "vedit.h" #include "veditoperations.h" -VEditOperations::VEditOperations(VEdit *editor, VNoteFile *noteFile) - : editor(editor), noteFile(noteFile) +VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file) + : m_editor(p_editor), m_file(p_file) { } void VEditOperations::insertTextAtCurPos(const QString &text) { - QTextCursor cursor(editor->document()); - cursor.setPosition(editor->textCursor().position()); + QTextCursor cursor(m_editor->document()); + cursor.setPosition(m_editor->textCursor().position()); cursor.insertText(text); } diff --git a/src/veditoperations.h b/src/veditoperations.h index 4dcabf5f..e0136a39 100644 --- a/src/veditoperations.h +++ b/src/veditoperations.h @@ -1,22 +1,24 @@ #ifndef VEDITOPERATIONS_H #define VEDITOPERATIONS_H -class VNoteFile; +#include +#include "vfile.h" + class VEdit; class QMimeData; class VEditOperations { public: - VEditOperations(VEdit *editor, VNoteFile *noteFile); + VEditOperations(VEdit *p_editor, VFile *p_file); virtual ~VEditOperations(); virtual bool insertImageFromMimeData(const QMimeData *source) = 0; virtual bool insertURLFromMimeData(const QMimeData *source) = 0; protected: void insertTextAtCurPos(const QString &text); - VEdit *editor; - VNoteFile *noteFile; + VEdit *m_editor; + QPointer m_file; }; #endif // VEDITOPERATIONS_H diff --git a/src/vedittab.cpp b/src/vedittab.cpp index ae6bbab2..c5af9ef1 100644 --- a/src/vedittab.cpp +++ b/src/vedittab.cpp @@ -18,37 +18,33 @@ extern VConfigManager vconfig; -VEditTab::VEditTab(const QString &path, bool modifiable, QWidget *parent) - : QStackedWidget(parent), mdConverterType(vconfig.getMdConverterType()) +VEditTab::VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent) + : QStackedWidget(p_parent), m_file(p_file), isEditMode(false), + mdConverterType(vconfig.getMdConverterType()) { - DocType docType = VUtils::isMarkdown(path) ? DocType::Markdown : DocType::Html; - QString basePath = QFileInfo(path).path(); - QString fileName = QFileInfo(path).fileName(); - qDebug() << "VEditTab basePath" << basePath << "file" << fileName; - QString fileText = VUtils::readFileFromDisk(path); - noteFile = new VNoteFile(basePath, fileName, fileText, - docType, modifiable); - - isEditMode = false; - + qDebug() << "ready to open" << p_file->getName(); + Q_ASSERT(!m_file->isOpened()); + m_file->open(); setupUI(); - - showFileReadMode(); - + if (p_mode == OpenFileMode::Edit) { + showFileEditMode(); + } else { + showFileReadMode(); + } connect(qApp, &QApplication::focusChanged, this, &VEditTab::handleFocusChanged); } VEditTab::~VEditTab() { - if (noteFile) { - delete noteFile; + if (m_file) { + m_file->close(); } } void VEditTab::setupUI() { - textEditor = new VEdit(noteFile); + textEditor = new VEdit(m_file); connect(textEditor, &VEdit::headersChanged, this, &VEditTab::updateTocFromHeaders); connect(textEditor, SIGNAL(curHeaderChanged(int)), @@ -57,7 +53,7 @@ void VEditTab::setupUI() this, &VEditTab::statusChanged); addWidget(textEditor); - switch (noteFile->docType) { + switch (m_file->getDocType()) { case DocType::Markdown: setupMarkdownPreview(); textBrowser = NULL; @@ -71,37 +67,38 @@ void VEditTab::setupUI() webPreviewer = NULL; break; default: - qWarning() << "error: unknown doc type" << int(noteFile->docType); + qWarning() << "error: unknown doc type" << int(m_file->getDocType()); } } void VEditTab::showFileReadMode() { + qDebug() << "read" << m_file->getName(); isEditMode = false; - switch (noteFile->docType) { + switch (m_file->getDocType()) { case DocType::Html: - textBrowser->setHtml(noteFile->content); + textBrowser->setHtml(m_file->getContent()); textBrowser->setFont(vconfig.getBaseEditFont()); textBrowser->setPalette(vconfig.getBaseEditPalette()); setCurrentWidget(textBrowser); break; case DocType::Markdown: if (mdConverterType == MarkdownConverterType::Marked) { - document.setText(noteFile->content); + document.setText(m_file->getContent()); } else { previewByConverter(); } setCurrentWidget(webPreviewer); break; default: - qWarning() << "error: unknown doc type" << int(noteFile->docType); + qWarning() << "error: unknown doc type" << int(m_file->getDocType()); } } void VEditTab::previewByConverter() { VMarkdownConverter mdConverter; - QString content = noteFile->content; + QString &content = m_file->getContent(); QString html = mdConverter.generateHtml(content, vconfig.getMarkdownExtensions()); QRegularExpression tocExp("

\\[TOC\\]<\\/p>", QRegularExpression::CaseInsensitiveOption); QString toc = mdConverter.generateToc(content, vconfig.getMarkdownExtensions()); @@ -119,15 +116,22 @@ void VEditTab::showFileEditMode() textEditor->setFocus(); } -bool VEditTab::requestClose() +bool VEditTab::closeFile(bool p_forced) { - readFile(); + if (p_forced && isEditMode) { + // Discard buffer content + textEditor->reloadFile(); + textEditor->endEdit(); + showFileReadMode(); + } else { + readFile(); + } return !isEditMode; } void VEditTab::editFile() { - if (isEditMode || !noteFile->modifiable) { + if (isEditMode) { return; } @@ -141,14 +145,12 @@ void VEditTab::readFile() } if (textEditor->isModified()) { - // Need to save the changes - QMessageBox msgBox(this); - msgBox.setText(QString("The note \"%1\" has been modified.").arg(noteFile->fileName)); - msgBox.setInformativeText("Do you want to save your changes?"); - msgBox.setIcon(QMessageBox::Information); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - int ret = msgBox.exec(); + // Prompt to save the changes + int ret = VUtils::showMessage(QMessageBox::Information, tr("Information"), + QString("Note %1 has been modified.").arg(m_file->getName()), + tr("Do you want to save your changes?"), + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, + QMessageBox::Save, this); switch (ret) { case QMessageBox::Save: saveFile(); @@ -171,33 +173,27 @@ void VEditTab::readFile() bool VEditTab::saveFile() { bool ret; - if (!isEditMode || !noteFile->modifiable || !textEditor->isModified()) { + if (!isEditMode || !textEditor->isModified()) { return true; } // Make sure the file already exists. Temporary deal with cases when user delete or move // a file. - QString filePath = QDir(noteFile->basePath).filePath(noteFile->fileName); + QString filePath = m_file->retrivePath(); if (!QFile(filePath).exists()) { - qWarning() << "error:" << filePath << "being written has been removed"; - QMessageBox msgBox(QMessageBox::Warning, tr("Fail to save to file"), - QString("%1 being written has been removed.").arg(filePath), - QMessageBox::Ok, this); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); + qWarning() << filePath << "being written has been removed"; + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), tr("Fail to save note"), + QString("%1 being written has been removed.").arg(filePath), + QMessageBox::Ok, QMessageBox::Ok, this); return false; } textEditor->saveFile(); - ret = VUtils::writeFileToDisk(filePath, noteFile->content); + ret = m_file->save(); if (!ret) { - QMessageBox msgBox(QMessageBox::Warning, tr("Fail to save to file"), - QString("Fail to write to disk when saving a note. Please try it again."), - QMessageBox::Ok, this); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.exec(); + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), tr("Fail to save note"), + QString("Fail to write to disk when saving a note. Please try it again."), + QMessageBox::Ok, QMessageBox::Ok, this); textEditor->setModified(true); - ret = false; } - ret = true; emit statusChanged(); return ret; } @@ -218,10 +214,10 @@ void VEditTab::setupMarkdownPreview() if (mdConverterType == MarkdownConverterType::Marked) { webPreviewer->setHtml(VNote::templateHtml, - QUrl::fromLocalFile(noteFile->basePath + QDir::separator())); + QUrl::fromLocalFile(m_file->retriveBasePath() + QDir::separator())); } else { webPreviewer->setHtml(VNote::preTemplateHtml + VNote::postTemplateHtml, - QUrl::fromLocalFile(noteFile->basePath + QDir::separator())); + QUrl::fromLocalFile(m_file->retriveBasePath() + QDir::separator())); } addWidget(webPreviewer); @@ -260,7 +256,7 @@ void VEditTab::updateTocFromHtml(const QString &tocHtml) } } - tableOfContent.filePath = QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)); + tableOfContent.filePath = m_file->retrivePath(); tableOfContent.valid = true; emit outlineChanged(tableOfContent); @@ -270,7 +266,7 @@ void VEditTab::updateTocFromHeaders(const QVector &headers) { tableOfContent.type = VHeaderType::LineNumber; tableOfContent.headers = headers; - tableOfContent.filePath = QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)); + tableOfContent.filePath = m_file->retrivePath(); tableOfContent.valid = true; emit outlineChanged(tableOfContent); @@ -367,8 +363,7 @@ void VEditTab::updateCurHeader(const QString &anchor) if (curHeader.anchor.mid(1) == anchor) { return; } - curHeader = VAnchor(QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)), - "#" + anchor, -1); + curHeader = VAnchor(m_file->retrivePath(), "#" + anchor, -1); if (!anchor.isEmpty()) { emit curHeaderChanged(curHeader); } @@ -379,16 +374,8 @@ void VEditTab::updateCurHeader(int lineNumber) if (curHeader.lineNumber == lineNumber) { return; } - curHeader = VAnchor(QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)), - "", lineNumber); + curHeader = VAnchor(m_file->retrivePath(), "", lineNumber); if (lineNumber > -1) { emit curHeaderChanged(curHeader); } } - -void VEditTab::updatePath(const QString &newPath) -{ - QFileInfo info(newPath); - noteFile->basePath = info.path(); - noteFile->fileName = info.fileName(); -} diff --git a/src/vedittab.h b/src/vedittab.h index 93059376..06eb7807 100644 --- a/src/vedittab.h +++ b/src/vedittab.h @@ -3,13 +3,14 @@ #include #include +#include #include "vconstants.h" -#include "vnotefile.h" #include "vdocument.h" #include "vmarkdownconverter.h" #include "vconfigmanager.h" #include "vedit.h" #include "vtoc.h" +#include "vfile.h" class QTextBrowser; class QWebEngineView; @@ -20,9 +21,9 @@ class VEditTab : public QStackedWidget { Q_OBJECT public: - VEditTab(const QString &path, bool modifiable, QWidget *parent = 0); + VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent = 0); ~VEditTab(); - bool requestClose(); + bool closeFile(bool p_forced); // Enter edit mode void editFile(); // Enter read mode @@ -36,8 +37,7 @@ public: void requestUpdateOutline(); void requestUpdateCurHeader(); void scrollToAnchor(const VAnchor& anchor); - void updatePath(const QString &newPath); - + inline VFile *getFile(); signals: void getFocused(); void outlineChanged(const VToc &toc); @@ -61,7 +61,7 @@ private: void parseTocUl(QXmlStreamReader &xml, QVector &headers, int level); void parseTocLi(QXmlStreamReader &xml, QVector &headers, int level); - VNoteFile *noteFile; + QPointer m_file; bool isEditMode; QTextBrowser *textBrowser; VEdit *textEditor; @@ -93,4 +93,9 @@ inline bool VEditTab::isChild(QObject *obj) return false; } +inline VFile *VEditTab::getFile() +{ + return m_file; +} + #endif // VEDITTAB_H diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp index 00eeaabf..f6dd07d8 100644 --- a/src/veditwindow.cpp +++ b/src/veditwindow.cpp @@ -5,6 +5,7 @@ #include "vnote.h" #include "vconfigmanager.h" #include "utils/vutils.h" +#include "vfile.h" extern VConfigManager vconfig; @@ -70,11 +71,9 @@ void VEditWindow::splitWindow() void VEditWindow::removeSplit() { // Close all the files one by one - // If user do not want to close a file, just stop removing - if (closeAllFiles(false)) { - Q_ASSERT(count() == 0); - emit requestRemoveSplit(this); - } + // If user do not want to close a file, just stop removing. + // Otherwise, closeAllFiles() will emit requestRemoveSplit. + closeAllFiles(false); } void VEditWindow::setRemoveSplitEnable(bool enabled) @@ -82,68 +81,67 @@ void VEditWindow::setRemoveSplitEnable(bool enabled) removeSplitAct->setVisible(enabled); } -void VEditWindow::openWelcomePage() +void VEditWindow::removeEditTab(int p_index) { - int idx = openFileInTab("", vconfig.getWelcomePagePath(), false); - setTabText(idx, generateTabText("Welcome to VNote", false)); - setTabToolTip(idx, "VNote"); + if (p_index > -1 && p_index < tabBar()->count()) { + VEditTab *editor = getTab(p_index); + removeTab(p_index); + delete editor; + if (tabBar()->count() == 0) { + emit requestRemoveSplit(this); + } + } } -int VEditWindow::insertTabWithData(int index, QWidget *page, - const QJsonObject &tabData) +int VEditWindow::insertEditTab(int p_index, VFile *p_file, QWidget *p_page) { - QString label = VUtils::fileNameFromPath(tabData["relative_path"].toString()); - int idx = insertTab(index, page, label); + int idx = insertTab(p_index, p_page, p_file->getName()); QTabBar *tabs = tabBar(); - tabs->setTabData(idx, tabData); - tabs->setTabToolTip(idx, generateTooltip(tabData)); + tabs->setTabToolTip(idx, generateTooltip(p_file)); noticeStatus(currentIndex()); return idx; } -int VEditWindow::appendTabWithData(QWidget *page, const QJsonObject &tabData) +int VEditWindow::appendEditTab(VFile *p_file, QWidget *p_page) { - return insertTabWithData(count(), page, tabData); + return insertEditTab(count(), p_file, p_page); } -int VEditWindow::openFile(const QString ¬ebook, const QString &relativePath, int mode) +int VEditWindow::openFile(VFile *p_file, OpenFileMode p_mode) { + qDebug() << "open" << p_file->getName(); // Find if it has been opened already - int idx = findTabByFile(notebook, relativePath); + int idx = findTabByFile(p_file); if (idx > -1) { goto out; } - idx = openFileInTab(notebook, relativePath, true); + idx = openFileInTab(p_file, p_mode); out: setCurrentIndex(idx); - if (mode == OpenFileMode::Edit) { - editFile(); - } focusWindow(); noticeStatus(idx); return idx; } -// Return true if we closed the file -bool VEditWindow::closeFile(const QString ¬ebook, const QString &relativePath, bool isForced) +// Return true if we closed the file actually +bool VEditWindow::closeFile(const VFile *p_file, bool p_forced) { // Find if it has been opened already - int idx = findTabByFile(notebook, relativePath); + int idx = findTabByFile(p_file); if (idx == -1) { return false; } VEditTab *editor = getTab(idx); Q_ASSERT(editor); - bool ok = true; - if (!isForced) { + if (!p_forced) { setCurrentIndex(idx); noticeStatus(idx); - ok = editor->requestClose(); } + // Even p_forced is true we need to delete unused images. + bool ok = editor->closeFile(p_forced); if (ok) { - removeTab(idx); - delete editor; + removeEditTab(idx); } updateTabListMenu(); return ok; @@ -155,15 +153,15 @@ bool VEditWindow::closeAllFiles(bool p_forced) bool ret = true; for (int i = 0; i < nrTab; ++i) { VEditTab *editor = getTab(0); - bool ok = true; + if (!p_forced) { setCurrentIndex(0); noticeStatus(0); - ok = editor->requestClose(); } + // Even p_forced is true we need to delete unused images. + bool ok = editor->closeFile(p_forced); if (ok) { - removeTab(0); - delete editor; + removeEditTab(0); } else { ret = false; break; @@ -176,11 +174,9 @@ bool VEditWindow::closeAllFiles(bool p_forced) return ret; } -int VEditWindow::openFileInTab(const QString ¬ebook, const QString &relativePath, - bool modifiable) +int VEditWindow::openFileInTab(VFile *p_file, OpenFileMode p_mode) { - QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(notebook)).filePath(relativePath)); - VEditTab *editor = new VEditTab(path, modifiable); + VEditTab *editor = new VEditTab(p_file, p_mode); connect(editor, &VEditTab::getFocused, this, &VEditWindow::getFocused); connect(editor, &VEditTab::outlineChanged, @@ -190,23 +186,16 @@ int VEditWindow::openFileInTab(const QString ¬ebook, const QString &relativeP connect(editor, &VEditTab::statusChanged, this, &VEditWindow::handleTabStatusChanged); - QJsonObject tabJson; - tabJson["notebook"] = notebook; - tabJson["relative_path"] = relativePath; - tabJson["modifiable"] = modifiable; - int idx = appendTabWithData(editor, tabJson); + int idx = appendEditTab(p_file, editor); updateTabListMenu(); return idx; } -int VEditWindow::findTabByFile(const QString ¬ebook, const QString &relativePath) const +int VEditWindow::findTabByFile(const VFile *p_file) const { - QTabBar *tabs = tabBar(); - int nrTabs = tabs->count(); - + int nrTabs = tabBar()->count(); for (int i = 0; i < nrTabs; ++i) { - QJsonObject tabJson = tabs->tabData(i).toJsonObject(); - if (tabJson["notebook"] == notebook && tabJson["relative_path"] == relativePath) { + if (getTab(i)->getFile() == p_file) { return i; } } @@ -217,10 +206,9 @@ bool VEditWindow::handleTabCloseRequest(int index) { VEditTab *editor = getTab(index); Q_ASSERT(editor); - bool ok = editor->requestClose(); + bool ok = editor->closeFile(false); if (ok) { - removeTab(index); - delete editor; + removeEditTab(index); } updateTabListMenu(); noticeStatus(currentIndex()); @@ -260,81 +248,29 @@ void VEditWindow::saveFile() editor->saveFile(); } -void VEditWindow::handleDirectoryRenamed(const QString ¬ebook, const QString &oldRelativePath, - const QString &newRelativePath) +void VEditWindow::noticeTabStatus(int p_index) { - QTabBar *tabs = tabBar(); - int nrTabs = tabs->count(); - for (int i = 0; i < nrTabs; ++i) { - QJsonObject tabJson = tabs->tabData(i).toJsonObject(); - if (tabJson["notebook"].toString() == notebook) { - QString relativePath = tabJson["relative_path"].toString(); - if (relativePath.startsWith(oldRelativePath)) { - relativePath.replace(0, oldRelativePath.size(), newRelativePath); - tabJson["relative_path"] = relativePath; - tabs->setTabData(i, tabJson); - tabs->setTabToolTip(i, generateTooltip(tabJson)); - QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(notebook)).filePath(relativePath)); - getTab(i)->updatePath(path); - } - } - } - updateTabListMenu(); -} - -void VEditWindow::handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath, - const QString &p_destNotebook, const QString &p_destRelativePath) -{ - QTabBar *tabs = tabBar(); - int nrTabs = tabs->count(); - for (int i = 0; i < nrTabs; ++i) { - QJsonObject tabJson = tabs->tabData(i).toJsonObject(); - if (tabJson["notebook"].toString() == p_srcNotebook) { - QString relativePath = tabJson["relative_path"].toString(); - if (relativePath == p_srcRelativePath) { - VEditTab *tab = getTab(i); - tabJson["notebook"] = p_destNotebook; - tabJson["relative_path"] = p_destRelativePath; - tabs->setTabData(i, tabJson); - tabs->setTabToolTip(i, generateTooltip(tabJson)); - tabs->setTabText(i, generateTabText(VUtils::fileNameFromPath(p_destRelativePath), tab->isModified())); - QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(p_destNotebook)).filePath(p_destRelativePath)); - tab->updatePath(path); - } - } - } - updateTabListMenu(); -} - -void VEditWindow::noticeTabStatus(int index) -{ - if (index == -1) { - emit tabStatusChanged("", "", false, false, false); + if (p_index == -1) { + emit tabStatusChanged(NULL, false); return; } - QJsonObject tabJson = tabBar()->tabData(index).toJsonObject(); - Q_ASSERT(!tabJson.isEmpty()); - - QString notebook = tabJson["notebook"].toString(); - QString relativePath = tabJson["relative_path"].toString(); - VEditTab *editor = getTab(index); + VEditTab *editor = getTab(p_index); + const VFile *file = editor->getFile(); bool editMode = editor->getIsEditMode(); - bool modifiable = tabJson["modifiable"].toBool(); // Update tab text - tabBar()->setTabText(index, generateTabText(VUtils::fileNameFromPath(relativePath), - editor->isModified())); - - emit tabStatusChanged(notebook, relativePath, - editMode, modifiable, editor->isModified()); + tabBar()->setTabText(p_index, generateTabText(file->getName(), file->isModified())); + emit tabStatusChanged(file, editMode); } +// Be requested to report current status void VEditWindow::requestUpdateTabStatus() { noticeTabStatus(currentIndex()); } +// Be requested to report current outline void VEditWindow::requestUpdateOutline() { int idx = currentIndex(); @@ -345,6 +281,7 @@ void VEditWindow::requestUpdateOutline() getTab(idx)->requestUpdateOutline(); } +// Be requested to report current header void VEditWindow::requestUpdateCurHeader() { int idx = currentIndex(); @@ -355,6 +292,7 @@ void VEditWindow::requestUpdateCurHeader() getTab(idx)->requestUpdateCurHeader(); } +// Focus this windows. Try to focus current tab. void VEditWindow::focusWindow() { int idx = currentIndex(); @@ -392,9 +330,8 @@ void VEditWindow::tabListJump(QAction *action) return; } - QJsonObject tabJson = action->data().toJsonObject(); - int idx = findTabByFile(tabJson["notebook"].toString(), - tabJson["relative_path"].toString()); + QPointer file = action->data().value>(); + int idx = findTabByFile(file); Q_ASSERT(idx >= 0); setCurrentIndex(idx); noticeStatus(idx); @@ -416,54 +353,57 @@ void VEditWindow::updateTabListMenu() QTabBar *tabbar = tabBar(); int nrTab = tabbar->count(); for (int i = 0; i < nrTab; ++i) { - QAction *action = new QAction(tabbar->tabText(i), tabListAct); - action->setStatusTip(generateTooltip(tabbar->tabData(i).toJsonObject())); - action->setData(tabbar->tabData(i)); + VEditTab *editor = getTab(i); + QPointer file = editor->getFile(); + QAction *action = new QAction(file->getName(), tabListAct); + action->setStatusTip(generateTooltip(file)); + action->setData(QVariant::fromValue(file)); menu->addAction(action); } } -void VEditWindow::handleOutlineChanged(const VToc &toc) +void VEditWindow::handleOutlineChanged(const VToc &p_toc) { // Only propagate it if it is current tab int idx = currentIndex(); - QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject(); - Q_ASSERT(!tabJson.isEmpty()); - QString path = vnote->getNotebookPath(tabJson["notebook"].toString()); - path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString())); - - if (toc.filePath == path) { - emit outlineChanged(toc); + if (idx == -1) { + emit outlineChanged(VToc()); + return; + } + const VFile *file = getTab(idx)->getFile(); + if (p_toc.filePath == file->retrivePath()) { + emit outlineChanged(p_toc); } } -void VEditWindow::handleCurHeaderChanged(const VAnchor &anchor) +void VEditWindow::handleCurHeaderChanged(const VAnchor &p_anchor) { // Only propagate it if it is current tab int idx = currentIndex(); - QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject(); - Q_ASSERT(!tabJson.isEmpty()); - QString path = vnote->getNotebookPath(tabJson["notebook"].toString()); - path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString())); - - if (anchor.filePath == path) { - emit curHeaderChanged(anchor); + if (idx == -1) { + emit curHeaderChanged(VAnchor()); + return; + } + const VFile *file = getTab(idx)->getFile(); + if (p_anchor.filePath == file->retrivePath()) { + emit curHeaderChanged(p_anchor); } } -void VEditWindow::scrollCurTab(const VAnchor &anchor) +void VEditWindow::scrollCurTab(const VAnchor &p_anchor) { int idx = currentIndex(); - QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject(); - Q_ASSERT(!tabJson.isEmpty()); - QString path = vnote->getNotebookPath(tabJson["notebook"].toString()); - path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString())); - - if (path == anchor.filePath) { - getTab(idx)->scrollToAnchor(anchor); + if (idx == -1) { + emit curHeaderChanged(VAnchor()); + return; + } + const VFile *file = getTab(idx)->getFile(); + if (file->retrivePath() == p_anchor.filePath) { + getTab(idx)->scrollToAnchor(p_anchor); } } +// Update tab status, outline and current header. void VEditWindow::noticeStatus(int index) { noticeTabStatus(index); @@ -482,3 +422,15 @@ void VEditWindow::handleTabStatusChanged() { noticeTabStatus(currentIndex()); } + +void VEditWindow::updateFileInfo(const VFile *p_file) +{ + if (!p_file) { + return; + } + int idx = findTabByFile(p_file); + if (idx > -1) { + noticeStatus(idx); + updateTabListMenu(); + } +} diff --git a/src/veditwindow.h b/src/veditwindow.h index c3a6494b..39e779f5 100644 --- a/src/veditwindow.h +++ b/src/veditwindow.h @@ -13,16 +13,16 @@ class VNote; class QPushButton; class QActionGroup; +class VFile; class VEditWindow : public QTabWidget { Q_OBJECT public: explicit VEditWindow(VNote *vnote, QWidget *parent = 0); - int findTabByFile(const QString ¬ebook, const QString &relativePath) const; - int openFile(const QString ¬ebook, const QString &relativePath, - int mode); - bool closeFile(const QString ¬ebook, const QString &relativePath, bool isForced); + int findTabByFile(const VFile *p_file) const; + int openFile(VFile *p_file, OpenFileMode p_mode); + bool closeFile(const VFile *p_file, bool p_forced); void editFile(); void saveFile(); void readFile(); @@ -34,18 +34,14 @@ public: void requestUpdateCurHeader(); // Focus to current tab's editor void focusWindow(); - void scrollCurTab(const VAnchor &anchor); - void handleDirectoryRenamed(const QString ¬ebook, - const QString &oldRelativePath, const QString &newRelativePath); - void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath, - const QString &p_destNotebook, const QString &p_destRelativePath); + void scrollCurTab(const VAnchor &p_anchor); + void updateFileInfo(const VFile *p_file); protected: void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; signals: - void tabStatusChanged(const QString ¬ebook, const QString &relativePath, - bool editMode, bool modifiable, bool modified); + void tabStatusChanged(const VFile *p_file, bool p_editMode); void requestSplitWindow(VEditWindow *curWindow); void requestRemoveSplit(VEditWindow *curWindow); // This widget or its children get the focus @@ -60,21 +56,21 @@ private slots: void handleTabbarClicked(int index); void contextMenuRequested(QPoint pos); void tabListJump(QAction *action); - void handleOutlineChanged(const VToc &toc); - void handleCurHeaderChanged(const VAnchor &anchor); + void handleOutlineChanged(const VToc &p_toc); + void handleCurHeaderChanged(const VAnchor &p_anchor); void handleTabStatusChanged(); private: void setupCornerWidget(); - void openWelcomePage(); - int insertTabWithData(int index, QWidget *page, const QJsonObject &tabData); - int appendTabWithData(QWidget *page, const QJsonObject &tabData); - int openFileInTab(const QString ¬ebook, const QString &relativePath, bool modifiable); + void removeEditTab(int p_index); + int insertEditTab(int p_index, VFile *p_file, QWidget *p_page); + int appendEditTab(VFile *p_file, QWidget *p_page); + int openFileInTab(VFile *p_file, OpenFileMode p_mode); inline VEditTab *getTab(int tabIndex) const; - void noticeTabStatus(int index); + void noticeTabStatus(int p_index); void updateTabListMenu(); void noticeStatus(int index); - inline QString generateTooltip(const QJsonObject &tabData) const; + inline QString generateTooltip(const VFile *p_file) const; inline QString generateTabText(const QString &p_name, bool p_modified) const; VNote *vnote; @@ -94,10 +90,13 @@ inline VEditTab* VEditWindow::getTab(int tabIndex) const return dynamic_cast(widget(tabIndex)); } -inline QString VEditWindow::generateTooltip(const QJsonObject &tabData) const +inline QString VEditWindow::generateTooltip(const VFile *p_file) const { - // [Notebook]relativePath - return QString("[%1] %2").arg(tabData["notebook"].toString()).arg(tabData["relative_path"].toString()); + if (!p_file) { + return ""; + } + // [Notebook]path + return QString("[%1] %2").arg(p_file->retriveNotebook()).arg(p_file->retrivePath()); } inline QString VEditWindow::generateTabText(const QString &p_name, bool p_modified) const diff --git a/src/vfile.cpp b/src/vfile.cpp new file mode 100644 index 00000000..cb3c1c6b --- /dev/null +++ b/src/vfile.cpp @@ -0,0 +1,114 @@ +#include "vfile.h" + +#include +#include +#include +#include "utils/vutils.h" + +VFile::VFile(const QString &p_name, QObject *p_parent) + : QObject(p_parent), m_name(p_name), m_opened(false), m_modified(false), + m_docType(VUtils::isMarkdown(p_name) ? DocType::Markdown : DocType::Html) +{ +} + +bool VFile::open() +{ + Q_ASSERT(!m_name.isEmpty()); + if (m_opened) { + return true; + } + Q_ASSERT(m_content.isEmpty()); + Q_ASSERT(m_docType == (VUtils::isMarkdown(m_name) ? DocType::Markdown : DocType::Html)); + QString path = retrivePath(); + qDebug() << "path" << path; + m_content = VUtils::readFileFromDisk(path); + m_modified = false; + m_opened = true; + qDebug() << "file" << m_name << "opened"; + return true; +} + +void VFile::close() +{ + if (!m_opened) { + return; + } + m_content.clear(); + m_opened = false; +} + +void VFile::deleteDiskFile() +{ + Q_ASSERT(parent()); + + // Delete local images in ./images if it is Markdown + if (m_docType == DocType::Markdown) { + deleteLocalImages(); + } + + // Delete the file + QString filePath = retrivePath(); + QFile file(filePath); + if (file.remove()) { + qDebug() << "deleted" << filePath; + } else { + qWarning() << "failed to delete" << filePath; + } +} + +bool VFile::save() +{ + Q_ASSERT(m_opened); + bool ret = VUtils::writeFileToDisk(retrivePath(), m_content); + return ret; +} + +void VFile::convert(DocType p_curType, DocType p_targetType) +{ + Q_ASSERT(!m_opened); + m_docType = p_targetType; + if (p_curType == p_targetType) { + return; + } + QString path = retrivePath(); + QString fileText = VUtils::readFileFromDisk(path); + QTextEdit editor; + if (p_curType == DocType::Markdown) { + editor.setPlainText(fileText); + fileText = editor.toHtml(); + } else { + editor.setHtml(fileText); + fileText = editor.toPlainText(); + } + VUtils::writeFileToDisk(path, fileText); + qDebug() << getName() << "converted" << (int)p_curType << (int)p_targetType; +} + +void VFile::setModified(bool p_modified) +{ + m_modified = p_modified; +} + +void VFile::deleteLocalImages() +{ + Q_ASSERT(m_docType == DocType::Markdown); + QString filePath = retrivePath(); + QVector images = VUtils::imagesFromMarkdownFile(filePath); + int deleted = 0; + for (int i = 0; i < images.size(); ++i) { + QFile file(images[i]); + if (file.remove()) { + ++deleted; + } + } + qDebug() << "delete" << deleted << "images for" << filePath; +} + +void VFile::setName(const QString &p_name) +{ + m_name = p_name; + DocType newType = VUtils::isMarkdown(p_name) ? DocType::Markdown : DocType::Html; + if (newType != m_docType) { + qWarning() << "setName() change the DocType. A convertion should be followed"; + } +} diff --git a/src/vfile.h b/src/vfile.h new file mode 100644 index 00000000..52213b2d --- /dev/null +++ b/src/vfile.h @@ -0,0 +1,125 @@ +#ifndef VFILE_H +#define VFILE_H + +#include +#include +#include +#include "vdirectory.h" +#include "vconstants.h" + +class VFile : public QObject +{ + Q_OBJECT +public: + explicit VFile(const QString &p_name, QObject *p_parent); + bool open(); + void close(); + bool save(); + // Convert current file type. + void convert(DocType p_curType, DocType p_targetType); + + inline const QString &getName() const; + void setName(const QString &p_name); + inline VDirectory *getDirectory(); + inline const VDirectory *getDirectory() const; + inline DocType getDocType() const; + inline QString &getContent(); + inline void setContent(const QString &p_content); + inline QString retriveNotebook() const; + inline QString retrivePath() const; + inline QString retriveRelativePath() const; + inline QString retriveBasePath() const; + inline QString retriveImagePath() const; + inline bool isModified() const; + inline bool isOpened() const; + +signals: + +public slots: + void setModified(bool p_modified); + +private: + // Delete the file and corresponding images + void deleteDiskFile(); + // Delete local images in ./images of DocType::Markdown + void deleteLocalImages(); + + QString m_name; + bool m_opened; + // File has been modified in editor + bool m_modified; + DocType m_docType; + QString m_content; + friend class VDirectory; +}; + +inline const QString &VFile::getName() const +{ + return m_name; +} + +inline VDirectory *VFile::getDirectory() +{ + Q_ASSERT(parent()); + return (VDirectory *)parent(); +} + +inline const VDirectory *VFile::getDirectory() const +{ + Q_ASSERT(parent()); + return (const VDirectory *)parent(); +} + +inline DocType VFile::getDocType() const +{ + return m_docType; +} + +inline QString &VFile::getContent() +{ + return m_content; +} + +inline QString VFile::retriveNotebook() const +{ + return getDirectory()->retriveNotebook(); +} + +inline QString VFile::retrivePath() const +{ + QString dirPath = getDirectory()->retrivePath(); + return QDir(dirPath).filePath(m_name); +} + +inline QString VFile::retriveRelativePath() const +{ + QString dirRelativePath = getDirectory()->retriveRelativePath(); + return QDir(dirRelativePath).filePath(m_name); +} + +inline QString VFile::retriveBasePath() const +{ + return getDirectory()->retrivePath(); +} + +inline QString VFile::retriveImagePath() const +{ + return QDir(retriveBasePath()).filePath("images"); +} + +inline void VFile::setContent(const QString &p_content) +{ + m_content = p_content; +} + +inline bool VFile::isModified() const +{ + return m_modified; +} + +inline bool VFile::isOpened() const +{ + return m_opened; +} + +#endif // VFILE_H diff --git a/src/vfilelist.cpp b/src/vfilelist.cpp index a93761c1..ce359428 100644 --- a/src/vfilelist.cpp +++ b/src/vfilelist.cpp @@ -7,9 +7,10 @@ #include "vnote.h" #include "veditarea.h" #include "utils/vutils.h" +#include "vfile.h" -VFileList::VFileList(VNote *vnote, QWidget *parent) - : QWidget(parent), vnote(vnote) +VFileList::VFileList(QWidget *parent) + : QWidget(parent) { setupUI(); initActions(); @@ -43,14 +44,14 @@ void VFileList::initActions() deleteFileAct = new QAction(QIcon(":/resources/icons/delete_note.svg"), tr("&Delete"), this); deleteFileAct->setStatusTip(tr("Delete selected note")); - connect(deleteFileAct, &QAction::triggered, - this, &VFileList::deleteCurFile); + connect(deleteFileAct, SIGNAL(triggered(bool)), + this, SLOT(deleteFile())); fileInfoAct = new QAction(QIcon(":/resources/icons/note_info.svg"), tr("&Info"), this); fileInfoAct->setStatusTip(tr("View and edit current note's information")); - connect(fileInfoAct, &QAction::triggered, - this, &VFileList::curFileInfo); + connect(fileInfoAct, SIGNAL(triggered(bool)), + this, SLOT(fileInfo())); copyAct = new QAction(QIcon(":/resources/icons/copy.svg"), tr("&Copy"), this); @@ -71,85 +72,49 @@ void VFileList::initActions() this, &VFileList::pasteFilesInCurDir); } -void VFileList::setDirectory(QJsonObject dirJson) +void VFileList::setDirectory(VDirectory *p_directory) { - fileList->clear(); - if (dirJson.isEmpty()) { - clearDirectoryInfo(); - emit directoryChanged("", ""); + if (m_directory == p_directory) { + return; + } + m_directory = p_directory; + if (!m_directory) { + fileList->clear(); return; } - notebook = dirJson["notebook"].toString(); - relativePath = dirJson["relative_path"].toString(); - rootPath = ""; - const QVector ¬ebooks = vnote->getNotebooks(); - for (int i = 0; i < notebooks.size(); ++i) { - if (notebooks[i]->getName() == notebook) { - rootPath = notebooks[i]->getPath(); - break; - } - } - Q_ASSERT(!rootPath.isEmpty()); - + qDebug() << "filelist set directory" << m_directory->getName(); updateFileList(); - - emit directoryChanged(notebook, relativePath); -} - -void VFileList::clearDirectoryInfo() -{ - notebook = relativePath = rootPath = ""; } void VFileList::updateFileList() { - QString path = QDir(rootPath).filePath(relativePath); - fileList->clear(); - if (!QDir(path).exists()) { - qDebug() << "invalid notebook directory:" << path; - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory."), - QMessageBox::Ok, this); - msgBox.setInformativeText(QString("Notebook directory \"%1\" either does not exist or is not valid.") - .arg(path)); - msgBox.exec(); + if (!m_directory->open()) { 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."), - QMessageBox::Ok, this); - 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); + const QVector &files = m_directory->getFiles(); + for (int i = 0; i < files.size(); ++i) { + VFile *file = files[i]; + insertFileListItem(file); } } -void VFileList::curFileInfo() +void VFileList::fileInfo() { QListWidgetItem *curItem = fileList->currentItem(); - QJsonObject curItemJson = curItem->data(Qt::UserRole).toJsonObject(); - Q_ASSERT(!curItemJson.isEmpty()); - QString curItemName = curItemJson["name"].toString(); - fileInfo(notebook, QDir(relativePath).filePath(curItemName)); + Q_ASSERT(curItem); + fileInfo(getVFile(curItem)); } -void VFileList::fileInfo(const QString &p_notebook, const QString &p_relativePath) +void VFileList::fileInfo(VFile *p_file) { - qDebug() << "fileInfo" << p_notebook << p_relativePath; + if (!p_file) { + return; + } + VDirectory *dir = p_file->getDirectory(); QString info; - QString defaultName = VUtils::fileNameFromPath(p_relativePath); + QString defaultName = p_file->getName(); QString curName = defaultName; do { VFileInfoDialog dialog(tr("Note Information"), info, defaultName, this); @@ -158,23 +123,22 @@ void VFileList::fileInfo(const QString &p_notebook, const QString &p_relativePat if (name == curName) { return; } - if (isConflictNameWithExisting(name)) { - info = "Name already exists.\nPlease choose another name:"; + if (dir->findFile(name)) { + info = "Name already exists.\nPlease choose another name."; defaultName = name; continue; } - copyFile(p_notebook, p_relativePath, p_notebook, - QDir(VUtils::basePathFromPath(p_relativePath)).filePath(name), true); + copyFile(dir, name, p_file, true); } break; } while (true); } -QListWidgetItem* VFileList::insertFileListItem(QJsonObject fileJson, bool atFront) +QListWidgetItem* VFileList::insertFileListItem(VFile *file, bool atFront) { - Q_ASSERT(!fileJson.isEmpty()); - QListWidgetItem *item = new QListWidgetItem(fileJson["name"].toString()); - item->setData(Qt::UserRole, fileJson); + Q_ASSERT(file); + QListWidgetItem *item = new QListWidgetItem(file->getName()); + item->setData(Qt::UserRole, QVariant::fromValue(file)); if (atFront) { fileList->insertItem(0, item); @@ -183,7 +147,7 @@ QListWidgetItem* VFileList::insertFileListItem(QJsonObject fileJson, bool atFron } // Qt seems not to update the QListWidget correctly. Manually force it to repaint. fileList->update(); - qDebug() << "add new list item:" << fileJson["name"].toString(); + qDebug() << "VFileList adds" << file->getName(); return item; } @@ -198,65 +162,91 @@ void VFileList::removeFileListItem(QListWidgetItem *item) void VFileList::newFile() { + if (!m_directory) { + return; + } + QString info = QString("Create a new note under %1.").arg(m_directory->getName()); QString text("&Note name:"); QString defaultText("new_note"); do { - VNewFileDialog dialog(QString("Create a new note under %1").arg(VUtils::directoryNameFromPath(relativePath)), - text, defaultText, this); + VNewFileDialog dialog(QString("Create new note"), info, text, defaultText, this); if (dialog.exec() == QDialog::Accepted) { QString name = dialog.getNameInput(); - if (isConflictNameWithExisting(name)) { - text = "Name already exists.\nPlease choose another name:"; + if (m_directory->findFile(name)) { + info = "Name already exists.\nPlease choose another name."; defaultText = name; continue; } - QListWidgetItem *newItem = createFileAndUpdateList(name); - if (newItem) { - fileList->setCurrentItem(newItem); - // Qt seems not to update the QListWidget correctly. Manually force it to repaint. - fileList->update(); - - // Open this file in edit mode - QJsonObject itemJson = newItem->data(Qt::UserRole).toJsonObject(); - Q_ASSERT(!itemJson.isEmpty()); - itemJson["notebook"] = notebook; - itemJson["relative_path"] = QDir::cleanPath(QDir(relativePath).filePath(name)); - itemJson["mode"] = OpenFileMode::Edit; - emit fileCreated(itemJson); + VFile *file = m_directory->createFile(name); + if (!file) { + VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Failed to create file %1.").arg(name), "", + QMessageBox::Ok, QMessageBox::Ok, this); + return; } + QVector items = updateFileListAdded(); + Q_ASSERT(items.size() == 1); + fileList->setCurrentItem(items[0]); + // Qt seems not to update the QListWidget correctly. Manually force it to repaint. + fileList->update(); + + // Open it in edit mode + emit fileCreated(file, OpenFileMode::Edit); } break; } while (true); } -void VFileList::deleteCurFile() +QVector VFileList::updateFileListAdded() +{ + QVector ret; + const QVector &files = m_directory->getFiles(); + for (int i = 0; i < files.size(); ++i) { + VFile *file = files[i]; + if (i >= fileList->count()) { + QListWidgetItem *item = insertFileListItem(file, false); + ret.append(item); + } else { + VFile *itemFile = getVFile(fileList->item(i)); + if (itemFile != file) { + QListWidgetItem *item = insertFileListItem(file, false); + ret.append(item); + } + } + } + qDebug() << ret.size() << "items added"; + return ret; +} + +// Delete the file related to current item +void VFileList::deleteFile() { QListWidgetItem *curItem = fileList->currentItem(); Q_ASSERT(curItem); - QJsonObject curItemJson = curItem->data(Qt::UserRole).toJsonObject(); - QString curItemName = curItemJson["name"].toString(); - deleteFile(notebook, QDir(relativePath).filePath(curItemName)); + deleteFile(getVFile(curItem)); } -// @p_relativePath contains the file name -void VFileList::deleteFile(const QString &p_notebook, const QString &p_relativePath) +// @p_file may or may not be listed in VFileList +void VFileList::deleteFile(VFile *p_file) { - QString fileName = VUtils::fileNameFromPath(p_relativePath); - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), - QString("Are you sure you want to delete note \"%1\"?") - .arg(fileName), QMessageBox::Ok | QMessageBox::Cancel, - this); - msgBox.setInformativeText(tr("This may be not recoverable.")); - msgBox.setDefaultButton(QMessageBox::Ok); - if (msgBox.exec() == QMessageBox::Ok) { - // First close this file forcely - QJsonObject curItemJson; - curItemJson["notebook"] = p_notebook; - curItemJson["relative_path"] = QDir::cleanPath(p_relativePath); - curItemJson["is_forced"] = true; - emit fileDeleted(curItemJson); + if (!p_file) { + return; + } + VDirectory *dir = p_file->getDirectory(); + QString fileName = p_file->getName(); + int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("Are you sure to delete note %1?").arg(fileName), tr("This may be unrecoverable!"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok, this); + if (ret == QMessageBox::Ok) { + editArea->closeFile(p_file, true); - deleteFileAndUpdateList(p_notebook, p_relativePath); + // Remove the item before deleting it totally, or p_file will be invalid. + QListWidgetItem *item = findItem(p_file); + if (item) { + removeFileListItem(item); + } + + dir->deleteFile(p_file); } } @@ -265,7 +255,7 @@ void VFileList::contextMenuRequested(QPoint pos) QListWidgetItem *item = fileList->itemAt(pos); QMenu menu(this); - if (notebook.isEmpty()) { + if (!m_directory) { return; } menu.addAction(newFileAct); @@ -290,187 +280,56 @@ void VFileList::contextMenuRequested(QPoint pos) menu.exec(fileList->mapToGlobal(pos)); } -bool VFileList::isConflictNameWithExisting(const QString &name) +QListWidgetItem* VFileList::findItem(const VFile *p_file) { - int nrChild = fileList->count(); - for (int i = 0; i < nrChild; ++i) { - QListWidgetItem *item = fileList->item(i); - QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject(); - Q_ASSERT(!itemJson.isEmpty()); - if (itemJson["name"].toString() == name) { - return true; - } - } - return false; -} - -QListWidgetItem* VFileList::findItem(const QString &p_notebook, const QString &p_relativePath) -{ - if (p_notebook != notebook || VUtils::basePathFromPath(p_relativePath) != QDir::cleanPath(relativePath)) { + if (!p_file || p_file->getDirectory() != m_directory) { return NULL; } - QString name = VUtils::fileNameFromPath(p_relativePath); + int nrChild = fileList->count(); for (int i = 0; i < nrChild; ++i) { QListWidgetItem *item = fileList->item(i); - QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject(); - Q_ASSERT(!itemJson.isEmpty()); - if (itemJson["name"].toString() == name) { + if (p_file == getVFile(item)) { return item; } } return NULL; } -QListWidgetItem* VFileList::createFileAndUpdateList(const QString &name) -{ - 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), QMessageBox::Ok, this); - 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; - - if (!addFileInConfig(filePath, 0)) { - file.remove(); - return NULL; - } - - return insertFileListItem(readFileInConfig(filePath), true); -} - -void VFileList::deleteFileAndUpdateList(const QString &p_notebook, - const QString &p_relativePath) -{ - QString filePath = QDir(vnote->getNotebookPath(p_notebook)).filePath(p_relativePath); - - if (!removeFileInConfig(filePath)) { - return; - } - - // Delete local images in ./images - deleteLocalImages(filePath); - - // Delete the file - QFile file(filePath); - if (!file.remove()) { - qWarning() << "error: fail to delete" << filePath; - } else { - qDebug() << "delete" << filePath; - } - - QListWidgetItem *item = findItem(p_notebook, p_relativePath); - if (item) { - removeFileListItem(item); - } -} - void VFileList::handleItemClicked(QListWidgetItem *currentItem) { if (!currentItem) { - emit fileClicked(QJsonObject()); + emit fileClicked(NULL); return; } // Qt seems not to update the QListWidget correctly. Manually force it to repaint. fileList->update(); - QJsonObject itemJson = currentItem->data(Qt::UserRole).toJsonObject(); - Q_ASSERT(!itemJson.isEmpty()); - itemJson["notebook"] = notebook; - itemJson["relative_path"] = QDir::cleanPath(QDir(relativePath).filePath(itemJson["name"].toString())); - itemJson["mode"] = OpenFileMode::Read; - emit fileClicked(itemJson); + emit fileClicked(getVFile(currentItem), OpenFileMode::Read); } -bool VFileList::importFile(const QString &name) +bool VFileList::importFile(const QString &p_srcFilePath) { - if (name.isEmpty()) { + if (p_srcFilePath.isEmpty()) { return false; } - if (isConflictNameWithExisting(name)) { - return false; - } - + Q_ASSERT(m_directory); // Copy file @name to current directory - QString targetPath = QDir(rootPath).filePath(relativePath); - QString srcName = QFileInfo(name).fileName(); + QString targetPath = m_directory->retrivePath(); + QString srcName = VUtils::fileNameFromPath(p_srcFilePath); if (srcName.isEmpty()) { return false; } - QString targetName = QDir(targetPath).filePath(srcName); - - bool ret = QFile::copy(name, targetName); + QString targetFilePath = QDir(targetPath).filePath(srcName); + bool ret = VUtils::copyFile(p_srcFilePath, targetFilePath, false); if (!ret) { - qWarning() << "error: fail to copy" << name << "to" << targetName; return false; } - // Update current directory's config file to include this new file - QJsonObject dirJson = VConfigManager::readDirectoryConfig(targetPath); - Q_ASSERT(!dirJson.isEmpty()); - QJsonObject fileJson; - fileJson["name"] = srcName; - QJsonArray fileArray = dirJson["files"].toArray(); - fileArray.push_front(fileJson); - dirJson["files"] = fileArray; - if (!VConfigManager::writeDirectoryConfig(targetPath, dirJson)) { - qWarning() << "error: fail to update directory's configuration file to add a new file" - << srcName; - QFile(targetName).remove(); - return false; + VFile *destFile = m_directory->addFile(srcName, -1); + if (destFile) { + return insertFileListItem(destFile, false); } - - return insertFileListItem(fileJson, true); -} - -void VFileList::handleDirectoryRenamed(const QString ¬ebook, - const QString &oldRelativePath, const QString &newRelativePath) -{ - if (notebook == this->notebook - && relativePath.startsWith(oldRelativePath)) { - relativePath.replace(0, oldRelativePath.size(), newRelativePath); - } -} - -void VFileList::convertFileType(const QString ¬ebook, const QString &fileRelativePath, - DocType oldType, DocType newType) -{ - Q_ASSERT(oldType != newType); - QString filePath = QDir(vnote->getNotebookPath(notebook)).filePath(fileRelativePath); - QString fileText = VUtils::readFileFromDisk(filePath); - QTextEdit editor; - if (oldType == DocType::Markdown) { - editor.setPlainText(fileText); - fileText = editor.toHtml(); - } else { - editor.setHtml(fileText); - fileText = editor.toPlainText(); - } - VUtils::writeFileToDisk(filePath, fileText); -} - -void VFileList::deleteLocalImages(const QString &filePath) -{ - if (!VUtils::isMarkdown(filePath)) { - return; - } - - QVector images = VUtils::imagesFromMarkdownFile(filePath); - int deleted = 0; - for (int i = 0; i < images.size(); ++i) { - QFile file(images[i]); - if (file.remove()) { - ++deleted; - } - } - qDebug() << "delete" << deleted << "images for" << filePath; + return false; } void VFileList::copySelectedFiles(bool p_isCut) @@ -480,14 +339,15 @@ void VFileList::copySelectedFiles(bool p_isCut) return; } QJsonArray files; - QDir dir(relativePath); + m_copiedFiles.clear(); for (int i = 0; i < items.size(); ++i) { - QJsonObject itemJson = items[i]->data(Qt::UserRole).toJsonObject(); - QString itemName = itemJson["name"].toString(); + VFile *file = getVFile(items[i]); QJsonObject fileJson; - fileJson["notebook"] = notebook; - fileJson["relative_path"] = dir.filePath(itemName); + fileJson["notebook"] = file->retriveNotebook(); + fileJson["path"] = file->retrivePath(); files.append(fileJson); + + m_copiedFiles.append(file); } copyFileInfoToClipboard(files, p_isCut); @@ -511,219 +371,71 @@ void VFileList::copyFileInfoToClipboard(const QJsonArray &p_files, bool p_isCut) void VFileList::pasteFilesInCurDir() { - pasteFiles(notebook, relativePath); + pasteFiles(m_directory); } -void VFileList::pasteFiles(const QString &p_notebook, const QString &p_dirRelativePath) +void VFileList::pasteFiles(VDirectory *p_destDir) { - qDebug() << "paste files to" << p_notebook << p_dirRelativePath; + qDebug() << "paste files to" << p_destDir->getName(); QClipboard *clipboard = QApplication::clipboard(); QString text = clipboard->text(); QJsonObject clip = QJsonDocument::fromJson(text.toLocal8Bit()).object(); Q_ASSERT(!clip.isEmpty() && clip["operation"] == (int)ClipboardOpType::CopyFile); - bool isCut = clip["is_cut"].toBool(); - QJsonArray sources = clip["sources"].toArray(); - int nrFiles = sources.size(); - QDir destDir(p_dirRelativePath); int nrPasted = 0; - for (int i = 0; i < nrFiles; ++i) { - QJsonObject file = sources[i].toObject(); - QString srcNotebook = file["notebook"].toString(); - QString srcRelativePath = file["relative_path"].toString(); - bool ret = copyFile(srcNotebook, srcRelativePath, p_notebook, - destDir.filePath(VUtils::fileNameFromPath(srcRelativePath)), isCut); - if (ret) { + for (int i = 0; i < m_copiedFiles.size(); ++i) { + QPointer srcFile = m_copiedFiles[i]; + if (!srcFile) { + continue; + } + QString fileName = srcFile->getName(); + VDirectory *srcDir = srcFile->getDirectory(); + if (srcDir == p_destDir && !isCut) { + // Copy and paste in the same directory. + // Rename it to xx_copy.md + fileName = VUtils::generateCopiedFileName(srcDir->retrivePath(), fileName); + } + if (copyFile(p_destDir, fileName, srcFile, isCut)) { nrPasted++; } } + qDebug() << "pasted" << nrPasted << "files sucessfully"; clipboard->clear(); + m_copiedFiles.clear(); } -bool VFileList::copyFile(const QString &p_srcNotebook, const QString &p_srcRelativePath, - const QString &p_destNotebook, const QString &p_destRelativePath, - bool p_isCut) +bool VFileList::copyFile(VDirectory *p_destDir, const QString &p_destName, VFile *p_file, bool p_cut) { - QString srcPath = QDir(vnote->getNotebookPath(p_srcNotebook)).filePath(p_srcRelativePath); - srcPath = QDir::cleanPath(srcPath); - QString destPath = QDir(vnote->getNotebookPath(p_destNotebook)).filePath(p_destRelativePath); - destPath = QDir::cleanPath(destPath); + QString srcPath = QDir::cleanPath(p_file->retrivePath()); + QString destPath = QDir::cleanPath(QDir(p_destDir->retrivePath()).filePath(p_destName)); if (srcPath == destPath) { return true; } - - // If change the file type, we need to convert it - bool needConversion = false; - DocType docType = VUtils::isMarkdown(srcPath) ? DocType::Markdown : DocType::Html; + // If change the file type, we need to close it first + DocType docType = p_file->getDocType(); DocType newDocType = VUtils::isMarkdown(destPath) ? DocType::Markdown : DocType::Html; if (docType != newDocType) { - if (editArea->isFileOpened(p_srcNotebook, p_srcRelativePath)) { - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Rename will change the note type"), - QMessageBox::Ok | QMessageBox::Cancel, this); - msgBox.setDefaultButton(QMessageBox::Ok); - msgBox.setInformativeText(QString("You should close the note %1 before continue") - .arg(VUtils::fileNameFromPath(p_srcRelativePath))); - if (QMessageBox::Ok == msgBox.exec()) { - QJsonObject curItemJson; - curItemJson["notebook"] = p_srcNotebook; - curItemJson["relative_path"] = p_srcRelativePath; - curItemJson["is_forced"] = false; - if (!editArea->closeFile(curItemJson)) { + if (editArea->isFileOpened(p_file)) { + int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"), + QString("The renaming will change the note type."), + QString("You should close the note %1 before continue.").arg(p_file->getName()), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok, this); + if (QMessageBox::Ok == ret) { + if (!editArea->closeFile(p_file, false)) { return false; } } else { return false; } } - // Convert it later - needConversion = true; } - QVector images; - if (docType == DocType::Markdown) { - images = VUtils::imagesFromMarkdownFile(srcPath); - } - - // Copy the file - if (!VUtils::copyFile(srcPath, destPath, p_isCut)) { - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Fail to copy %1 from %2.") - .arg(p_srcRelativePath).arg(p_srcNotebook), QMessageBox::Ok, this); - msgBox.setInformativeText(QString("Please check if there already exists a file with the same name")); - msgBox.exec(); - return false; - } - - if (needConversion) { - convertFileType(p_destNotebook, p_destRelativePath, docType, newDocType); - } - - // We need to copy images when it is still markdown - if (!images.isEmpty()) { - if (newDocType == DocType::Markdown) { - QString dirPath = QDir(VUtils::basePathFromPath(destPath)).filePath("images"); - VUtils::makeDirectory(dirPath); - int nrPasted = 0; - for (int i = 0; i < images.size(); ++i) { - if (!QFile(images[i]).exists()) { - continue; - } - - QString destImagePath = QDir(dirPath).filePath(VUtils::fileNameFromPath(images[i])); - if (VUtils::copyFile(images[i], destImagePath, p_isCut)) { - nrPasted++; - } else { - QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Fail to copy image %1.") - .arg(images[i]), QMessageBox::Ok, this); - msgBox.setInformativeText(QString("Please check if there already exists a file with the same name and manually copy it")); - msgBox.exec(); - } - } - qDebug() << "pasted" << nrPasted << "images sucessfully"; - } else { - // Delete the images - for (int i = 0; i < images.size(); ++i) { - QFile file(images[i]); - file.remove(); - } - } - } - - int idx = -1; - if (p_isCut) { - // Remove src in the config - idx = removeFileInConfig(srcPath); - if (VUtils::basePathFromPath(srcPath) != VUtils::basePathFromPath(destPath)) { - idx = -1; - } - } - - // Add dest in the config - addFileInConfig(destPath, idx); - + VFile *destFile = VDirectory::copyFile(p_destDir, p_destName, p_file, p_cut); updateFileList(); - - if (p_isCut) { - emit fileRenamed(p_srcNotebook, p_srcRelativePath, - p_destNotebook, p_destRelativePath); + if (destFile) { + emit fileUpdated(destFile); } - return true; -} - -int VFileList::removeFileInConfig(const QString &p_filePath) -{ - QString dirPath = VUtils::basePathFromPath(p_filePath); - QString fileName = VUtils::fileNameFromPath(p_filePath); - // Update current directory's config file to exclude this file - QJsonObject dirJson = VConfigManager::readDirectoryConfig(dirPath); - Q_ASSERT(!dirJson.isEmpty()); - QJsonArray fileArray = dirJson["files"].toArray(); - bool deleted = false; - int idx = -1; - for (int i = 0; i < fileArray.size(); ++i) { - QJsonObject ele = fileArray[i].toObject(); - if (ele["name"].toString() == fileName) { - fileArray.removeAt(i); - deleted = true; - idx = i; - break; - } - } - if (!deleted) { - qWarning() << "error: fail to find" << fileName << "to delete"; - return idx; - } - dirJson["files"] = fileArray; - if (!VConfigManager::writeDirectoryConfig(dirPath, dirJson)) { - qWarning() << "error: fail to update directory's configuration file to delete" - << fileName; - return idx; - } - return idx; -} - -// @index = -1, add it to the end of the list -bool VFileList::addFileInConfig(const QString &p_filePath, int p_index) -{ - QString dirPath = VUtils::basePathFromPath(p_filePath); - QString fileName = VUtils::fileNameFromPath(p_filePath); - - // Update current directory's config file to include this file - QJsonObject dirJson = VConfigManager::readDirectoryConfig(dirPath); - Q_ASSERT(!dirJson.isEmpty()); - QJsonObject fileJson; - fileJson["name"] = fileName; - QJsonArray fileArray = dirJson["files"].toArray(); - if (p_index == -1) { - p_index = fileArray.size(); - } - fileArray.insert(p_index, fileJson); - dirJson["files"] = fileArray; - if (!VConfigManager::writeDirectoryConfig(dirPath, dirJson)) { - qWarning() << "error: fail to update directory's configuration file to add a new file" - << fileName; - return false; - } - - return true; -} - -QJsonObject VFileList::readFileInConfig(const QString &p_filePath) -{ - QString dirPath = VUtils::basePathFromPath(p_filePath); - QString fileName = VUtils::fileNameFromPath(p_filePath); - - QJsonObject dirJson = VConfigManager::readDirectoryConfig(dirPath); - Q_ASSERT(!dirJson.isEmpty()); - - qDebug() << "config" << p_filePath; - QJsonArray fileArray = dirJson["files"].toArray(); - for (int i = 0; i < fileArray.size(); ++i) { - QJsonObject ele = fileArray[i].toObject(); - if (ele["name"].toString() == fileName) { - return ele; - } - } - return QJsonObject(); + return destFile != NULL; } diff --git a/src/vfilelist.h b/src/vfilelist.h index 2050bba1..1e7851a2 100644 --- a/src/vfilelist.h +++ b/src/vfilelist.h @@ -5,13 +5,16 @@ #include #include #include +#include +#include #include "vnotebook.h" #include "vconstants.h" +#include "vdirectory.h" +#include "vfile.h" class QAction; class VNote; class QListWidget; -class QListWidgetItem; class QPushButton; class VEditArea; @@ -19,69 +22,49 @@ class VFileList : public QWidget { Q_OBJECT public: - explicit VFileList(VNote *vnote, QWidget *parent = 0); - bool importFile(const QString &name); + explicit VFileList(QWidget *parent = 0); + bool importFile(const QString &p_srcFilePath); inline void setEditArea(VEditArea *editArea); - void fileInfo(const QString &p_notebook, const QString &p_relativePath); - void deleteFile(const QString &p_notebook, const QString &p_relativePath); + void fileInfo(VFile *p_file); + void deleteFile(VFile *p_file); signals: - void fileClicked(QJsonObject fileJson); - void fileDeleted(QJsonObject fileJson); - void fileCreated(QJsonObject fileJson); - void fileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath, - const QString &p_destNotebook, const QString &p_destRelativePath); - void directoryChanged(const QString ¬ebook, const QString &relativePath); + void fileClicked(VFile *p_file, OpenFileMode mode = OpenFileMode::Read); + void fileCreated(VFile *p_file, OpenFileMode mode = OpenFileMode::Read); + void fileUpdated(const VFile *p_file); private slots: void contextMenuRequested(QPoint pos); void handleItemClicked(QListWidgetItem *currentItem); - void curFileInfo(); - void deleteCurFile(); + void fileInfo(); + // m_copiedFiles will keep the files's VFile. void copySelectedFiles(bool p_isCut = false); void cutSelectedFiles(); void pasteFilesInCurDir(); + void deleteFile(); public slots: - void setDirectory(QJsonObject dirJson); - void handleDirectoryRenamed(const QString ¬ebook, const QString &oldRelativePath, - const QString &newRelativePath); + void setDirectory(VDirectory *p_directory); void newFile(); private: void setupUI(); void updateFileList(); - QListWidgetItem *insertFileListItem(QJsonObject fileJson, bool atFront = false); + QListWidgetItem *insertFileListItem(VFile *file, bool atFront = false); void removeFileListItem(QListWidgetItem *item); void initActions(); - bool isConflictNameWithExisting(const QString &name); - QListWidgetItem *createFileAndUpdateList(const QString &name); - void deleteFileAndUpdateList(const QString &p_notebook, - const QString &p_relativePath); - void clearDirectoryInfo(); - void convertFileType(const QString ¬ebook, const QString &fileRelativePath, - DocType oldType, DocType newType); - QListWidgetItem *findItem(const QString &p_notebook, const QString &p_relativePath); - void deleteLocalImages(const QString &filePath); + QListWidgetItem *findItem(const VFile *p_file); void copyFileInfoToClipboard(const QJsonArray &p_files, bool p_isCut); - void pasteFiles(const QString &p_notebook, const QString &p_dirRelativePath); - bool copyFile(const QString &p_srcNotebook, const QString &p_srcRelativePath, - const QString &p_destNotebook, const QString &p_destRelativePath, - bool p_isCut); - int removeFileInConfig(const QString &p_filePath); - bool addFileInConfig(const QString &p_filePath, int p_index); - QJsonObject readFileInConfig(const QString &p_filePath); - - VNote *vnote; - QString notebook; - // Current directory's relative path - QString relativePath; - // Used for cache - QString rootPath; + void pasteFiles(VDirectory *p_destDir); + bool copyFile(VDirectory *p_destDir, const QString &p_destName, VFile *p_file, bool p_cut); + // New items have been added to direcotry. Update file list accordingly. + QVector updateFileListAdded(); + inline QPointer getVFile(QListWidgetItem *p_item); VEditArea *editArea; - QListWidget *fileList; + QPointer m_directory; + QVector > m_copiedFiles; // Actions QAction *newFileAct; @@ -97,4 +80,10 @@ inline void VFileList::setEditArea(VEditArea *editArea) this->editArea = editArea; } +inline QPointer VFileList::getVFile(QListWidgetItem *p_item) +{ + Q_ASSERT(p_item); + return p_item->data(Qt::UserRole).value(); +} + #endif // VFILELIST_H diff --git a/src/vfilelocation.cpp b/src/vfilelocation.cpp deleted file mode 100644 index 7361f269..00000000 --- a/src/vfilelocation.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "vfilelocation.h" - -VFileLocation::VFileLocation() -{ -} - -VFileLocation::VFileLocation(const QString &p_notebook, const QString &p_relativePath) - : m_notebook(p_notebook), m_relativePath(p_relativePath) -{ -} diff --git a/src/vfilelocation.h b/src/vfilelocation.h deleted file mode 100644 index b11a8bc0..00000000 --- a/src/vfilelocation.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef VFILELOCATION_H -#define VFILELOCATION_H - -#include - -class VFileLocation -{ -public: - VFileLocation(); - VFileLocation(const QString &p_notebook, const QString &p_relativePath); - QString m_notebook; - QString m_relativePath; -}; - -#endif // VFILELOCATION_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 948ea2d3..4e6caaec 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -35,7 +35,7 @@ void VMainWindow::setupUI() { QWidget *directoryPanel = setupDirectoryPanel(); - fileList = new VFileList(vnote); + fileList = new VFileList(); fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); editArea = new VEditArea(vnote); @@ -57,23 +57,14 @@ void VMainWindow::setupUI() connect(directoryTree, &VDirectoryTree::currentDirectoryChanged, fileList, &VFileList::setDirectory); - connect(directoryTree, &VDirectoryTree::directoryRenamed, - fileList, &VFileList::handleDirectoryRenamed); - connect(fileList, &VFileList::directoryChanged, - this, &VMainWindow::handleFileListDirectoryChanged); - connect(fileList, &VFileList::fileClicked, editArea, &VEditArea::openFile); - connect(fileList, &VFileList::fileDeleted, - editArea, &VEditArea::closeFile); connect(fileList, &VFileList::fileCreated, editArea, &VEditArea::openFile); + connect(fileList, &VFileList::fileUpdated, + editArea, &VEditArea::handleFileUpdated); connect(editArea, &VEditArea::curTabStatusChanged, this, &VMainWindow::handleCurTabStatusChanged); - connect(directoryTree, &VDirectoryTree::directoryRenamed, - editArea, &VEditArea::handleDirectoryRenamed); - connect(fileList, &VFileList::fileRenamed, - editArea, &VEditArea::handleFileRenamed); connect(newNotebookBtn, &QPushButton::clicked, this, &VMainWindow::onNewNotebookBtnClicked); @@ -433,10 +424,10 @@ void VMainWindow::setCurNotebookIndex(int index) } Q_ASSERT(index < vnote->getNotebooks().size()); // Update directoryTree - QString notebook; + VNotebook *notebook = NULL; if (index > -1) { vconfig.setCurNotebookIndex(index); - notebook = vnote->getNotebooks()[index]->getName(); + notebook = vnote->getNotebooks()[index]; newRootDirAct->setEnabled(true); } else { newRootDirAct->setEnabled(false); @@ -525,7 +516,8 @@ void VMainWindow::onNotebookInfoBtnClicked() void VMainWindow::importNoteFromFile() { static QString lastPath = QDir::homePath(); - QStringList files = QFileDialog::getOpenFileNames(this,tr("Select files(HTML or Markdown) to be imported as notes"), + QStringList files = QFileDialog::getOpenFileNames(this, + tr("Select files(HTML or Markdown) to be imported as notes"), lastPath); if (files.isEmpty()) { return; @@ -668,15 +660,15 @@ void VMainWindow::setRenderBackgroundColor(QAction *action) vnote->updateTemplate(); } -void VMainWindow::updateToolbarFromTabChage(bool empty, bool editMode, bool modifiable) +void VMainWindow::updateToolbarFromTabChage(const VFile *p_file, bool p_editMode) { - if (empty || !modifiable) { + if (!p_file) { editNoteAct->setEnabled(false); saveExitAct->setVisible(false); discardExitAct->setVisible(false); saveNoteAct->setVisible(false); deleteNoteAct->setEnabled(false); - } else if (editMode) { + } else if (p_editMode) { editNoteAct->setEnabled(false); saveExitAct->setVisible(true); discardExitAct->setVisible(true); @@ -690,29 +682,26 @@ void VMainWindow::updateToolbarFromTabChage(bool empty, bool editMode, bool modi deleteNoteAct->setEnabled(true); } - if (empty) { - noteInfoAct->setEnabled(false); - } else { + if (p_file) { noteInfoAct->setEnabled(true); + } else { + noteInfoAct->setEnabled(false); } } -void VMainWindow::handleCurTabStatusChanged(const QString ¬ebook, const QString &relativePath, - bool editMode, bool modifiable, bool modified) +void VMainWindow::handleCurTabStatusChanged(const VFile *p_file, bool p_editMode) { - updateToolbarFromTabChage(notebook.isEmpty(), editMode, modifiable); + updateToolbarFromTabChage(p_file, p_editMode); QString title; - if (!notebook.isEmpty()) { - title = QString("[%1] %2").arg(notebook).arg(relativePath); - if (modified) { + if (p_file) { + title = QString("[%1] %2").arg(p_file->retriveNotebook()).arg(p_file->retrivePath()); + if (p_file->isModified()) { title.append('*'); } } updateWindowTitle(title); - - curEditNotebook = notebook; - curEditRelativePath = relativePath; + m_curFile = const_cast(p_file); } void VMainWindow::changePanelView(QAction *action) @@ -751,15 +740,6 @@ void VMainWindow::changeSplitterView(int nrPanel) } } -void VMainWindow::handleFileListDirectoryChanged(const QString ¬ebook, const QString &relativePath) -{ - if (relativePath.isEmpty()) { - newNoteAct->setEnabled(false); - } else { - newNoteAct->setEnabled(true); - } -} - void VMainWindow::updateWindowTitle(const QString &str) { QString title = "VNote"; @@ -771,12 +751,14 @@ void VMainWindow::updateWindowTitle(const QString &str) void VMainWindow::curEditFileInfo() { - fileList->fileInfo(curEditNotebook, curEditRelativePath); + Q_ASSERT(m_curFile); + fileList->fileInfo(m_curFile); } void VMainWindow::deleteCurNote() { - fileList->deleteFile(curEditNotebook, curEditRelativePath); + Q_ASSERT(m_curFile); + fileList->deleteFile(m_curFile); } void VMainWindow::closeEvent(QCloseEvent *event) diff --git a/src/vmainwindow.h b/src/vmainwindow.h index 5eec4ceb..97b93179 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include "vfile.h" class QLabel; class QComboBox; @@ -48,15 +50,13 @@ private slots: void setTabStopWidth(QAction *action); void setEditorBackgroundColor(QAction *action); void setRenderBackgroundColor(QAction *action); - void handleCurTabStatusChanged(const QString ¬ebook, const QString &relativePath, - bool editMode, bool modifiable, bool modified); + void handleCurTabStatusChanged(const VFile *p_file, bool p_editMode); void changePanelView(QAction *action); - void handleFileListDirectoryChanged(const QString ¬ebook, const QString &relativePath); void curEditFileInfo(); void deleteCurNote(); signals: - void curNotebookChanged(const QString ¬ebookName); + void curNotebookChanged(VNotebook *p_notebook); protected: void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; @@ -74,16 +74,14 @@ private: void initEditorBackgroundMenu(QMenu *menu); void changeSplitterView(int nrPanel); void updateWindowTitle(const QString &str); - void updateToolbarFromTabChage(bool empty, bool editMode, bool modifiable); + void updateToolbarFromTabChage(const VFile *p_file, bool p_editMode); void saveStateAndGeometry(); void restoreStateAndGeometry(); // If true, comboBox changes will not trigger any signal out bool notebookComboMuted; VNote *vnote; - - QString curEditNotebook; - QString curEditRelativePath; + QPointer m_curFile; QLabel *notebookLabel; QLabel *directoryLabel; diff --git a/src/vmdeditoperations.cpp b/src/vmdeditoperations.cpp index 3a742a27..b39c503c 100644 --- a/src/vmdeditoperations.cpp +++ b/src/vmdeditoperations.cpp @@ -9,13 +9,13 @@ #include #include "vmdeditoperations.h" #include "dialog/vinsertimagedialog.h" -#include "vnotefile.h" #include "utils/vutils.h" #include "vedit.h" #include "vdownloader.h" +#include "vfile.h" -VMdEditOperations::VMdEditOperations(VEdit *editor, VNoteFile *noteFile) - : VEditOperations(editor, noteFile) +VMdEditOperations::VMdEditOperations(VEdit *p_editor, VFile *p_file) + : VEditOperations(p_editor, p_file) { } @@ -26,13 +26,11 @@ bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source) return false; } VInsertImageDialog dialog(QObject::tr("Insert image from clipboard"), QObject::tr("image_title"), - "", (QWidget *)editor); + "", (QWidget *)m_editor); dialog.setBrowseable(false); dialog.setImage(image); if (dialog.exec() == QDialog::Accepted) { - insertImageFromQImage(dialog.getImageTitleInput(), - QDir::cleanPath(QDir(noteFile->basePath).filePath("images")), - image); + insertImageFromQImage(dialog.getImageTitleInput(), m_file->retriveImagePath(), image); } return true; } @@ -48,7 +46,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin bool ret = image.save(filePath); if (!ret) { QMessageBox msgBox(QMessageBox::Warning, QObject::tr("Warning"), QString("Fail to save image %1").arg(filePath), - QMessageBox::Ok, (QWidget *)editor); + QMessageBox::Ok, (QWidget *)m_editor); msgBox.exec(); return; } @@ -56,7 +54,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin QString md = QString("![%1](images/%2)").arg(title).arg(fileName); insertTextAtCurPos(md); - editor->insertImage(fileName); + m_editor->insertImage(fileName); } void VMdEditOperations::insertImageFromPath(const QString &title, @@ -71,7 +69,7 @@ void VMdEditOperations::insertImageFromPath(const QString &title, if (!ret) { qWarning() << "error: fail to copy" << oriImagePath << "to" << filePath; QMessageBox msgBox(QMessageBox::Warning, QObject::tr("Warning"), QString("Fail to save image %1").arg(filePath), - QMessageBox::Ok, (QWidget *)editor); + QMessageBox::Ok, (QWidget *)m_editor); msgBox.exec(); return; } @@ -79,7 +77,7 @@ void VMdEditOperations::insertImageFromPath(const QString &title, QString md = QString("![%1](images/%2)").arg(title).arg(fileName); insertTextAtCurPos(md); - editor->insertImage(fileName); + m_editor->insertImage(fileName); } bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl) @@ -105,7 +103,7 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl) } - VInsertImageDialog dialog(title, QObject::tr("image_title"), imagePath, (QWidget *)editor); + VInsertImageDialog dialog(title, QObject::tr("image_title"), imagePath, (QWidget *)m_editor); dialog.setBrowseable(false); if (isLocal) { dialog.setImage(image); @@ -118,12 +116,10 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl) } if (dialog.exec() == QDialog::Accepted) { if (isLocal) { - insertImageFromPath(dialog.getImageTitleInput(), - QDir::cleanPath(QDir(noteFile->basePath).filePath("images")), + insertImageFromPath(dialog.getImageTitleInput(), m_file->retriveImagePath(), imagePath); } else { - insertImageFromQImage(dialog.getImageTitleInput(), - QDir::cleanPath(QDir(noteFile->basePath).filePath("images")), + insertImageFromQImage(dialog.getImageTitleInput(), m_file->retriveImagePath(), dialog.getImage()); } } diff --git a/src/vmdeditoperations.h b/src/vmdeditoperations.h index 12f48dd9..4ef8f55d 100644 --- a/src/vmdeditoperations.h +++ b/src/vmdeditoperations.h @@ -11,7 +11,7 @@ class VMdEditOperations : public VEditOperations { public: - VMdEditOperations(VEdit *editor, VNoteFile *noteFile); + VMdEditOperations(VEdit *p_editor, VFile *p_file); bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertURLFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertImageFromURL(const QUrl &imageUrl); diff --git a/src/vnote.h b/src/vnote.h index d4c4c674..4c542254 100644 --- a/src/vnote.h +++ b/src/vnote.h @@ -10,8 +10,7 @@ #include #include #include "vnotebook.h" - -enum OpenFileMode {Read = 0, Edit}; +#include "vconstants.h" class VNote : public QObject { diff --git a/src/vnotebook.cpp b/src/vnotebook.cpp index 0f6223c9..27867454 100644 --- a/src/vnotebook.cpp +++ b/src/vnotebook.cpp @@ -1,14 +1,16 @@ #include "vnotebook.h" - -VNotebook::VNotebook(QObject *parent) - : QObject(parent) -{ - -} +#include "vdirectory.h" +#include "utils/vutils.h" VNotebook::VNotebook(const QString &name, const QString &path, QObject *parent) : QObject(parent), m_name(name), m_path(path) { + m_rootDir = new VDirectory(this, VUtils::directoryNameFromPath(path)); +} + +VNotebook::~VNotebook() +{ + delete m_rootDir; } QString VNotebook::getName() const @@ -33,5 +35,10 @@ void VNotebook::setPath(const QString &path) void VNotebook::close(bool p_forced) { - //TODO + m_rootDir->close(); +} + +bool VNotebook::open() +{ + return m_rootDir->open(); } diff --git a/src/vnotebook.h b/src/vnotebook.h index 03716575..cd2de0c7 100644 --- a/src/vnotebook.h +++ b/src/vnotebook.h @@ -4,13 +4,17 @@ #include #include +class VDirectory; + class VNotebook : public QObject { Q_OBJECT public: - VNotebook(QObject *parent = 0); VNotebook(const QString &name, const QString &path, QObject *parent = 0); + ~VNotebook(); + // Open the root directory to load contents + bool open(); // Close all the directory and files of this notebook. // If @p_forced, unsaved files will also be closed without a confirm. void close(bool p_forced); @@ -19,10 +23,21 @@ public: QString getPath() const; void setName(const QString &name); void setPath(const QString &path); + inline VDirectory *getRootDir(); + +signals: + void contentChanged(); private: QString m_name; QString m_path; + // Parent is NULL for root directory + VDirectory *m_rootDir; }; +inline VDirectory *VNotebook::getRootDir() +{ + return m_rootDir; +} + #endif // VNOTEBOOK_H diff --git a/src/vnotefile.cpp b/src/vnotefile.cpp deleted file mode 100644 index 7dd44312..00000000 --- a/src/vnotefile.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "vnotefile.h" - -VNoteFile::VNoteFile(const QString &basePath, const QString &fileName, - const QString &content, DocType docType, bool modifiable) - : basePath(basePath), fileName(fileName), - content(content), docType(docType), modifiable(modifiable) -{ - -} diff --git a/src/vnotefile.h b/src/vnotefile.h deleted file mode 100644 index 693c87f4..00000000 --- a/src/vnotefile.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef VNOTEFILE_H -#define VNOTEFILE_H - -#include -#include "vconstants.h" - -class VNoteFile -{ -public: - VNoteFile(const QString &basePath, const QString &fileName, const QString &content, - DocType docType, bool modifiable); - - QString basePath; - QString fileName; - QString content; - DocType docType; - bool modifiable; -}; - -#endif // VNOTEFILE_H