refactor: add VFile and VDirectory

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-11-29 23:38:17 +08:00
parent 8e8d69c85f
commit e7c42ba5be
39 changed files with 1624 additions and 1416 deletions

View File

@ -20,7 +20,7 @@ void VDirInfoDialog::setupUI()
if (!info.isEmpty()) { if (!info.isEmpty()) {
infoLabel = new QLabel(info); infoLabel = new QLabel(info);
} }
nameLabel = new QLabel(tr("&Name")); nameLabel = new QLabel(tr("&Name:"));
nameEdit = new QLineEdit(defaultName); nameEdit = new QLineEdit(defaultName);
nameEdit->selectAll(); nameEdit->selectAll();
nameLabel->setBuddy(nameEdit); nameLabel->setBuddy(nameEdit);
@ -29,10 +29,7 @@ void VDirInfoDialog::setupUI()
okBtn->setDefault(true); okBtn->setDefault(true);
cancelBtn = new QPushButton(tr("&Cancel")); cancelBtn = new QPushButton(tr("&Cancel"));
QVBoxLayout *topLayout = new QVBoxLayout(); QHBoxLayout *topLayout = new QHBoxLayout();
if (infoLabel) {
topLayout->addWidget(infoLabel);
}
topLayout->addWidget(nameLabel); topLayout->addWidget(nameLabel);
topLayout->addWidget(nameEdit); topLayout->addWidget(nameEdit);
@ -42,6 +39,9 @@ void VDirInfoDialog::setupUI()
btmLayout->addWidget(cancelBtn); btmLayout->addWidget(cancelBtn);
QVBoxLayout *mainLayout = new QVBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout();
if (infoLabel) {
mainLayout->addWidget(infoLabel);
}
mainLayout->addLayout(topLayout); mainLayout->addLayout(topLayout);
mainLayout->addLayout(btmLayout); mainLayout->addLayout(btmLayout);
setLayout(mainLayout); setLayout(mainLayout);

View File

@ -29,10 +29,7 @@ void VFileInfoDialog::setupUI()
okBtn->setDefault(true); okBtn->setDefault(true);
cancelBtn = new QPushButton(tr("&Cancel")); cancelBtn = new QPushButton(tr("&Cancel"));
QVBoxLayout *topLayout = new QVBoxLayout(); QHBoxLayout *topLayout = new QHBoxLayout();
if (infoLabel) {
topLayout->addWidget(infoLabel);
}
topLayout->addWidget(nameLabel); topLayout->addWidget(nameLabel);
topLayout->addWidget(nameEdit); topLayout->addWidget(nameEdit);
@ -42,6 +39,9 @@ void VFileInfoDialog::setupUI()
btmLayout->addWidget(cancelBtn); btmLayout->addWidget(cancelBtn);
QVBoxLayout *mainLayout = new QVBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout();
if (infoLabel) {
mainLayout->addWidget(infoLabel);
}
mainLayout->addLayout(topLayout); mainLayout->addLayout(topLayout);
mainLayout->addLayout(btmLayout); mainLayout->addLayout(btmLayout);
setLayout(mainLayout); setLayout(mainLayout);

View File

@ -1,9 +1,9 @@
#include <QtWidgets> #include <QtWidgets>
#include "vnewdirdialog.h" #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) QWidget *parent)
: QDialog(parent), title(title), name(name), defaultName(defaultName) : QDialog(parent), title(title), info(info), name(name), defaultName(defaultName)
{ {
setupUI(); setupUI();
@ -14,6 +14,11 @@ VNewDirDialog::VNewDirDialog(const QString &title, const QString &name, const QS
void VNewDirDialog::setupUI() void VNewDirDialog::setupUI()
{ {
QLabel *infoLabel = NULL;
if (!info.isEmpty()) {
infoLabel = new QLabel(info);
}
nameLabel = new QLabel(name); nameLabel = new QLabel(name);
nameEdit = new QLineEdit(defaultName); nameEdit = new QLineEdit(defaultName);
nameEdit->selectAll(); nameEdit->selectAll();
@ -23,7 +28,7 @@ void VNewDirDialog::setupUI()
okBtn->setDefault(true); okBtn->setDefault(true);
cancelBtn = new QPushButton(tr("&Cancel")); cancelBtn = new QPushButton(tr("&Cancel"));
QVBoxLayout *topLayout = new QVBoxLayout(); QHBoxLayout *topLayout = new QHBoxLayout();
topLayout->addWidget(nameLabel); topLayout->addWidget(nameLabel);
topLayout->addWidget(nameEdit); topLayout->addWidget(nameEdit);
@ -33,6 +38,9 @@ void VNewDirDialog::setupUI()
btmLayout->addWidget(cancelBtn); btmLayout->addWidget(cancelBtn);
QVBoxLayout *mainLayout = new QVBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout();
if (infoLabel) {
mainLayout->addWidget(infoLabel);
}
mainLayout->addLayout(topLayout); mainLayout->addLayout(topLayout);
mainLayout->addLayout(btmLayout); mainLayout->addLayout(btmLayout);
setLayout(mainLayout); setLayout(mainLayout);

View File

@ -12,7 +12,7 @@ class VNewDirDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
VNewDirDialog(const QString &title, const QString &name, VNewDirDialog(const QString &title, const QString &info, const QString &name,
const QString &defaultName, QWidget *parent = 0); const QString &defaultName, QWidget *parent = 0);
QString getNameInput() const; QString getNameInput() const;
@ -28,6 +28,7 @@ private:
QPushButton *cancelBtn; QPushButton *cancelBtn;
QString title; QString title;
QString info;
QString name; QString name;
QString defaultName; QString defaultName;
}; };

View File

@ -1,9 +1,9 @@
#include <QtWidgets> #include <QtWidgets>
#include "vnewfiledialog.h" #include "vnewfiledialog.h"
VNewFileDialog::VNewFileDialog(const QString &title, const QString &name, const QString &defaultName, VNewFileDialog::VNewFileDialog(const QString &title, const QString &info, const QString &name,
QWidget *parent) const QString &defaultName, QWidget *parent)
: QDialog(parent), title(title), name(name), defaultName(defaultName) : QDialog(parent), title(title), info(info), name(name), defaultName(defaultName)
{ {
setupUI(); setupUI();
@ -14,6 +14,11 @@ VNewFileDialog::VNewFileDialog(const QString &title, const QString &name, const
void VNewFileDialog::setupUI() void VNewFileDialog::setupUI()
{ {
QLabel *infoLabel = NULL;
if (!info.isEmpty()) {
infoLabel = new QLabel(info);
}
nameLabel = new QLabel(name); nameLabel = new QLabel(name);
nameEdit = new QLineEdit(defaultName); nameEdit = new QLineEdit(defaultName);
nameEdit->selectAll(); nameEdit->selectAll();
@ -23,7 +28,7 @@ void VNewFileDialog::setupUI()
okBtn->setDefault(true); okBtn->setDefault(true);
cancelBtn = new QPushButton(tr("&Cancel")); cancelBtn = new QPushButton(tr("&Cancel"));
QVBoxLayout *topLayout = new QVBoxLayout(); QHBoxLayout *topLayout = new QHBoxLayout();
topLayout->addWidget(nameLabel); topLayout->addWidget(nameLabel);
topLayout->addWidget(nameEdit); topLayout->addWidget(nameEdit);
@ -33,6 +38,9 @@ void VNewFileDialog::setupUI()
btmLayout->addWidget(cancelBtn); btmLayout->addWidget(cancelBtn);
QVBoxLayout *mainLayout = new QVBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout();
if (infoLabel) {
mainLayout->addWidget(infoLabel);
}
mainLayout->addLayout(topLayout); mainLayout->addLayout(topLayout);
mainLayout->addLayout(btmLayout); mainLayout->addLayout(btmLayout);
setLayout(mainLayout); setLayout(mainLayout);

View File

@ -12,8 +12,8 @@ class VNewFileDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
VNewFileDialog(const QString &title, const QString &name, const QString &defaultName, VNewFileDialog(const QString &title, const QString &info, const QString &name,
QWidget *parent = 0); const QString &defaultName, QWidget *parent = 0);
QString getNameInput() const; QString getNameInput() const;
private slots: private slots:
@ -28,6 +28,7 @@ private:
QPushButton *cancelBtn; QPushButton *cancelBtn;
QString title; QString title;
QString info;
QString name; QString name;
QString defaultName; QString defaultName;
}; };

View File

@ -23,7 +23,6 @@ SOURCES += main.cpp\
vfilelist.cpp \ vfilelist.cpp \
dialog/vnewfiledialog.cpp \ dialog/vnewfiledialog.cpp \
vedit.cpp \ vedit.cpp \
vnotefile.cpp \
vdocument.cpp \ vdocument.cpp \
utils/vutils.cpp \ utils/vutils.cpp \
vpreviewpage.cpp \ vpreviewpage.cpp \
@ -43,8 +42,9 @@ SOURCES += main.cpp\
vedittab.cpp \ vedittab.cpp \
voutline.cpp \ voutline.cpp \
vtoc.cpp \ vtoc.cpp \
vfilelocation.cpp \ vsingleinstanceguard.cpp \
vsingleinstanceguard.cpp vdirectory.cpp \
vfile.cpp
HEADERS += vmainwindow.h \ HEADERS += vmainwindow.h \
vdirectorytree.h \ vdirectorytree.h \
@ -56,7 +56,6 @@ HEADERS += vmainwindow.h \
dialog/vnewfiledialog.h \ dialog/vnewfiledialog.h \
vedit.h \ vedit.h \
vconstants.h \ vconstants.h \
vnotefile.h \
vdocument.h \ vdocument.h \
utils/vutils.h \ utils/vutils.h \
vpreviewpage.h \ vpreviewpage.h \
@ -76,8 +75,9 @@ HEADERS += vmainwindow.h \
vedittab.h \ vedittab.h \
voutline.h \ voutline.h \
vtoc.h \ vtoc.h \
vfilelocation.h \ vsingleinstanceguard.h \
vsingleinstanceguard.h vdirectory.h \
vfile.h
RESOURCES += \ RESOURCES += \
vnote.qrc vnote.qrc

View File

@ -9,7 +9,6 @@
#include <QJsonObject> #include <QJsonObject>
#include <QJsonDocument> #include <QJsonDocument>
#include <QDateTime> #include <QDateTime>
VUtils::VUtils() VUtils::VUtils()
{ {
} }
@ -202,3 +201,38 @@ bool VUtils::copyFile(const QString &p_srcFilePath, const QString &p_destFilePat
} }
return true; 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;
}

View File

@ -5,6 +5,7 @@
#include <QColor> #include <QColor>
#include <QVector> #include <QVector>
#include <QPair> #include <QPair>
#include <QMessageBox>
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "vconstants.h" #include "vconstants.h"
@ -19,6 +20,7 @@ public:
static QRgb QRgbFromString(const QString &str); static QRgb QRgbFromString(const QString &str);
static QString generateImageFileName(const QString &path, const QString &title, static QString generateImageFileName(const QString &path, const QString &title,
const QString &format = "png"); const QString &format = "png");
static QString generateCopiedFileName(const QString &p_dirPath, const QString &p_fileName);
static void processStyle(QString &style, const QVector<QPair<QString, QString> > &varMap); static void processStyle(QString &style, const QVector<QPair<QString, QString> > &varMap);
static bool isMarkdown(const QString &fileName); static bool isMarkdown(const QString &fileName);
static inline QString directoryNameFromPath(const QString& path); static inline QString directoryNameFromPath(const QString& path);
@ -28,6 +30,9 @@ public:
static void makeDirectory(const QString &path); static void makeDirectory(const QString &path);
static ClipboardOpType opTypeInClipboard(); static ClipboardOpType opTypeInClipboard();
static bool copyFile(const QString &p_srcFilePath, const QString &p_destFilePath, bool p_isCut); 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) inline QString VUtils::directoryNameFromPath(const QString &path)

View File

@ -3,5 +3,6 @@
enum class DocType { Html, Markdown }; enum class DocType { Html, Markdown };
enum class ClipboardOpType { Invalid, CopyFile, CopyDir }; enum class ClipboardOpType { Invalid, CopyFile, CopyDir };
enum class OpenFileMode {Read = 0, Edit};
#endif #endif

468
src/vdirectory.cpp Normal file
View File

@ -0,0 +1,468 @@
#include "vdirectory.h"
#include <QDir>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
#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<QString> 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;
}

109
src/vdirectory.h Normal file
View File

@ -0,0 +1,109 @@
#ifndef VDIRECTORY_H
#define VDIRECTORY_H
#include <QObject>
#include <QString>
#include <QVector>
#include <QPointer>
#include <QJsonObject>
#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<VDirectory *> &getSubDirs() const;
inline const QString &getName() const;
inline bool isOpened() const;
inline VDirectory *getParentDirectory();
inline const QVector<VFile *> &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<VNotebook> m_notebook;
QString m_name;
// Owner of the sub-directories
QVector<VDirectory *> m_subDirs;
// Owner of the files
QVector<VFile *> m_files;
bool m_opened;
};
inline const QVector<VDirectory *> &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<VFile *> &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

View File

@ -4,6 +4,8 @@
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "dialog/vdirinfodialog.h" #include "dialog/vdirinfodialog.h"
#include "vnote.h" #include "vnote.h"
#include "vdirectory.h"
#include "utils/vutils.h"
VDirectoryTree::VDirectoryTree(VNote *vnote, QWidget *parent) VDirectoryTree::VDirectoryTree(VNote *vnote, QWidget *parent)
: QTreeWidget(parent), vnote(vnote) : QTreeWidget(parent), vnote(vnote)
@ -14,7 +16,7 @@ VDirectoryTree::VDirectoryTree(VNote *vnote, QWidget *parent)
initActions(); initActions();
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
this, SLOT(updateItemSubtree(QTreeWidgetItem*))); this, SLOT(updateChildren(QTreeWidgetItem*)));
connect(this, SIGNAL(customContextMenuRequested(QPoint)), connect(this, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(contextMenuRequested(QPoint))); this, SLOT(contextMenuRequested(QPoint)));
connect(this, &VDirectoryTree::currentItemChanged, connect(this, &VDirectoryTree::currentItemChanged,
@ -29,11 +31,6 @@ void VDirectoryTree::initActions()
connect(newRootDirAct, &QAction::triggered, connect(newRootDirAct, &QAction::triggered,
this, &VDirectoryTree::newRootDirectory); 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 = new QAction(tr("&New sub-directory"), this);
newSubDirAct->setStatusTip(tr("Create a new sub-directory")); newSubDirAct->setStatusTip(tr("Create a new sub-directory"));
connect(newSubDirAct, &QAction::triggered, connect(newSubDirAct, &QAction::triggered,
@ -52,208 +49,159 @@ void VDirectoryTree::initActions()
this, &VDirectoryTree::editDirectoryInfo); this, &VDirectoryTree::editDirectoryInfo);
} }
void VDirectoryTree::setNotebook(const QString& notebookName) void VDirectoryTree::setNotebook(VNotebook *p_notebook)
{ {
if (notebook == notebookName) { if (m_notebook == p_notebook) {
return; return;
} }
notebook = notebookName; if (m_notebook) {
treePath = ""; // Disconnect
if (notebook.isEmpty()) { 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(); clear();
return; return;
} }
const QVector<VNotebook *> &notebooks = vnote->getNotebooks(); if (!m_notebook->open()) {
for (int i = 0; i < notebooks.size(); ++i) { VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
if (notebooks[i]->getName() == notebook) { QString("Failed to open notebook %1").arg(m_notebook->getName()), "",
treePath = notebooks[i]->getPath(); QMessageBox::Ok, QMessageBox::Ok, this);
break; clear();
} return;
} }
Q_ASSERT(!treePath.isEmpty());
updateDirectoryTree(); 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() 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(); clear();
VDirectory *rootDir = m_notebook->getRootDir();
const QVector<VDirectory *> &subDirs = rootDir->getSubDirs();
for (int i = 0; i < subDirs.size(); ++i) {
VDirectory *dir = subDirs[i];
QTreeWidgetItem *item = new QTreeWidgetItem(this);
if (!validatePath(path)) { fillTreeItem(*item, dir->getName(), dir,
qDebug() << "invalid notebook path:" << path; QIcon(":/resources/icons/dir_item.svg"));
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook path."),
QMessageBox::Ok, this); updateDirectoryTreeOne(item, 1);
msgBox.setInformativeText(QString("Notebook path \"%1\" either does not exist or is not valid.")
.arg(path));
msgBox.exec();
return;
} }
setCurrentItem(topLevelItem(0));
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";
} }
void VDirectoryTree::updateDirectoryTreeOne(QTreeWidgetItem &parent, const QString &relativePath, void VDirectoryTree::updateDirectoryTreeOne(QTreeWidgetItem *p_parent, int depth)
int depth)
{ {
Q_ASSERT(parent.childCount() == 0); Q_ASSERT(p_parent->childCount() == 0);
// Going deep enough
if (depth <= 0) { if (depth <= 0) {
return; return;
} }
VDirectory *dir = getVDirectory(p_parent);
qDebug() << "update directory" << relativePath; if (!dir->open()) {
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString path(QDir::cleanPath(treePath + QDir::separator() + relativePath)); QString("Failed to open directory %1").arg(dir->getName()), "",
if (!validatePath(path)) { QMessageBox::Ok, QMessageBox::Ok, this);
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();
return; return;
} }
const QVector<VDirectory *> &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); fillTreeItem(*item, subDir->getName(), subDir,
if (configJson.isEmpty()) { QIcon(":/resources/icons/dir_item.svg"));
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 sub_directories section updateDirectoryTreeOne(item, depth - 1);
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);
} }
} }
QString VDirectoryTree::calculateItemRelativePath(QTreeWidgetItem *item) // Update @p_item's children items
void VDirectoryTree::updateChildren(QTreeWidgetItem *p_item)
{ {
if (!item) { Q_ASSERT(p_item);
return "."; int nrChild = p_item->childCount();
}
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();
if (nrChild == 0) { 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<QTreeWidgetItem *> VDirectoryTree::updateItemChildrenAdded(QTreeWidgetItem *p_item)
{
QVector<QTreeWidgetItem *> ret;
QPointer<VDirectory> parentDir;
if (p_item) {
parentDir = getVDirectory(p_item);
} else { } else {
for (int i = 0; i < nrChild; ++i) { parentDir = m_notebook->getRootDir();
QTreeWidgetItem *childItem = item->child(i); }
if (childItem->childCount() > 0) { const QVector<VDirectory *> &subDirs = parentDir->getSubDirs();
continue; 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) void VDirectoryTree::contextMenuRequested(QPoint pos)
{ {
if (!m_notebook) {
return;
}
QTreeWidgetItem *item = itemAt(pos); QTreeWidgetItem *item = itemAt(pos);
QMenu menu(this); QMenu menu(this);
@ -265,7 +213,6 @@ void VDirectoryTree::contextMenuRequested(QPoint pos)
if (item->parent()) { if (item->parent()) {
// Low-level item // Low-level item
menu.addAction(newSubDirAct); menu.addAction(newSubDirAct);
menu.addAction(newSiblingDirAct);
} else { } else {
// Top-level item // Top-level item
menu.addAction(newRootDirAct); menu.addAction(newRootDirAct);
@ -277,60 +224,40 @@ void VDirectoryTree::contextMenuRequested(QPoint pos)
menu.exec(mapToGlobal(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() void VDirectoryTree::newSubDirectory()
{ {
if (!m_notebook) {
return;
}
QTreeWidgetItem *curItem = currentItem(); QTreeWidgetItem *curItem = currentItem();
if (!curItem) { if (!curItem) {
return; return;
} }
QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject(); VDirectory *curDir = getVDirectory(curItem);
QString curItemName = curItemJson["name"].toString();
QString info = QString("Create sub-directory under %1.").arg(curDir->getName());
QString text("&Directory name:"); QString text("&Directory name:");
QString defaultText("new_directory"); QString defaultText("new_directory");
do { do {
VNewDirDialog dialog(QString("Create a new directory under %1").arg(curItemName), text, VNewDirDialog dialog(tr("Create directory"), info, text, defaultText, this);
defaultText, this);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput(); QString name = dialog.getNameInput();
if (isConflictNameWithChildren(curItem, name)) { if (curDir->findSubDirectory(name)) {
text = "Name already exists.\nPlease choose another name:"; info = QString("Name already exists under %1.\nPlease choose another name.").arg(curDir->getName());
defaultText = name; defaultText = name;
continue; continue;
} }
QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(curItem, name); VDirectory *subDir = curDir->createSubDirectory(name);
if (newItem) { if (!subDir) {
this->setCurrentItem(newItem); VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Failed to create directory %1.").arg(name), "",
QMessageBox::Ok, QMessageBox::Ok, this);
return;
} }
QVector<QTreeWidgetItem *> items = updateItemChildrenAdded(curItem);
Q_ASSERT(items.size() == 1);
setCurrentItem(items[0]);
} }
break; break;
} while (true); } while (true);
@ -338,23 +265,32 @@ void VDirectoryTree::newSubDirectory()
void VDirectoryTree::newRootDirectory() 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 text("&Directory name:");
QString defaultText("new_directory"); QString defaultText("new_directory");
VDirectory *rootDir = m_notebook->getRootDir();
do { do {
VNewDirDialog dialog(tr("Create a new root directory"), text, VNewDirDialog dialog(tr("Create root directory"), info, text, defaultText, this);
defaultText, this);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput(); QString name = dialog.getNameInput();
if (isConflictNameWithChildren(NULL, name)) { if (rootDir->findSubDirectory(name)) {
text = "Name already exists.\nPlease choose another name:"; info = QString("Name already exists in notebook %1.\nPlease choose another name.").arg(m_notebook->getName());
defaultText = name; defaultText = name;
continue; continue;
} }
QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(NULL, name); VDirectory *subDir = rootDir->createSubDirectory(name);
if (newItem) { if (!subDir) {
this->setCurrentItem(newItem); VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Failed to create directory %1.").arg(name), "",
QMessageBox::Ok, QMessageBox::Ok, this);
return;
} }
QVector<QTreeWidgetItem *> items = updateItemChildrenAdded(NULL);
Q_ASSERT(items.size() == 1);
setCurrentItem(items[0]);
} }
break; break;
} while (true); } while (true);
@ -366,140 +302,26 @@ void VDirectoryTree::deleteDirectory()
if (!curItem) { if (!curItem) {
return; return;
} }
QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject(); VDirectory *curDir = getVDirectory(curItem);
QString curItemName = curItemJson["name"].toString(); int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Are you sure to delete directory %1?").arg(curDir->getName()),
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Are you sure you want to delete directory \"%1\"?") tr("This will delete any files under this directory."), QMessageBox::Ok | QMessageBox::Cancel,
.arg(curItemName), QMessageBox::Ok | QMessageBox::Cancel, this); QMessageBox::Ok, this);
msgBox.setInformativeText(tr("This will delete any files under this directory.")); if (ret == QMessageBox::Ok) {
msgBox.setDefaultButton(QMessageBox::Cancel); VDirectory *parentDir = curDir->getParentDirectory();
if (msgBox.exec() == QMessageBox::Ok) { Q_ASSERT(parentDir);
deleteDirectoryAndUpdateTree(curItem); 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) void VDirectoryTree::currentDirectoryItemChanged(QTreeWidgetItem *currentItem)
{ {
if (!currentItem) { if (!currentItem) {
emit currentDirectoryChanged(QJsonObject()); emit currentDirectoryChanged(NULL);
return; return;
} }
QJsonObject itemJson = currentItem->data(0, Qt::UserRole).toJsonObject(); emit currentDirectoryChanged(getVDirectory(currentItem));
Q_ASSERT(!itemJson.isEmpty());
itemJson["notebook"] = notebook;
itemJson["relative_path"] = calculateItemRelativePath(currentItem);
emit currentDirectoryChanged(itemJson);
} }
void VDirectoryTree::editDirectoryInfo() void VDirectoryTree::editDirectoryInfo()
@ -508,80 +330,33 @@ void VDirectoryTree::editDirectoryInfo()
if (!curItem) { if (!curItem) {
return; 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 info;
QString defaultName = curItemName; QString defaultName = curName;
do { do {
VDirInfoDialog dialog(tr("Directory Information"), info, defaultName, this); VDirInfoDialog dialog(tr("Directory Information"), info, defaultName, this);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput(); QString name = dialog.getNameInput();
if (name == curItemName) { if (name == curName) {
return; return;
} }
if (isConflictNameWithChildren(curItem->parent(), name)) { if (parentDir->findSubDirectory(name)) {
info = "Name already exists.\nPlease choose another name:"; info = "Name already exists.\nPlease choose another name.";
defaultName = name; defaultName = name;
continue; 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; break;
} while (true); } 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);
}

View File

@ -3,6 +3,9 @@
#include <QTreeWidget> #include <QTreeWidget>
#include <QJsonObject> #include <QJsonObject>
#include <QPointer>
#include <QVector>
#include "vdirectory.h"
#include "vnotebook.h" #include "vnotebook.h"
class VNote; class VNote;
@ -14,56 +17,35 @@ public:
explicit VDirectoryTree(VNote *vnote, QWidget *parent = 0); explicit VDirectoryTree(VNote *vnote, QWidget *parent = 0);
signals: signals:
void currentDirectoryChanged(QJsonObject itemJson); void currentDirectoryChanged(VDirectory *p_directory);
void directoryRenamed(const QString &notebook, const QString &oldRelativePath, void directoryRenamed(const QString &notebook, const QString &oldRelativePath,
const QString &newRelativePath); const QString &newRelativePath);
public slots: public slots:
void setNotebook(const QString &notebookName); void setNotebook(VNotebook *p_notebook);
void newRootDirectory(); void newRootDirectory();
void deleteDirectory(); void deleteDirectory();
void editDirectoryInfo(); void editDirectoryInfo();
void updateDirectoryTree();
private slots: private slots:
// Read config file and pdate the subtree of @item in the directory tree. void updateChildren(QTreeWidgetItem *p_item);
// If @item has no child, we will call updateDirectoryTreeOne() to update it.
// Otherwise, we will loop all its direct-children and try to populate it if
// it has not been populated yet.
void updateItemSubtree(QTreeWidgetItem *item);
void contextMenuRequested(QPoint pos); void contextMenuRequested(QPoint pos);
void newSiblingDirectory();
void newSubDirectory(); void newSubDirectory();
void currentDirectoryItemChanged(QTreeWidgetItem *currentItem); void currentDirectoryItemChanged(QTreeWidgetItem *currentItem);
private: private:
QString calculateItemRelativePath(QTreeWidgetItem *item); void updateDirectoryTreeOne(QTreeWidgetItem *p_parent, int depth);
// Clean and pdate the TreeWidget according to treePath void fillTreeItem(QTreeWidgetItem &p_item, const QString &p_name,
void updateDirectoryTree(); VDirectory *p_directory, const QIcon &p_icon);
// 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 initActions(); void initActions();
QTreeWidgetItem* createDirectoryAndUpdateTree(QTreeWidgetItem *parent, const QString &name); // New directories added to @p_item. Update @p_item's children items.
void deleteDirectoryAndUpdateTree(QTreeWidgetItem *item); // If @p_item is NULL, then top level items added.
// If @name conflict with the children's names of @parent. QVector<QTreeWidgetItem *> updateItemChildrenAdded(QTreeWidgetItem *p_item);
bool isConflictNameWithChildren(const QTreeWidgetItem *parent, const QString &name); inline QPointer<VDirectory> getVDirectory(QTreeWidgetItem *p_item);
QTreeWidgetItem* insertDirectoryTreeItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding,
const QJsonObject &newItem);
void removeDirectoryTreeItem(QTreeWidgetItem *item);
void renameDirectory(QTreeWidgetItem *item, const QString &newName);
VNote *vnote; VNote *vnote;
QPointer<VNotebook> m_notebook;
QString notebook;
// Used for cache
QString treePath;
// Actions // Actions
QAction *newRootDirAct; QAction *newRootDirAct;
@ -73,4 +55,10 @@ private:
QAction *dirInfoAct; QAction *dirInfoAct;
}; };
inline QPointer<VDirectory> VDirectoryTree::getVDirectory(QTreeWidgetItem *p_item)
{
Q_ASSERT(p_item);
return p_item->data(0, Qt::UserRole).value<VDirectory *>();
}
#endif // VDIRECTORYTREE_H #endif // VDIRECTORYTREE_H

View File

@ -10,18 +10,20 @@
extern VConfigManager vconfig; extern VConfigManager vconfig;
VEdit::VEdit(VNoteFile *noteFile, QWidget *parent) VEdit::VEdit(VFile *p_file, QWidget *p_parent)
: QTextEdit(parent), noteFile(noteFile), mdHighlighter(NULL) : 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); setAcceptRichText(false);
mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(), mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
500, document()); 500, document());
connect(mdHighlighter, &HGMarkdownHighlighter::highlightCompleted, connect(mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
this, &VEdit::generateEditOutline); this, &VEdit::generateEditOutline);
editOps = new VMdEditOperations(this, noteFile); editOps = new VMdEditOperations(this, m_file);
} else { } else {
setAutoFormatting(QTextEdit::AutoBulletList);
editOps = NULL; editOps = NULL;
} }
@ -33,6 +35,10 @@ VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
VEdit::~VEdit() VEdit::~VEdit()
{ {
if (m_file) {
disconnect(document(), &QTextDocument::modificationChanged,
(VFile *)m_file, &VFile::setModified);
}
if (editOps) { if (editOps) {
delete editOps; delete editOps;
editOps = NULL; editOps = NULL;
@ -41,7 +47,7 @@ VEdit::~VEdit()
void VEdit::updateFontAndPalette() void VEdit::updateFontAndPalette()
{ {
switch (noteFile->docType) { switch (m_file->getDocType()) {
case DocType::Markdown: case DocType::Markdown:
setFont(vconfig.getMdEditFont()); setFont(vconfig.getMdEditFont());
setPalette(vconfig.getMdEditPalette()); setPalette(vconfig.getMdEditPalette());
@ -51,14 +57,14 @@ void VEdit::updateFontAndPalette()
setPalette(vconfig.getBaseEditPalette()); setPalette(vconfig.getBaseEditPalette());
break; break;
default: default:
qWarning() << "error: unknown doc type" << int(noteFile->docType); qWarning() << "error: unknown doc type" << int(m_file->getDocType());
return; return;
} }
} }
void VEdit::updateTabSettings() void VEdit::updateTabSettings()
{ {
switch (noteFile->docType) { switch (m_file->getDocType()) {
case DocType::Markdown: case DocType::Markdown:
if (vconfig.getTabStopWidth() > 0) { if (vconfig.getTabStopWidth() > 0) {
QFontMetrics metrics(vconfig.getMdEditFont()); QFontMetrics metrics(vconfig.getMdEditFont());
@ -72,7 +78,7 @@ void VEdit::updateTabSettings()
} }
break; break;
default: default:
qWarning() << "error: unknown doc type" << int(noteFile->docType); qWarning() << "error: unknown doc type" << int(m_file->getDocType());
return; return;
} }
@ -84,27 +90,28 @@ void VEdit::updateTabSettings()
void VEdit::beginEdit() void VEdit::beginEdit()
{ {
setReadOnly(false);
updateTabSettings(); updateTabSettings();
updateFontAndPalette(); updateFontAndPalette();
switch (noteFile->docType) { switch (m_file->getDocType()) {
case DocType::Html: case DocType::Html:
setHtml(noteFile->content); setHtml(m_file->getContent());
break; break;
case DocType::Markdown: case DocType::Markdown:
setFont(vconfig.getMdEditFont()); setFont(vconfig.getMdEditFont());
setPlainText(noteFile->content); setPlainText(m_file->getContent());
initInitImages(); initInitImages();
break; break;
default: 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() void VEdit::endEdit()
{ {
setReadOnly(true); setReadOnly(true);
if (noteFile->docType == DocType::Markdown) { if (m_file->getDocType() == DocType::Markdown) {
clearUnusedImages(); clearUnusedImages();
} }
} }
@ -115,31 +122,32 @@ void VEdit::saveFile()
return; return;
} }
switch (noteFile->docType) { switch (m_file->getDocType()) {
case DocType::Html: case DocType::Html:
noteFile->content = toHtml(); m_file->setContent(toHtml());
break; break;
case DocType::Markdown: case DocType::Markdown:
noteFile->content = toPlainText(); m_file->setContent(toPlainText());
break; break;
default: default:
qWarning() << "error: unknown doc type" << int(noteFile->docType); qWarning() << "error: unknown doc type" << int(m_file->getDocType());
} }
document()->setModified(false); document()->setModified(false);
} }
void VEdit::reloadFile() void VEdit::reloadFile()
{ {
switch (noteFile->docType) { switch (m_file->getDocType()) {
case DocType::Html: case DocType::Html:
setHtml(noteFile->content); setHtml(m_file->getContent());
break; break;
case DocType::Markdown: case DocType::Markdown:
setPlainText(noteFile->content); setPlainText(m_file->getContent());
break; break;
default: 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) void VEdit::keyPressEvent(QKeyEvent *event)
@ -236,12 +244,12 @@ void VEdit::insertImage(const QString &name)
void VEdit::initInitImages() void VEdit::initInitImages()
{ {
m_initImages = VUtils::imagesFromMarkdownFile(QDir(noteFile->basePath).filePath(noteFile->fileName)); m_initImages = VUtils::imagesFromMarkdownFile(m_file->retrivePath());
} }
void VEdit::clearUnusedImages() void VEdit::clearUnusedImages()
{ {
QVector<QString> images = VUtils::imagesFromMarkdownFile(QDir(noteFile->basePath).filePath(noteFile->fileName)); QVector<QString> images = VUtils::imagesFromMarkdownFile(m_file->retrivePath());
if (!m_insertedImages.isEmpty()) { if (!m_insertedImages.isEmpty()) {
QVector<QString> imageNames(images.size()); QVector<QString> imageNames(images.size());
@ -249,7 +257,7 @@ void VEdit::clearUnusedImages()
imageNames[i] = VUtils::fileNameFromPath(images[i]); 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) { for (int i = 0; i < m_insertedImages.size(); ++i) {
QString name = m_insertedImages[i]; QString name = m_insertedImages[i];
int j; int j;

View File

@ -3,9 +3,10 @@
#include <QTextEdit> #include <QTextEdit>
#include <QString> #include <QString>
#include <QPointer>
#include "vconstants.h" #include "vconstants.h"
#include "vnotefile.h"
#include "vtoc.h" #include "vtoc.h"
#include "vfile.h"
class HGMarkdownHighlighter; class HGMarkdownHighlighter;
class VEditOperations; class VEditOperations;
@ -14,12 +15,12 @@ class VEdit : public QTextEdit
{ {
Q_OBJECT Q_OBJECT
public: public:
VEdit(VNoteFile *noteFile, QWidget *parent = 0); VEdit(VFile *p_file, QWidget *p_parent = 0);
~VEdit(); ~VEdit();
void beginEdit(); void beginEdit();
void endEdit(); void endEdit();
// Save buffer content to noteFile->content. // Save buffer content to VFile.
void saveFile(); void saveFile();
inline void setModified(bool modified); inline void setModified(bool modified);
@ -48,9 +49,9 @@ private:
void initInitImages(); void initInitImages();
void clearUnusedImages(); void clearUnusedImages();
QPointer<VFile> m_file;
bool isExpandTab; bool isExpandTab;
QString tabSpaces; QString tabSpaces;
VNoteFile *noteFile;
HGMarkdownHighlighter *mdHighlighter; HGMarkdownHighlighter *mdHighlighter;
VEditOperations *editOps; VEditOperations *editOps;
QVector<VHeader> headers; QVector<VHeader> headers;
@ -67,6 +68,9 @@ inline bool VEdit::isModified() const
inline void VEdit::setModified(bool modified) inline void VEdit::setModified(bool modified)
{ {
document()->setModified(modified); document()->setModified(modified);
if (m_file) {
m_file->setModified(modified);
}
} }
#endif // VEDIT_H #endif // VEDIT_H

View File

@ -4,6 +4,7 @@
#include "vedittab.h" #include "vedittab.h"
#include "vnote.h" #include "vnote.h"
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "vfile.h"
VEditArea::VEditArea(VNote *vnote, QWidget *parent) VEditArea::VEditArea(VNote *vnote, QWidget *parent)
: QWidget(parent), vnote(vnote), curWindowIndex(0) : 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 // A given file can be opened in multiple split windows. A given file could be
// opened at most in one tab inside a window. // 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; return;
} }
qDebug() << "VEditArea open" << p_file->getName() << (int)p_mode;
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;
// Find if it has been opened already // Find if it has been opened already
int winIdx, tabIdx; int winIdx, tabIdx;
bool setFocus = false; bool setFocus = false;
auto tabs = findTabsByFile(notebook, relativePath); auto tabs = findTabsByFile(p_file);
if (!tabs.empty()) { if (!tabs.empty()) {
// Current window first // Current window first
winIdx = tabs[0].first; winIdx = tabs[0].first;
@ -115,19 +108,19 @@ void VEditArea::openFile(QJsonObject fileJson)
// Open it in current window // Open it in current window
winIdx = curWindowIndex; winIdx = curWindowIndex;
tabIdx = openFileInWindow(winIdx, notebook, relativePath, mode); tabIdx = openFileInWindow(winIdx, p_file, p_mode);
out: out:
setCurrentTab(winIdx, tabIdx, setFocus); setCurrentTab(winIdx, tabIdx, setFocus);
} }
QVector<QPair<int, int> > VEditArea::findTabsByFile(const QString &notebook, const QString &relativePath) QVector<QPair<int, int> > VEditArea::findTabsByFile(const VFile *p_file)
{ {
QVector<QPair<int, int> > tabs; QVector<QPair<int, int> > tabs;
int nrWin = splitter->count(); int nrWin = splitter->count();
for (int winIdx = 0; winIdx < nrWin; ++winIdx) { for (int winIdx = 0; winIdx < nrWin; ++winIdx) {
VEditWindow *win = getWindow(winIdx); VEditWindow *win = getWindow(winIdx);
int tabIdx = win->findTabByFile(notebook, relativePath); int tabIdx = win->findTabByFile(p_file);
if (tabIdx != -1) { if (tabIdx != -1) {
QPair<int, int> match; QPair<int, int> match;
match.first = winIdx; match.first = winIdx;
@ -138,12 +131,11 @@ QVector<QPair<int, int> > VEditArea::findTabsByFile(const QString &notebook, con
return tabs; return tabs;
} }
int VEditArea::openFileInWindow(int windowIndex, const QString &notebook, const QString &relativePath, int VEditArea::openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode)
int mode)
{ {
Q_ASSERT(windowIndex < splitter->count()); Q_ASSERT(windowIndex < splitter->count());
VEditWindow *win = getWindow(windowIndex); 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) void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus)
@ -178,21 +170,18 @@ void VEditArea::updateWindowStatus()
win->requestUpdateCurHeader(); 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; return true;
} }
QString notebook = fileJson["notebook"].toString();
QString relativePath = fileJson["relative_path"].toString();
bool isForced = fileJson["is_forced"].toBool();
int nrWin = splitter->count(); int nrWin = splitter->count();
bool ret = false; bool ret = false;
for (int i = 0; i < nrWin; ++i) { for (int i = 0; i < nrWin; ++i) {
VEditWindow *win = getWindow(i); VEditWindow *win = getWindow(i);
ret = ret || win->closeFile(notebook, relativePath, isForced); ret = ret || win->closeFile(p_file, p_forced);
} }
updateWindowStatus();
return ret; return ret;
} }
@ -206,6 +195,7 @@ bool VEditArea::closeAllFiles(bool p_forced)
return false; return false;
} }
} }
updateWindowStatus();
return true; return true;
} }
@ -233,29 +223,6 @@ void VEditArea::saveAndReadFile()
win->saveAndReadFile(); win->saveAndReadFile();
} }
void VEditArea::handleDirectoryRenamed(const QString &notebook, 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) void VEditArea::handleSplitWindowRequest(VEditWindow *curWindow)
{ {
if (!curWindow) { if (!curWindow) {
@ -287,7 +254,6 @@ void VEditArea::handleRemoveSplitRequest(VEditWindow *curWindow)
void VEditArea::mousePressEvent(QMouseEvent *event) void VEditArea::mousePressEvent(QMouseEvent *event)
{ {
return;
qDebug() << "VEditArea press event" << event; qDebug() << "VEditArea press event" << event;
QPoint pos = event->pos(); QPoint pos = event->pos();
int nrWin = splitter->count(); int nrWin = splitter->count();
@ -335,7 +301,15 @@ void VEditArea::handleOutlineItemActivated(const VAnchor &anchor)
getWindow(curWindowIndex)->scrollCurTab(anchor); getWindow(curWindowIndex)->scrollCurTab(anchor);
} }
bool VEditArea::isFileOpened(const QString &notebook, 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);
}
} }

View File

@ -15,18 +15,18 @@
#include "vtoc.h" #include "vtoc.h"
class VNote; class VNote;
class VFile;
class VEditArea : public QWidget class VEditArea : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit VEditArea(VNote *vnote, QWidget *parent = 0); explicit VEditArea(VNote *vnote, QWidget *parent = 0);
bool isFileOpened(const QString &notebook, const QString &relativePath); bool isFileOpened(const VFile *p_file);
bool closeAllFiles(bool p_forced); bool closeAllFiles(bool p_forced);
signals: signals:
void curTabStatusChanged(const QString &notebook, const QString &relativePath, void curTabStatusChanged(const VFile *p_file, bool p_editMode);
bool editMode, bool modifiable, bool modified);
void outlineChanged(const VToc &toc); void outlineChanged(const VToc &toc);
void curHeaderChanged(const VAnchor &anchor); void curHeaderChanged(const VAnchor &anchor);
@ -34,17 +34,14 @@ protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
public slots: public slots:
void openFile(QJsonObject fileJson); void openFile(VFile *p_file, OpenFileMode p_mode);
bool closeFile(QJsonObject fileJson); bool closeFile(const VFile *p_file, bool p_forced);
void editFile(); void editFile();
void saveFile(); void saveFile();
void readFile(); void readFile();
void saveAndReadFile(); void saveAndReadFile();
void handleOutlineItemActivated(const VAnchor &anchor); void handleOutlineItemActivated(const VAnchor &anchor);
void handleDirectoryRenamed(const QString &notebook, void handleFileUpdated(const VFile *p_file);
const QString &oldRelativePath, const QString &newRelativePath);
void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &p_destNotebook, const QString &p_destRelativePath);
private slots: private slots:
void handleSplitWindowRequest(VEditWindow *curWindow); void handleSplitWindowRequest(VEditWindow *curWindow);
@ -55,9 +52,8 @@ private slots:
private: private:
void setupUI(); void setupUI();
QVector<QPair<int, int> > findTabsByFile(const QString &notebook, const QString &relativePath); QVector<QPair<int, int> > findTabsByFile(const VFile *p_file);
int openFileInWindow(int windowIndex, const QString &notebook, const QString &relativePath, int openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode);
int mode);
void setCurrentTab(int windowIndex, int tabIndex, bool setFocus); void setCurrentTab(int windowIndex, int tabIndex, bool setFocus);
void setCurrentWindow(int windowIndex, bool setFocus); void setCurrentWindow(int windowIndex, bool setFocus);
inline VEditWindow *getWindow(int windowIndex) const; inline VEditWindow *getWindow(int windowIndex) const;

View File

@ -3,15 +3,15 @@
#include "vedit.h" #include "vedit.h"
#include "veditoperations.h" #include "veditoperations.h"
VEditOperations::VEditOperations(VEdit *editor, VNoteFile *noteFile) VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
: editor(editor), noteFile(noteFile) : m_editor(p_editor), m_file(p_file)
{ {
} }
void VEditOperations::insertTextAtCurPos(const QString &text) void VEditOperations::insertTextAtCurPos(const QString &text)
{ {
QTextCursor cursor(editor->document()); QTextCursor cursor(m_editor->document());
cursor.setPosition(editor->textCursor().position()); cursor.setPosition(m_editor->textCursor().position());
cursor.insertText(text); cursor.insertText(text);
} }

View File

@ -1,22 +1,24 @@
#ifndef VEDITOPERATIONS_H #ifndef VEDITOPERATIONS_H
#define VEDITOPERATIONS_H #define VEDITOPERATIONS_H
class VNoteFile; #include <QPointer>
#include "vfile.h"
class VEdit; class VEdit;
class QMimeData; class QMimeData;
class VEditOperations class VEditOperations
{ {
public: public:
VEditOperations(VEdit *editor, VNoteFile *noteFile); VEditOperations(VEdit *p_editor, VFile *p_file);
virtual ~VEditOperations(); virtual ~VEditOperations();
virtual bool insertImageFromMimeData(const QMimeData *source) = 0; virtual bool insertImageFromMimeData(const QMimeData *source) = 0;
virtual bool insertURLFromMimeData(const QMimeData *source) = 0; virtual bool insertURLFromMimeData(const QMimeData *source) = 0;
protected: protected:
void insertTextAtCurPos(const QString &text); void insertTextAtCurPos(const QString &text);
VEdit *editor; VEdit *m_editor;
VNoteFile *noteFile; QPointer<VFile> m_file;
}; };
#endif // VEDITOPERATIONS_H #endif // VEDITOPERATIONS_H

View File

@ -18,37 +18,33 @@
extern VConfigManager vconfig; extern VConfigManager vconfig;
VEditTab::VEditTab(const QString &path, bool modifiable, QWidget *parent) VEditTab::VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent)
: QStackedWidget(parent), mdConverterType(vconfig.getMdConverterType()) : QStackedWidget(p_parent), m_file(p_file), isEditMode(false),
mdConverterType(vconfig.getMdConverterType())
{ {
DocType docType = VUtils::isMarkdown(path) ? DocType::Markdown : DocType::Html; qDebug() << "ready to open" << p_file->getName();
QString basePath = QFileInfo(path).path(); Q_ASSERT(!m_file->isOpened());
QString fileName = QFileInfo(path).fileName(); m_file->open();
qDebug() << "VEditTab basePath" << basePath << "file" << fileName;
QString fileText = VUtils::readFileFromDisk(path);
noteFile = new VNoteFile(basePath, fileName, fileText,
docType, modifiable);
isEditMode = false;
setupUI(); setupUI();
if (p_mode == OpenFileMode::Edit) {
showFileReadMode(); showFileEditMode();
} else {
showFileReadMode();
}
connect(qApp, &QApplication::focusChanged, connect(qApp, &QApplication::focusChanged,
this, &VEditTab::handleFocusChanged); this, &VEditTab::handleFocusChanged);
} }
VEditTab::~VEditTab() VEditTab::~VEditTab()
{ {
if (noteFile) { if (m_file) {
delete noteFile; m_file->close();
} }
} }
void VEditTab::setupUI() void VEditTab::setupUI()
{ {
textEditor = new VEdit(noteFile); textEditor = new VEdit(m_file);
connect(textEditor, &VEdit::headersChanged, connect(textEditor, &VEdit::headersChanged,
this, &VEditTab::updateTocFromHeaders); this, &VEditTab::updateTocFromHeaders);
connect(textEditor, SIGNAL(curHeaderChanged(int)), connect(textEditor, SIGNAL(curHeaderChanged(int)),
@ -57,7 +53,7 @@ void VEditTab::setupUI()
this, &VEditTab::statusChanged); this, &VEditTab::statusChanged);
addWidget(textEditor); addWidget(textEditor);
switch (noteFile->docType) { switch (m_file->getDocType()) {
case DocType::Markdown: case DocType::Markdown:
setupMarkdownPreview(); setupMarkdownPreview();
textBrowser = NULL; textBrowser = NULL;
@ -71,37 +67,38 @@ void VEditTab::setupUI()
webPreviewer = NULL; webPreviewer = NULL;
break; break;
default: default:
qWarning() << "error: unknown doc type" << int(noteFile->docType); qWarning() << "error: unknown doc type" << int(m_file->getDocType());
} }
} }
void VEditTab::showFileReadMode() void VEditTab::showFileReadMode()
{ {
qDebug() << "read" << m_file->getName();
isEditMode = false; isEditMode = false;
switch (noteFile->docType) { switch (m_file->getDocType()) {
case DocType::Html: case DocType::Html:
textBrowser->setHtml(noteFile->content); textBrowser->setHtml(m_file->getContent());
textBrowser->setFont(vconfig.getBaseEditFont()); textBrowser->setFont(vconfig.getBaseEditFont());
textBrowser->setPalette(vconfig.getBaseEditPalette()); textBrowser->setPalette(vconfig.getBaseEditPalette());
setCurrentWidget(textBrowser); setCurrentWidget(textBrowser);
break; break;
case DocType::Markdown: case DocType::Markdown:
if (mdConverterType == MarkdownConverterType::Marked) { if (mdConverterType == MarkdownConverterType::Marked) {
document.setText(noteFile->content); document.setText(m_file->getContent());
} else { } else {
previewByConverter(); previewByConverter();
} }
setCurrentWidget(webPreviewer); setCurrentWidget(webPreviewer);
break; break;
default: default:
qWarning() << "error: unknown doc type" << int(noteFile->docType); qWarning() << "error: unknown doc type" << int(m_file->getDocType());
} }
} }
void VEditTab::previewByConverter() void VEditTab::previewByConverter()
{ {
VMarkdownConverter mdConverter; VMarkdownConverter mdConverter;
QString content = noteFile->content; QString &content = m_file->getContent();
QString html = mdConverter.generateHtml(content, vconfig.getMarkdownExtensions()); QString html = mdConverter.generateHtml(content, vconfig.getMarkdownExtensions());
QRegularExpression tocExp("<p>\\[TOC\\]<\\/p>", QRegularExpression::CaseInsensitiveOption); QRegularExpression tocExp("<p>\\[TOC\\]<\\/p>", QRegularExpression::CaseInsensitiveOption);
QString toc = mdConverter.generateToc(content, vconfig.getMarkdownExtensions()); QString toc = mdConverter.generateToc(content, vconfig.getMarkdownExtensions());
@ -119,15 +116,22 @@ void VEditTab::showFileEditMode()
textEditor->setFocus(); 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; return !isEditMode;
} }
void VEditTab::editFile() void VEditTab::editFile()
{ {
if (isEditMode || !noteFile->modifiable) { if (isEditMode) {
return; return;
} }
@ -141,14 +145,12 @@ void VEditTab::readFile()
} }
if (textEditor->isModified()) { if (textEditor->isModified()) {
// Need to save the changes // Prompt to save the changes
QMessageBox msgBox(this); int ret = VUtils::showMessage(QMessageBox::Information, tr("Information"),
msgBox.setText(QString("The note \"%1\" has been modified.").arg(noteFile->fileName)); QString("Note %1 has been modified.").arg(m_file->getName()),
msgBox.setInformativeText("Do you want to save your changes?"); tr("Do you want to save your changes?"),
msgBox.setIcon(QMessageBox::Information); QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); QMessageBox::Save, this);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret) { switch (ret) {
case QMessageBox::Save: case QMessageBox::Save:
saveFile(); saveFile();
@ -171,33 +173,27 @@ void VEditTab::readFile()
bool VEditTab::saveFile() bool VEditTab::saveFile()
{ {
bool ret; bool ret;
if (!isEditMode || !noteFile->modifiable || !textEditor->isModified()) { if (!isEditMode || !textEditor->isModified()) {
return true; return true;
} }
// Make sure the file already exists. Temporary deal with cases when user delete or move // Make sure the file already exists. Temporary deal with cases when user delete or move
// a file. // a file.
QString filePath = QDir(noteFile->basePath).filePath(noteFile->fileName); QString filePath = m_file->retrivePath();
if (!QFile(filePath).exists()) { if (!QFile(filePath).exists()) {
qWarning() << "error:" << filePath << "being written has been removed"; qWarning() << filePath << "being written has been removed";
QMessageBox msgBox(QMessageBox::Warning, tr("Fail to save to file"), VUtils::showMessage(QMessageBox::Warning, tr("Warning"), tr("Fail to save note"),
QString("%1 being written has been removed.").arg(filePath), QString("%1 being written has been removed.").arg(filePath),
QMessageBox::Ok, this); QMessageBox::Ok, QMessageBox::Ok, this);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
return false; return false;
} }
textEditor->saveFile(); textEditor->saveFile();
ret = VUtils::writeFileToDisk(filePath, noteFile->content); ret = m_file->save();
if (!ret) { if (!ret) {
QMessageBox msgBox(QMessageBox::Warning, tr("Fail to save to file"), 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."), QString("Fail to write to disk when saving a note. Please try it again."),
QMessageBox::Ok, this); QMessageBox::Ok, QMessageBox::Ok, this);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
textEditor->setModified(true); textEditor->setModified(true);
ret = false;
} }
ret = true;
emit statusChanged(); emit statusChanged();
return ret; return ret;
} }
@ -218,10 +214,10 @@ void VEditTab::setupMarkdownPreview()
if (mdConverterType == MarkdownConverterType::Marked) { if (mdConverterType == MarkdownConverterType::Marked) {
webPreviewer->setHtml(VNote::templateHtml, webPreviewer->setHtml(VNote::templateHtml,
QUrl::fromLocalFile(noteFile->basePath + QDir::separator())); QUrl::fromLocalFile(m_file->retriveBasePath() + QDir::separator()));
} else { } else {
webPreviewer->setHtml(VNote::preTemplateHtml + VNote::postTemplateHtml, webPreviewer->setHtml(VNote::preTemplateHtml + VNote::postTemplateHtml,
QUrl::fromLocalFile(noteFile->basePath + QDir::separator())); QUrl::fromLocalFile(m_file->retriveBasePath() + QDir::separator()));
} }
addWidget(webPreviewer); 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; tableOfContent.valid = true;
emit outlineChanged(tableOfContent); emit outlineChanged(tableOfContent);
@ -270,7 +266,7 @@ void VEditTab::updateTocFromHeaders(const QVector<VHeader> &headers)
{ {
tableOfContent.type = VHeaderType::LineNumber; tableOfContent.type = VHeaderType::LineNumber;
tableOfContent.headers = headers; tableOfContent.headers = headers;
tableOfContent.filePath = QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)); tableOfContent.filePath = m_file->retrivePath();
tableOfContent.valid = true; tableOfContent.valid = true;
emit outlineChanged(tableOfContent); emit outlineChanged(tableOfContent);
@ -367,8 +363,7 @@ void VEditTab::updateCurHeader(const QString &anchor)
if (curHeader.anchor.mid(1) == anchor) { if (curHeader.anchor.mid(1) == anchor) {
return; return;
} }
curHeader = VAnchor(QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)), curHeader = VAnchor(m_file->retrivePath(), "#" + anchor, -1);
"#" + anchor, -1);
if (!anchor.isEmpty()) { if (!anchor.isEmpty()) {
emit curHeaderChanged(curHeader); emit curHeaderChanged(curHeader);
} }
@ -379,16 +374,8 @@ void VEditTab::updateCurHeader(int lineNumber)
if (curHeader.lineNumber == lineNumber) { if (curHeader.lineNumber == lineNumber) {
return; return;
} }
curHeader = VAnchor(QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)), curHeader = VAnchor(m_file->retrivePath(), "", lineNumber);
"", lineNumber);
if (lineNumber > -1) { if (lineNumber > -1) {
emit curHeaderChanged(curHeader); emit curHeaderChanged(curHeader);
} }
} }
void VEditTab::updatePath(const QString &newPath)
{
QFileInfo info(newPath);
noteFile->basePath = info.path();
noteFile->fileName = info.fileName();
}

View File

@ -3,13 +3,14 @@
#include <QStackedWidget> #include <QStackedWidget>
#include <QString> #include <QString>
#include <QPointer>
#include "vconstants.h" #include "vconstants.h"
#include "vnotefile.h"
#include "vdocument.h" #include "vdocument.h"
#include "vmarkdownconverter.h" #include "vmarkdownconverter.h"
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "vedit.h" #include "vedit.h"
#include "vtoc.h" #include "vtoc.h"
#include "vfile.h"
class QTextBrowser; class QTextBrowser;
class QWebEngineView; class QWebEngineView;
@ -20,9 +21,9 @@ class VEditTab : public QStackedWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
VEditTab(const QString &path, bool modifiable, QWidget *parent = 0); VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent = 0);
~VEditTab(); ~VEditTab();
bool requestClose(); bool closeFile(bool p_forced);
// Enter edit mode // Enter edit mode
void editFile(); void editFile();
// Enter read mode // Enter read mode
@ -36,8 +37,7 @@ public:
void requestUpdateOutline(); void requestUpdateOutline();
void requestUpdateCurHeader(); void requestUpdateCurHeader();
void scrollToAnchor(const VAnchor& anchor); void scrollToAnchor(const VAnchor& anchor);
void updatePath(const QString &newPath); inline VFile *getFile();
signals: signals:
void getFocused(); void getFocused();
void outlineChanged(const VToc &toc); void outlineChanged(const VToc &toc);
@ -61,7 +61,7 @@ private:
void parseTocUl(QXmlStreamReader &xml, QVector<VHeader> &headers, int level); void parseTocUl(QXmlStreamReader &xml, QVector<VHeader> &headers, int level);
void parseTocLi(QXmlStreamReader &xml, QVector<VHeader> &headers, int level); void parseTocLi(QXmlStreamReader &xml, QVector<VHeader> &headers, int level);
VNoteFile *noteFile; QPointer<VFile> m_file;
bool isEditMode; bool isEditMode;
QTextBrowser *textBrowser; QTextBrowser *textBrowser;
VEdit *textEditor; VEdit *textEditor;
@ -93,4 +93,9 @@ inline bool VEditTab::isChild(QObject *obj)
return false; return false;
} }
inline VFile *VEditTab::getFile()
{
return m_file;
}
#endif // VEDITTAB_H #endif // VEDITTAB_H

View File

@ -5,6 +5,7 @@
#include "vnote.h" #include "vnote.h"
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "utils/vutils.h" #include "utils/vutils.h"
#include "vfile.h"
extern VConfigManager vconfig; extern VConfigManager vconfig;
@ -70,11 +71,9 @@ void VEditWindow::splitWindow()
void VEditWindow::removeSplit() void VEditWindow::removeSplit()
{ {
// Close all the files one by one // Close all the files one by one
// If user do not want to close a file, just stop removing // If user do not want to close a file, just stop removing.
if (closeAllFiles(false)) { // Otherwise, closeAllFiles() will emit requestRemoveSplit.
Q_ASSERT(count() == 0); closeAllFiles(false);
emit requestRemoveSplit(this);
}
} }
void VEditWindow::setRemoveSplitEnable(bool enabled) void VEditWindow::setRemoveSplitEnable(bool enabled)
@ -82,68 +81,67 @@ void VEditWindow::setRemoveSplitEnable(bool enabled)
removeSplitAct->setVisible(enabled); removeSplitAct->setVisible(enabled);
} }
void VEditWindow::openWelcomePage() void VEditWindow::removeEditTab(int p_index)
{ {
int idx = openFileInTab("", vconfig.getWelcomePagePath(), false); if (p_index > -1 && p_index < tabBar()->count()) {
setTabText(idx, generateTabText("Welcome to VNote", false)); VEditTab *editor = getTab(p_index);
setTabToolTip(idx, "VNote"); removeTab(p_index);
delete editor;
if (tabBar()->count() == 0) {
emit requestRemoveSplit(this);
}
}
} }
int VEditWindow::insertTabWithData(int index, QWidget *page, int VEditWindow::insertEditTab(int p_index, VFile *p_file, QWidget *p_page)
const QJsonObject &tabData)
{ {
QString label = VUtils::fileNameFromPath(tabData["relative_path"].toString()); int idx = insertTab(p_index, p_page, p_file->getName());
int idx = insertTab(index, page, label);
QTabBar *tabs = tabBar(); QTabBar *tabs = tabBar();
tabs->setTabData(idx, tabData); tabs->setTabToolTip(idx, generateTooltip(p_file));
tabs->setTabToolTip(idx, generateTooltip(tabData));
noticeStatus(currentIndex()); noticeStatus(currentIndex());
return idx; 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 &notebook, 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 // Find if it has been opened already
int idx = findTabByFile(notebook, relativePath); int idx = findTabByFile(p_file);
if (idx > -1) { if (idx > -1) {
goto out; goto out;
} }
idx = openFileInTab(notebook, relativePath, true); idx = openFileInTab(p_file, p_mode);
out: out:
setCurrentIndex(idx); setCurrentIndex(idx);
if (mode == OpenFileMode::Edit) {
editFile();
}
focusWindow(); focusWindow();
noticeStatus(idx); noticeStatus(idx);
return idx; return idx;
} }
// Return true if we closed the file // Return true if we closed the file actually
bool VEditWindow::closeFile(const QString &notebook, const QString &relativePath, bool isForced) bool VEditWindow::closeFile(const VFile *p_file, bool p_forced)
{ {
// Find if it has been opened already // Find if it has been opened already
int idx = findTabByFile(notebook, relativePath); int idx = findTabByFile(p_file);
if (idx == -1) { if (idx == -1) {
return false; return false;
} }
VEditTab *editor = getTab(idx); VEditTab *editor = getTab(idx);
Q_ASSERT(editor); Q_ASSERT(editor);
bool ok = true; if (!p_forced) {
if (!isForced) {
setCurrentIndex(idx); setCurrentIndex(idx);
noticeStatus(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) { if (ok) {
removeTab(idx); removeEditTab(idx);
delete editor;
} }
updateTabListMenu(); updateTabListMenu();
return ok; return ok;
@ -155,15 +153,15 @@ bool VEditWindow::closeAllFiles(bool p_forced)
bool ret = true; bool ret = true;
for (int i = 0; i < nrTab; ++i) { for (int i = 0; i < nrTab; ++i) {
VEditTab *editor = getTab(0); VEditTab *editor = getTab(0);
bool ok = true;
if (!p_forced) { if (!p_forced) {
setCurrentIndex(0); setCurrentIndex(0);
noticeStatus(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) { if (ok) {
removeTab(0); removeEditTab(0);
delete editor;
} else { } else {
ret = false; ret = false;
break; break;
@ -176,11 +174,9 @@ bool VEditWindow::closeAllFiles(bool p_forced)
return ret; return ret;
} }
int VEditWindow::openFileInTab(const QString &notebook, const QString &relativePath, int VEditWindow::openFileInTab(VFile *p_file, OpenFileMode p_mode)
bool modifiable)
{ {
QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(notebook)).filePath(relativePath)); VEditTab *editor = new VEditTab(p_file, p_mode);
VEditTab *editor = new VEditTab(path, modifiable);
connect(editor, &VEditTab::getFocused, connect(editor, &VEditTab::getFocused,
this, &VEditWindow::getFocused); this, &VEditWindow::getFocused);
connect(editor, &VEditTab::outlineChanged, connect(editor, &VEditTab::outlineChanged,
@ -190,23 +186,16 @@ int VEditWindow::openFileInTab(const QString &notebook, const QString &relativeP
connect(editor, &VEditTab::statusChanged, connect(editor, &VEditTab::statusChanged,
this, &VEditWindow::handleTabStatusChanged); this, &VEditWindow::handleTabStatusChanged);
QJsonObject tabJson; int idx = appendEditTab(p_file, editor);
tabJson["notebook"] = notebook;
tabJson["relative_path"] = relativePath;
tabJson["modifiable"] = modifiable;
int idx = appendTabWithData(editor, tabJson);
updateTabListMenu(); updateTabListMenu();
return idx; return idx;
} }
int VEditWindow::findTabByFile(const QString &notebook, const QString &relativePath) const int VEditWindow::findTabByFile(const VFile *p_file) const
{ {
QTabBar *tabs = tabBar(); int nrTabs = tabBar()->count();
int nrTabs = tabs->count();
for (int i = 0; i < nrTabs; ++i) { for (int i = 0; i < nrTabs; ++i) {
QJsonObject tabJson = tabs->tabData(i).toJsonObject(); if (getTab(i)->getFile() == p_file) {
if (tabJson["notebook"] == notebook && tabJson["relative_path"] == relativePath) {
return i; return i;
} }
} }
@ -217,10 +206,9 @@ bool VEditWindow::handleTabCloseRequest(int index)
{ {
VEditTab *editor = getTab(index); VEditTab *editor = getTab(index);
Q_ASSERT(editor); Q_ASSERT(editor);
bool ok = editor->requestClose(); bool ok = editor->closeFile(false);
if (ok) { if (ok) {
removeTab(index); removeEditTab(index);
delete editor;
} }
updateTabListMenu(); updateTabListMenu();
noticeStatus(currentIndex()); noticeStatus(currentIndex());
@ -260,81 +248,29 @@ void VEditWindow::saveFile()
editor->saveFile(); editor->saveFile();
} }
void VEditWindow::handleDirectoryRenamed(const QString &notebook, const QString &oldRelativePath, void VEditWindow::noticeTabStatus(int p_index)
const QString &newRelativePath)
{ {
QTabBar *tabs = tabBar(); if (p_index == -1) {
int nrTabs = tabs->count(); emit tabStatusChanged(NULL, false);
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);
return; return;
} }
QJsonObject tabJson = tabBar()->tabData(index).toJsonObject(); VEditTab *editor = getTab(p_index);
Q_ASSERT(!tabJson.isEmpty()); const VFile *file = editor->getFile();
QString notebook = tabJson["notebook"].toString();
QString relativePath = tabJson["relative_path"].toString();
VEditTab *editor = getTab(index);
bool editMode = editor->getIsEditMode(); bool editMode = editor->getIsEditMode();
bool modifiable = tabJson["modifiable"].toBool();
// Update tab text // Update tab text
tabBar()->setTabText(index, generateTabText(VUtils::fileNameFromPath(relativePath), tabBar()->setTabText(p_index, generateTabText(file->getName(), file->isModified()));
editor->isModified())); emit tabStatusChanged(file, editMode);
emit tabStatusChanged(notebook, relativePath,
editMode, modifiable, editor->isModified());
} }
// Be requested to report current status
void VEditWindow::requestUpdateTabStatus() void VEditWindow::requestUpdateTabStatus()
{ {
noticeTabStatus(currentIndex()); noticeTabStatus(currentIndex());
} }
// Be requested to report current outline
void VEditWindow::requestUpdateOutline() void VEditWindow::requestUpdateOutline()
{ {
int idx = currentIndex(); int idx = currentIndex();
@ -345,6 +281,7 @@ void VEditWindow::requestUpdateOutline()
getTab(idx)->requestUpdateOutline(); getTab(idx)->requestUpdateOutline();
} }
// Be requested to report current header
void VEditWindow::requestUpdateCurHeader() void VEditWindow::requestUpdateCurHeader()
{ {
int idx = currentIndex(); int idx = currentIndex();
@ -355,6 +292,7 @@ void VEditWindow::requestUpdateCurHeader()
getTab(idx)->requestUpdateCurHeader(); getTab(idx)->requestUpdateCurHeader();
} }
// Focus this windows. Try to focus current tab.
void VEditWindow::focusWindow() void VEditWindow::focusWindow()
{ {
int idx = currentIndex(); int idx = currentIndex();
@ -392,9 +330,8 @@ void VEditWindow::tabListJump(QAction *action)
return; return;
} }
QJsonObject tabJson = action->data().toJsonObject(); QPointer<VFile> file = action->data().value<QPointer<VFile>>();
int idx = findTabByFile(tabJson["notebook"].toString(), int idx = findTabByFile(file);
tabJson["relative_path"].toString());
Q_ASSERT(idx >= 0); Q_ASSERT(idx >= 0);
setCurrentIndex(idx); setCurrentIndex(idx);
noticeStatus(idx); noticeStatus(idx);
@ -416,54 +353,57 @@ void VEditWindow::updateTabListMenu()
QTabBar *tabbar = tabBar(); QTabBar *tabbar = tabBar();
int nrTab = tabbar->count(); int nrTab = tabbar->count();
for (int i = 0; i < nrTab; ++i) { for (int i = 0; i < nrTab; ++i) {
QAction *action = new QAction(tabbar->tabText(i), tabListAct); VEditTab *editor = getTab(i);
action->setStatusTip(generateTooltip(tabbar->tabData(i).toJsonObject())); QPointer<VFile> file = editor->getFile();
action->setData(tabbar->tabData(i)); QAction *action = new QAction(file->getName(), tabListAct);
action->setStatusTip(generateTooltip(file));
action->setData(QVariant::fromValue(file));
menu->addAction(action); menu->addAction(action);
} }
} }
void VEditWindow::handleOutlineChanged(const VToc &toc) void VEditWindow::handleOutlineChanged(const VToc &p_toc)
{ {
// Only propagate it if it is current tab // Only propagate it if it is current tab
int idx = currentIndex(); int idx = currentIndex();
QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject(); if (idx == -1) {
Q_ASSERT(!tabJson.isEmpty()); emit outlineChanged(VToc());
QString path = vnote->getNotebookPath(tabJson["notebook"].toString()); return;
path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString())); }
const VFile *file = getTab(idx)->getFile();
if (toc.filePath == path) { if (p_toc.filePath == file->retrivePath()) {
emit outlineChanged(toc); 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 // Only propagate it if it is current tab
int idx = currentIndex(); int idx = currentIndex();
QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject(); if (idx == -1) {
Q_ASSERT(!tabJson.isEmpty()); emit curHeaderChanged(VAnchor());
QString path = vnote->getNotebookPath(tabJson["notebook"].toString()); return;
path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString())); }
const VFile *file = getTab(idx)->getFile();
if (anchor.filePath == path) { if (p_anchor.filePath == file->retrivePath()) {
emit curHeaderChanged(anchor); emit curHeaderChanged(p_anchor);
} }
} }
void VEditWindow::scrollCurTab(const VAnchor &anchor) void VEditWindow::scrollCurTab(const VAnchor &p_anchor)
{ {
int idx = currentIndex(); int idx = currentIndex();
QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject(); if (idx == -1) {
Q_ASSERT(!tabJson.isEmpty()); emit curHeaderChanged(VAnchor());
QString path = vnote->getNotebookPath(tabJson["notebook"].toString()); return;
path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString())); }
const VFile *file = getTab(idx)->getFile();
if (path == anchor.filePath) { if (file->retrivePath() == p_anchor.filePath) {
getTab(idx)->scrollToAnchor(anchor); getTab(idx)->scrollToAnchor(p_anchor);
} }
} }
// Update tab status, outline and current header.
void VEditWindow::noticeStatus(int index) void VEditWindow::noticeStatus(int index)
{ {
noticeTabStatus(index); noticeTabStatus(index);
@ -482,3 +422,15 @@ void VEditWindow::handleTabStatusChanged()
{ {
noticeTabStatus(currentIndex()); noticeTabStatus(currentIndex());
} }
void VEditWindow::updateFileInfo(const VFile *p_file)
{
if (!p_file) {
return;
}
int idx = findTabByFile(p_file);
if (idx > -1) {
noticeStatus(idx);
updateTabListMenu();
}
}

View File

@ -13,16 +13,16 @@
class VNote; class VNote;
class QPushButton; class QPushButton;
class QActionGroup; class QActionGroup;
class VFile;
class VEditWindow : public QTabWidget class VEditWindow : public QTabWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit VEditWindow(VNote *vnote, QWidget *parent = 0); explicit VEditWindow(VNote *vnote, QWidget *parent = 0);
int findTabByFile(const QString &notebook, const QString &relativePath) const; int findTabByFile(const VFile *p_file) const;
int openFile(const QString &notebook, const QString &relativePath, int openFile(VFile *p_file, OpenFileMode p_mode);
int mode); bool closeFile(const VFile *p_file, bool p_forced);
bool closeFile(const QString &notebook, const QString &relativePath, bool isForced);
void editFile(); void editFile();
void saveFile(); void saveFile();
void readFile(); void readFile();
@ -34,18 +34,14 @@ public:
void requestUpdateCurHeader(); void requestUpdateCurHeader();
// Focus to current tab's editor // Focus to current tab's editor
void focusWindow(); void focusWindow();
void scrollCurTab(const VAnchor &anchor); void scrollCurTab(const VAnchor &p_anchor);
void handleDirectoryRenamed(const QString &notebook, void updateFileInfo(const VFile *p_file);
const QString &oldRelativePath, const QString &newRelativePath);
void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &p_destNotebook, const QString &p_destRelativePath);
protected: protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
signals: signals:
void tabStatusChanged(const QString &notebook, const QString &relativePath, void tabStatusChanged(const VFile *p_file, bool p_editMode);
bool editMode, bool modifiable, bool modified);
void requestSplitWindow(VEditWindow *curWindow); void requestSplitWindow(VEditWindow *curWindow);
void requestRemoveSplit(VEditWindow *curWindow); void requestRemoveSplit(VEditWindow *curWindow);
// This widget or its children get the focus // This widget or its children get the focus
@ -60,21 +56,21 @@ private slots:
void handleTabbarClicked(int index); void handleTabbarClicked(int index);
void contextMenuRequested(QPoint pos); void contextMenuRequested(QPoint pos);
void tabListJump(QAction *action); void tabListJump(QAction *action);
void handleOutlineChanged(const VToc &toc); void handleOutlineChanged(const VToc &p_toc);
void handleCurHeaderChanged(const VAnchor &anchor); void handleCurHeaderChanged(const VAnchor &p_anchor);
void handleTabStatusChanged(); void handleTabStatusChanged();
private: private:
void setupCornerWidget(); void setupCornerWidget();
void openWelcomePage(); void removeEditTab(int p_index);
int insertTabWithData(int index, QWidget *page, const QJsonObject &tabData); int insertEditTab(int p_index, VFile *p_file, QWidget *p_page);
int appendTabWithData(QWidget *page, const QJsonObject &tabData); int appendEditTab(VFile *p_file, QWidget *p_page);
int openFileInTab(const QString &notebook, const QString &relativePath, bool modifiable); int openFileInTab(VFile *p_file, OpenFileMode p_mode);
inline VEditTab *getTab(int tabIndex) const; inline VEditTab *getTab(int tabIndex) const;
void noticeTabStatus(int index); void noticeTabStatus(int p_index);
void updateTabListMenu(); void updateTabListMenu();
void noticeStatus(int index); 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; inline QString generateTabText(const QString &p_name, bool p_modified) const;
VNote *vnote; VNote *vnote;
@ -94,10 +90,13 @@ inline VEditTab* VEditWindow::getTab(int tabIndex) const
return dynamic_cast<VEditTab *>(widget(tabIndex)); return dynamic_cast<VEditTab *>(widget(tabIndex));
} }
inline QString VEditWindow::generateTooltip(const QJsonObject &tabData) const inline QString VEditWindow::generateTooltip(const VFile *p_file) const
{ {
// [Notebook]relativePath if (!p_file) {
return QString("[%1] %2").arg(tabData["notebook"].toString()).arg(tabData["relative_path"].toString()); 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 inline QString VEditWindow::generateTabText(const QString &p_name, bool p_modified) const

114
src/vfile.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "vfile.h"
#include <QDir>
#include <QDebug>
#include <QTextEdit>
#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<QString> 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";
}
}

125
src/vfile.h Normal file
View File

@ -0,0 +1,125 @@
#ifndef VFILE_H
#define VFILE_H
#include <QObject>
#include <QString>
#include <QDir>
#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

View File

@ -7,9 +7,10 @@
#include "vnote.h" #include "vnote.h"
#include "veditarea.h" #include "veditarea.h"
#include "utils/vutils.h" #include "utils/vutils.h"
#include "vfile.h"
VFileList::VFileList(VNote *vnote, QWidget *parent) VFileList::VFileList(QWidget *parent)
: QWidget(parent), vnote(vnote) : QWidget(parent)
{ {
setupUI(); setupUI();
initActions(); initActions();
@ -43,14 +44,14 @@ void VFileList::initActions()
deleteFileAct = new QAction(QIcon(":/resources/icons/delete_note.svg"), deleteFileAct = new QAction(QIcon(":/resources/icons/delete_note.svg"),
tr("&Delete"), this); tr("&Delete"), this);
deleteFileAct->setStatusTip(tr("Delete selected note")); deleteFileAct->setStatusTip(tr("Delete selected note"));
connect(deleteFileAct, &QAction::triggered, connect(deleteFileAct, SIGNAL(triggered(bool)),
this, &VFileList::deleteCurFile); this, SLOT(deleteFile()));
fileInfoAct = new QAction(QIcon(":/resources/icons/note_info.svg"), fileInfoAct = new QAction(QIcon(":/resources/icons/note_info.svg"),
tr("&Info"), this); tr("&Info"), this);
fileInfoAct->setStatusTip(tr("View and edit current note's information")); fileInfoAct->setStatusTip(tr("View and edit current note's information"));
connect(fileInfoAct, &QAction::triggered, connect(fileInfoAct, SIGNAL(triggered(bool)),
this, &VFileList::curFileInfo); this, SLOT(fileInfo()));
copyAct = new QAction(QIcon(":/resources/icons/copy.svg"), copyAct = new QAction(QIcon(":/resources/icons/copy.svg"),
tr("&Copy"), this); tr("&Copy"), this);
@ -71,85 +72,49 @@ void VFileList::initActions()
this, &VFileList::pasteFilesInCurDir); this, &VFileList::pasteFilesInCurDir);
} }
void VFileList::setDirectory(QJsonObject dirJson) void VFileList::setDirectory(VDirectory *p_directory)
{ {
fileList->clear(); if (m_directory == p_directory) {
if (dirJson.isEmpty()) { return;
clearDirectoryInfo(); }
emit directoryChanged("", ""); m_directory = p_directory;
if (!m_directory) {
fileList->clear();
return; return;
} }
notebook = dirJson["notebook"].toString(); qDebug() << "filelist set directory" << m_directory->getName();
relativePath = dirJson["relative_path"].toString();
rootPath = "";
const QVector<VNotebook *> &notebooks = vnote->getNotebooks();
for (int i = 0; i < notebooks.size(); ++i) {
if (notebooks[i]->getName() == notebook) {
rootPath = notebooks[i]->getPath();
break;
}
}
Q_ASSERT(!rootPath.isEmpty());
updateFileList(); updateFileList();
emit directoryChanged(notebook, relativePath);
}
void VFileList::clearDirectoryInfo()
{
notebook = relativePath = rootPath = "";
} }
void VFileList::updateFileList() void VFileList::updateFileList()
{ {
QString path = QDir(rootPath).filePath(relativePath);
fileList->clear(); fileList->clear();
if (!QDir(path).exists()) { if (!m_directory->open()) {
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();
return; return;
} }
const QVector<VFile *> &files = m_directory->getFiles();
QJsonObject configJson = VConfigManager::readDirectoryConfig(path); for (int i = 0; i < files.size(); ++i) {
if (configJson.isEmpty()) { VFile *file = files[i];
qDebug() << "invalid notebook configuration for directory:" << path; insertFileListItem(file);
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);
} }
} }
void VFileList::curFileInfo() void VFileList::fileInfo()
{ {
QListWidgetItem *curItem = fileList->currentItem(); QListWidgetItem *curItem = fileList->currentItem();
QJsonObject curItemJson = curItem->data(Qt::UserRole).toJsonObject(); Q_ASSERT(curItem);
Q_ASSERT(!curItemJson.isEmpty()); fileInfo(getVFile(curItem));
QString curItemName = curItemJson["name"].toString();
fileInfo(notebook, QDir(relativePath).filePath(curItemName));
} }
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 info;
QString defaultName = VUtils::fileNameFromPath(p_relativePath); QString defaultName = p_file->getName();
QString curName = defaultName; QString curName = defaultName;
do { do {
VFileInfoDialog dialog(tr("Note Information"), info, defaultName, this); 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) { if (name == curName) {
return; return;
} }
if (isConflictNameWithExisting(name)) { if (dir->findFile(name)) {
info = "Name already exists.\nPlease choose another name:"; info = "Name already exists.\nPlease choose another name.";
defaultName = name; defaultName = name;
continue; continue;
} }
copyFile(p_notebook, p_relativePath, p_notebook, copyFile(dir, name, p_file, true);
QDir(VUtils::basePathFromPath(p_relativePath)).filePath(name), true);
} }
break; break;
} while (true); } while (true);
} }
QListWidgetItem* VFileList::insertFileListItem(QJsonObject fileJson, bool atFront) QListWidgetItem* VFileList::insertFileListItem(VFile *file, bool atFront)
{ {
Q_ASSERT(!fileJson.isEmpty()); Q_ASSERT(file);
QListWidgetItem *item = new QListWidgetItem(fileJson["name"].toString()); QListWidgetItem *item = new QListWidgetItem(file->getName());
item->setData(Qt::UserRole, fileJson); item->setData(Qt::UserRole, QVariant::fromValue(file));
if (atFront) { if (atFront) {
fileList->insertItem(0, item); 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. // Qt seems not to update the QListWidget correctly. Manually force it to repaint.
fileList->update(); fileList->update();
qDebug() << "add new list item:" << fileJson["name"].toString(); qDebug() << "VFileList adds" << file->getName();
return item; return item;
} }
@ -198,65 +162,91 @@ void VFileList::removeFileListItem(QListWidgetItem *item)
void VFileList::newFile() 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 text("&Note name:");
QString defaultText("new_note"); QString defaultText("new_note");
do { do {
VNewFileDialog dialog(QString("Create a new note under %1").arg(VUtils::directoryNameFromPath(relativePath)), VNewFileDialog dialog(QString("Create new note"), info, text, defaultText, this);
text, defaultText, this);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput(); QString name = dialog.getNameInput();
if (isConflictNameWithExisting(name)) { if (m_directory->findFile(name)) {
text = "Name already exists.\nPlease choose another name:"; info = "Name already exists.\nPlease choose another name.";
defaultText = name; defaultText = name;
continue; continue;
} }
QListWidgetItem *newItem = createFileAndUpdateList(name); VFile *file = m_directory->createFile(name);
if (newItem) { if (!file) {
fileList->setCurrentItem(newItem); VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
// Qt seems not to update the QListWidget correctly. Manually force it to repaint. QString("Failed to create file %1.").arg(name), "",
fileList->update(); QMessageBox::Ok, QMessageBox::Ok, this);
return;
// 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);
} }
QVector<QListWidgetItem *> 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; break;
} while (true); } while (true);
} }
void VFileList::deleteCurFile() QVector<QListWidgetItem *> VFileList::updateFileListAdded()
{
QVector<QListWidgetItem *> ret;
const QVector<VFile *> &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(); QListWidgetItem *curItem = fileList->currentItem();
Q_ASSERT(curItem); Q_ASSERT(curItem);
QJsonObject curItemJson = curItem->data(Qt::UserRole).toJsonObject(); deleteFile(getVFile(curItem));
QString curItemName = curItemJson["name"].toString();
deleteFile(notebook, QDir(relativePath).filePath(curItemName));
} }
// @p_relativePath contains the file name // @p_file may or may not be listed in VFileList
void VFileList::deleteFile(const QString &p_notebook, const QString &p_relativePath) void VFileList::deleteFile(VFile *p_file)
{ {
QString fileName = VUtils::fileNameFromPath(p_relativePath); if (!p_file) {
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), return;
QString("Are you sure you want to delete note \"%1\"?") }
.arg(fileName), QMessageBox::Ok | QMessageBox::Cancel, VDirectory *dir = p_file->getDirectory();
this); QString fileName = p_file->getName();
msgBox.setInformativeText(tr("This may be not recoverable.")); int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
msgBox.setDefaultButton(QMessageBox::Ok); QString("Are you sure to delete note %1?").arg(fileName), tr("This may be unrecoverable!"),
if (msgBox.exec() == QMessageBox::Ok) { QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok, this);
// First close this file forcely if (ret == QMessageBox::Ok) {
QJsonObject curItemJson; editArea->closeFile(p_file, true);
curItemJson["notebook"] = p_notebook;
curItemJson["relative_path"] = QDir::cleanPath(p_relativePath);
curItemJson["is_forced"] = true;
emit fileDeleted(curItemJson);
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); QListWidgetItem *item = fileList->itemAt(pos);
QMenu menu(this); QMenu menu(this);
if (notebook.isEmpty()) { if (!m_directory) {
return; return;
} }
menu.addAction(newFileAct); menu.addAction(newFileAct);
@ -290,187 +280,56 @@ void VFileList::contextMenuRequested(QPoint pos)
menu.exec(fileList->mapToGlobal(pos)); menu.exec(fileList->mapToGlobal(pos));
} }
bool VFileList::isConflictNameWithExisting(const QString &name) QListWidgetItem* VFileList::findItem(const VFile *p_file)
{ {
int nrChild = fileList->count(); if (!p_file || p_file->getDirectory() != m_directory) {
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)) {
return NULL; return NULL;
} }
QString name = VUtils::fileNameFromPath(p_relativePath);
int nrChild = fileList->count(); int nrChild = fileList->count();
for (int i = 0; i < nrChild; ++i) { for (int i = 0; i < nrChild; ++i) {
QListWidgetItem *item = fileList->item(i); QListWidgetItem *item = fileList->item(i);
QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject(); if (p_file == getVFile(item)) {
Q_ASSERT(!itemJson.isEmpty());
if (itemJson["name"].toString() == name) {
return item; return item;
} }
} }
return NULL; 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) void VFileList::handleItemClicked(QListWidgetItem *currentItem)
{ {
if (!currentItem) { if (!currentItem) {
emit fileClicked(QJsonObject()); emit fileClicked(NULL);
return; return;
} }
// Qt seems not to update the QListWidget correctly. Manually force it to repaint. // Qt seems not to update the QListWidget correctly. Manually force it to repaint.
fileList->update(); fileList->update();
QJsonObject itemJson = currentItem->data(Qt::UserRole).toJsonObject(); emit fileClicked(getVFile(currentItem), OpenFileMode::Read);
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);
} }
bool VFileList::importFile(const QString &name) bool VFileList::importFile(const QString &p_srcFilePath)
{ {
if (name.isEmpty()) { if (p_srcFilePath.isEmpty()) {
return false; return false;
} }
if (isConflictNameWithExisting(name)) { Q_ASSERT(m_directory);
return false;
}
// Copy file @name to current directory // Copy file @name to current directory
QString targetPath = QDir(rootPath).filePath(relativePath); QString targetPath = m_directory->retrivePath();
QString srcName = QFileInfo(name).fileName(); QString srcName = VUtils::fileNameFromPath(p_srcFilePath);
if (srcName.isEmpty()) { if (srcName.isEmpty()) {
return false; return false;
} }
QString targetName = QDir(targetPath).filePath(srcName); QString targetFilePath = QDir(targetPath).filePath(srcName);
bool ret = VUtils::copyFile(p_srcFilePath, targetFilePath, false);
bool ret = QFile::copy(name, targetName);
if (!ret) { if (!ret) {
qWarning() << "error: fail to copy" << name << "to" << targetName;
return false; return false;
} }
// Update current directory's config file to include this new file VFile *destFile = m_directory->addFile(srcName, -1);
QJsonObject dirJson = VConfigManager::readDirectoryConfig(targetPath); if (destFile) {
Q_ASSERT(!dirJson.isEmpty()); return insertFileListItem(destFile, false);
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;
} }
return false;
return insertFileListItem(fileJson, true);
}
void VFileList::handleDirectoryRenamed(const QString &notebook,
const QString &oldRelativePath, const QString &newRelativePath)
{
if (notebook == this->notebook
&& relativePath.startsWith(oldRelativePath)) {
relativePath.replace(0, oldRelativePath.size(), newRelativePath);
}
}
void VFileList::convertFileType(const QString &notebook, 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<QString> 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 VFileList::copySelectedFiles(bool p_isCut) void VFileList::copySelectedFiles(bool p_isCut)
@ -480,14 +339,15 @@ void VFileList::copySelectedFiles(bool p_isCut)
return; return;
} }
QJsonArray files; QJsonArray files;
QDir dir(relativePath); m_copiedFiles.clear();
for (int i = 0; i < items.size(); ++i) { for (int i = 0; i < items.size(); ++i) {
QJsonObject itemJson = items[i]->data(Qt::UserRole).toJsonObject(); VFile *file = getVFile(items[i]);
QString itemName = itemJson["name"].toString();
QJsonObject fileJson; QJsonObject fileJson;
fileJson["notebook"] = notebook; fileJson["notebook"] = file->retriveNotebook();
fileJson["relative_path"] = dir.filePath(itemName); fileJson["path"] = file->retrivePath();
files.append(fileJson); files.append(fileJson);
m_copiedFiles.append(file);
} }
copyFileInfoToClipboard(files, p_isCut); copyFileInfoToClipboard(files, p_isCut);
@ -511,219 +371,71 @@ void VFileList::copyFileInfoToClipboard(const QJsonArray &p_files, bool p_isCut)
void VFileList::pasteFilesInCurDir() 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(); QClipboard *clipboard = QApplication::clipboard();
QString text = clipboard->text(); QString text = clipboard->text();
QJsonObject clip = QJsonDocument::fromJson(text.toLocal8Bit()).object(); QJsonObject clip = QJsonDocument::fromJson(text.toLocal8Bit()).object();
Q_ASSERT(!clip.isEmpty() && clip["operation"] == (int)ClipboardOpType::CopyFile); Q_ASSERT(!clip.isEmpty() && clip["operation"] == (int)ClipboardOpType::CopyFile);
bool isCut = clip["is_cut"].toBool(); bool isCut = clip["is_cut"].toBool();
QJsonArray sources = clip["sources"].toArray();
int nrFiles = sources.size();
QDir destDir(p_dirRelativePath);
int nrPasted = 0; int nrPasted = 0;
for (int i = 0; i < nrFiles; ++i) { for (int i = 0; i < m_copiedFiles.size(); ++i) {
QJsonObject file = sources[i].toObject(); QPointer<VFile> srcFile = m_copiedFiles[i];
QString srcNotebook = file["notebook"].toString(); if (!srcFile) {
QString srcRelativePath = file["relative_path"].toString(); continue;
bool ret = copyFile(srcNotebook, srcRelativePath, p_notebook, }
destDir.filePath(VUtils::fileNameFromPath(srcRelativePath)), isCut); QString fileName = srcFile->getName();
if (ret) { 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++; nrPasted++;
} }
} }
qDebug() << "pasted" << nrPasted << "files sucessfully"; qDebug() << "pasted" << nrPasted << "files sucessfully";
clipboard->clear(); clipboard->clear();
m_copiedFiles.clear();
} }
bool VFileList::copyFile(const QString &p_srcNotebook, const QString &p_srcRelativePath, bool VFileList::copyFile(VDirectory *p_destDir, const QString &p_destName, VFile *p_file, bool p_cut)
const QString &p_destNotebook, const QString &p_destRelativePath,
bool p_isCut)
{ {
QString srcPath = QDir(vnote->getNotebookPath(p_srcNotebook)).filePath(p_srcRelativePath); QString srcPath = QDir::cleanPath(p_file->retrivePath());
srcPath = QDir::cleanPath(srcPath); QString destPath = QDir::cleanPath(QDir(p_destDir->retrivePath()).filePath(p_destName));
QString destPath = QDir(vnote->getNotebookPath(p_destNotebook)).filePath(p_destRelativePath);
destPath = QDir::cleanPath(destPath);
if (srcPath == destPath) { if (srcPath == destPath) {
return true; return true;
} }
// If change the file type, we need to close it first
// If change the file type, we need to convert it DocType docType = p_file->getDocType();
bool needConversion = false;
DocType docType = VUtils::isMarkdown(srcPath) ? DocType::Markdown : DocType::Html;
DocType newDocType = VUtils::isMarkdown(destPath) ? DocType::Markdown : DocType::Html; DocType newDocType = VUtils::isMarkdown(destPath) ? DocType::Markdown : DocType::Html;
if (docType != newDocType) { if (docType != newDocType) {
if (editArea->isFileOpened(p_srcNotebook, p_srcRelativePath)) { if (editArea->isFileOpened(p_file)) {
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Rename will change the note type"), int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QMessageBox::Ok | QMessageBox::Cancel, this); QString("The renaming will change the note type."),
msgBox.setDefaultButton(QMessageBox::Ok); QString("You should close the note %1 before continue.").arg(p_file->getName()),
msgBox.setInformativeText(QString("You should close the note %1 before continue") QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok, this);
.arg(VUtils::fileNameFromPath(p_srcRelativePath))); if (QMessageBox::Ok == ret) {
if (QMessageBox::Ok == msgBox.exec()) { if (!editArea->closeFile(p_file, false)) {
QJsonObject curItemJson;
curItemJson["notebook"] = p_srcNotebook;
curItemJson["relative_path"] = p_srcRelativePath;
curItemJson["is_forced"] = false;
if (!editArea->closeFile(curItemJson)) {
return false; return false;
} }
} else { } else {
return false; return false;
} }
} }
// Convert it later
needConversion = true;
} }
QVector<QString> images; VFile *destFile = VDirectory::copyFile(p_destDir, p_destName, p_file, p_cut);
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);
updateFileList(); updateFileList();
if (destFile) {
if (p_isCut) { emit fileUpdated(destFile);
emit fileRenamed(p_srcNotebook, p_srcRelativePath,
p_destNotebook, p_destRelativePath);
} }
return true; return destFile != NULL;
}
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();
} }

View File

@ -5,13 +5,16 @@
#include <QJsonObject> #include <QJsonObject>
#include <QFileInfo> #include <QFileInfo>
#include <QDir> #include <QDir>
#include <QPointer>
#include <QListWidgetItem>
#include "vnotebook.h" #include "vnotebook.h"
#include "vconstants.h" #include "vconstants.h"
#include "vdirectory.h"
#include "vfile.h"
class QAction; class QAction;
class VNote; class VNote;
class QListWidget; class QListWidget;
class QListWidgetItem;
class QPushButton; class QPushButton;
class VEditArea; class VEditArea;
@ -19,69 +22,49 @@ class VFileList : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit VFileList(VNote *vnote, QWidget *parent = 0); explicit VFileList(QWidget *parent = 0);
bool importFile(const QString &name); bool importFile(const QString &p_srcFilePath);
inline void setEditArea(VEditArea *editArea); inline void setEditArea(VEditArea *editArea);
void fileInfo(const QString &p_notebook, const QString &p_relativePath); void fileInfo(VFile *p_file);
void deleteFile(const QString &p_notebook, const QString &p_relativePath); void deleteFile(VFile *p_file);
signals: signals:
void fileClicked(QJsonObject fileJson); void fileClicked(VFile *p_file, OpenFileMode mode = OpenFileMode::Read);
void fileDeleted(QJsonObject fileJson); void fileCreated(VFile *p_file, OpenFileMode mode = OpenFileMode::Read);
void fileCreated(QJsonObject fileJson); void fileUpdated(const VFile *p_file);
void fileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &p_destNotebook, const QString &p_destRelativePath);
void directoryChanged(const QString &notebook, const QString &relativePath);
private slots: private slots:
void contextMenuRequested(QPoint pos); void contextMenuRequested(QPoint pos);
void handleItemClicked(QListWidgetItem *currentItem); void handleItemClicked(QListWidgetItem *currentItem);
void curFileInfo(); void fileInfo();
void deleteCurFile(); // m_copiedFiles will keep the files's VFile.
void copySelectedFiles(bool p_isCut = false); void copySelectedFiles(bool p_isCut = false);
void cutSelectedFiles(); void cutSelectedFiles();
void pasteFilesInCurDir(); void pasteFilesInCurDir();
void deleteFile();
public slots: public slots:
void setDirectory(QJsonObject dirJson); void setDirectory(VDirectory *p_directory);
void handleDirectoryRenamed(const QString &notebook, const QString &oldRelativePath,
const QString &newRelativePath);
void newFile(); void newFile();
private: private:
void setupUI(); void setupUI();
void updateFileList(); void updateFileList();
QListWidgetItem *insertFileListItem(QJsonObject fileJson, bool atFront = false); QListWidgetItem *insertFileListItem(VFile *file, bool atFront = false);
void removeFileListItem(QListWidgetItem *item); void removeFileListItem(QListWidgetItem *item);
void initActions(); void initActions();
bool isConflictNameWithExisting(const QString &name); QListWidgetItem *findItem(const VFile *p_file);
QListWidgetItem *createFileAndUpdateList(const QString &name);
void deleteFileAndUpdateList(const QString &p_notebook,
const QString &p_relativePath);
void clearDirectoryInfo();
void convertFileType(const QString &notebook, const QString &fileRelativePath,
DocType oldType, DocType newType);
QListWidgetItem *findItem(const QString &p_notebook, const QString &p_relativePath);
void deleteLocalImages(const QString &filePath);
void copyFileInfoToClipboard(const QJsonArray &p_files, bool p_isCut); void copyFileInfoToClipboard(const QJsonArray &p_files, bool p_isCut);
void pasteFiles(const QString &p_notebook, const QString &p_dirRelativePath); void pasteFiles(VDirectory *p_destDir);
bool copyFile(const QString &p_srcNotebook, const QString &p_srcRelativePath, bool copyFile(VDirectory *p_destDir, const QString &p_destName, VFile *p_file, bool p_cut);
const QString &p_destNotebook, const QString &p_destRelativePath, // New items have been added to direcotry. Update file list accordingly.
bool p_isCut); QVector<QListWidgetItem *> updateFileListAdded();
int removeFileInConfig(const QString &p_filePath); inline QPointer<VFile> getVFile(QListWidgetItem *p_item);
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;
VEditArea *editArea; VEditArea *editArea;
QListWidget *fileList; QListWidget *fileList;
QPointer<VDirectory> m_directory;
QVector<QPointer<VFile> > m_copiedFiles;
// Actions // Actions
QAction *newFileAct; QAction *newFileAct;
@ -97,4 +80,10 @@ inline void VFileList::setEditArea(VEditArea *editArea)
this->editArea = editArea; this->editArea = editArea;
} }
inline QPointer<VFile> VFileList::getVFile(QListWidgetItem *p_item)
{
Q_ASSERT(p_item);
return p_item->data(Qt::UserRole).value<VFile *>();
}
#endif // VFILELIST_H #endif // VFILELIST_H

View File

@ -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)
{
}

View File

@ -1,15 +0,0 @@
#ifndef VFILELOCATION_H
#define VFILELOCATION_H
#include <QString>
class VFileLocation
{
public:
VFileLocation();
VFileLocation(const QString &p_notebook, const QString &p_relativePath);
QString m_notebook;
QString m_relativePath;
};
#endif // VFILELOCATION_H

View File

@ -35,7 +35,7 @@ void VMainWindow::setupUI()
{ {
QWidget *directoryPanel = setupDirectoryPanel(); QWidget *directoryPanel = setupDirectoryPanel();
fileList = new VFileList(vnote); fileList = new VFileList();
fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
editArea = new VEditArea(vnote); editArea = new VEditArea(vnote);
@ -57,23 +57,14 @@ void VMainWindow::setupUI()
connect(directoryTree, &VDirectoryTree::currentDirectoryChanged, connect(directoryTree, &VDirectoryTree::currentDirectoryChanged,
fileList, &VFileList::setDirectory); fileList, &VFileList::setDirectory);
connect(directoryTree, &VDirectoryTree::directoryRenamed,
fileList, &VFileList::handleDirectoryRenamed);
connect(fileList, &VFileList::directoryChanged,
this, &VMainWindow::handleFileListDirectoryChanged);
connect(fileList, &VFileList::fileClicked, connect(fileList, &VFileList::fileClicked,
editArea, &VEditArea::openFile); editArea, &VEditArea::openFile);
connect(fileList, &VFileList::fileDeleted,
editArea, &VEditArea::closeFile);
connect(fileList, &VFileList::fileCreated, connect(fileList, &VFileList::fileCreated,
editArea, &VEditArea::openFile); editArea, &VEditArea::openFile);
connect(fileList, &VFileList::fileUpdated,
editArea, &VEditArea::handleFileUpdated);
connect(editArea, &VEditArea::curTabStatusChanged, connect(editArea, &VEditArea::curTabStatusChanged,
this, &VMainWindow::handleCurTabStatusChanged); this, &VMainWindow::handleCurTabStatusChanged);
connect(directoryTree, &VDirectoryTree::directoryRenamed,
editArea, &VEditArea::handleDirectoryRenamed);
connect(fileList, &VFileList::fileRenamed,
editArea, &VEditArea::handleFileRenamed);
connect(newNotebookBtn, &QPushButton::clicked, connect(newNotebookBtn, &QPushButton::clicked,
this, &VMainWindow::onNewNotebookBtnClicked); this, &VMainWindow::onNewNotebookBtnClicked);
@ -433,10 +424,10 @@ void VMainWindow::setCurNotebookIndex(int index)
} }
Q_ASSERT(index < vnote->getNotebooks().size()); Q_ASSERT(index < vnote->getNotebooks().size());
// Update directoryTree // Update directoryTree
QString notebook; VNotebook *notebook = NULL;
if (index > -1) { if (index > -1) {
vconfig.setCurNotebookIndex(index); vconfig.setCurNotebookIndex(index);
notebook = vnote->getNotebooks()[index]->getName(); notebook = vnote->getNotebooks()[index];
newRootDirAct->setEnabled(true); newRootDirAct->setEnabled(true);
} else { } else {
newRootDirAct->setEnabled(false); newRootDirAct->setEnabled(false);
@ -525,7 +516,8 @@ void VMainWindow::onNotebookInfoBtnClicked()
void VMainWindow::importNoteFromFile() void VMainWindow::importNoteFromFile()
{ {
static QString lastPath = QDir::homePath(); 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); lastPath);
if (files.isEmpty()) { if (files.isEmpty()) {
return; return;
@ -668,15 +660,15 @@ void VMainWindow::setRenderBackgroundColor(QAction *action)
vnote->updateTemplate(); 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); editNoteAct->setEnabled(false);
saveExitAct->setVisible(false); saveExitAct->setVisible(false);
discardExitAct->setVisible(false); discardExitAct->setVisible(false);
saveNoteAct->setVisible(false); saveNoteAct->setVisible(false);
deleteNoteAct->setEnabled(false); deleteNoteAct->setEnabled(false);
} else if (editMode) { } else if (p_editMode) {
editNoteAct->setEnabled(false); editNoteAct->setEnabled(false);
saveExitAct->setVisible(true); saveExitAct->setVisible(true);
discardExitAct->setVisible(true); discardExitAct->setVisible(true);
@ -690,29 +682,26 @@ void VMainWindow::updateToolbarFromTabChage(bool empty, bool editMode, bool modi
deleteNoteAct->setEnabled(true); deleteNoteAct->setEnabled(true);
} }
if (empty) { if (p_file) {
noteInfoAct->setEnabled(false);
} else {
noteInfoAct->setEnabled(true); noteInfoAct->setEnabled(true);
} else {
noteInfoAct->setEnabled(false);
} }
} }
void VMainWindow::handleCurTabStatusChanged(const QString &notebook, const QString &relativePath, void VMainWindow::handleCurTabStatusChanged(const VFile *p_file, bool p_editMode)
bool editMode, bool modifiable, bool modified)
{ {
updateToolbarFromTabChage(notebook.isEmpty(), editMode, modifiable); updateToolbarFromTabChage(p_file, p_editMode);
QString title; QString title;
if (!notebook.isEmpty()) { if (p_file) {
title = QString("[%1] %2").arg(notebook).arg(relativePath); title = QString("[%1] %2").arg(p_file->retriveNotebook()).arg(p_file->retrivePath());
if (modified) { if (p_file->isModified()) {
title.append('*'); title.append('*');
} }
} }
updateWindowTitle(title); updateWindowTitle(title);
m_curFile = const_cast<VFile *>(p_file);
curEditNotebook = notebook;
curEditRelativePath = relativePath;
} }
void VMainWindow::changePanelView(QAction *action) void VMainWindow::changePanelView(QAction *action)
@ -751,15 +740,6 @@ void VMainWindow::changeSplitterView(int nrPanel)
} }
} }
void VMainWindow::handleFileListDirectoryChanged(const QString &notebook, const QString &relativePath)
{
if (relativePath.isEmpty()) {
newNoteAct->setEnabled(false);
} else {
newNoteAct->setEnabled(true);
}
}
void VMainWindow::updateWindowTitle(const QString &str) void VMainWindow::updateWindowTitle(const QString &str)
{ {
QString title = "VNote"; QString title = "VNote";
@ -771,12 +751,14 @@ void VMainWindow::updateWindowTitle(const QString &str)
void VMainWindow::curEditFileInfo() void VMainWindow::curEditFileInfo()
{ {
fileList->fileInfo(curEditNotebook, curEditRelativePath); Q_ASSERT(m_curFile);
fileList->fileInfo(m_curFile);
} }
void VMainWindow::deleteCurNote() void VMainWindow::deleteCurNote()
{ {
fileList->deleteFile(curEditNotebook, curEditRelativePath); Q_ASSERT(m_curFile);
fileList->deleteFile(m_curFile);
} }
void VMainWindow::closeEvent(QCloseEvent *event) void VMainWindow::closeEvent(QCloseEvent *event)

View File

@ -4,7 +4,9 @@
#include <QMainWindow> #include <QMainWindow>
#include <QVector> #include <QVector>
#include <QPair> #include <QPair>
#include <QPointer>
#include <QString> #include <QString>
#include "vfile.h"
class QLabel; class QLabel;
class QComboBox; class QComboBox;
@ -48,15 +50,13 @@ private slots:
void setTabStopWidth(QAction *action); void setTabStopWidth(QAction *action);
void setEditorBackgroundColor(QAction *action); void setEditorBackgroundColor(QAction *action);
void setRenderBackgroundColor(QAction *action); void setRenderBackgroundColor(QAction *action);
void handleCurTabStatusChanged(const QString &notebook, const QString &relativePath, void handleCurTabStatusChanged(const VFile *p_file, bool p_editMode);
bool editMode, bool modifiable, bool modified);
void changePanelView(QAction *action); void changePanelView(QAction *action);
void handleFileListDirectoryChanged(const QString &notebook, const QString &relativePath);
void curEditFileInfo(); void curEditFileInfo();
void deleteCurNote(); void deleteCurNote();
signals: signals:
void curNotebookChanged(const QString &notebookName); void curNotebookChanged(VNotebook *p_notebook);
protected: protected:
void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE;
@ -74,16 +74,14 @@ private:
void initEditorBackgroundMenu(QMenu *menu); void initEditorBackgroundMenu(QMenu *menu);
void changeSplitterView(int nrPanel); void changeSplitterView(int nrPanel);
void updateWindowTitle(const QString &str); 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 saveStateAndGeometry();
void restoreStateAndGeometry(); void restoreStateAndGeometry();
// If true, comboBox changes will not trigger any signal out // If true, comboBox changes will not trigger any signal out
bool notebookComboMuted; bool notebookComboMuted;
VNote *vnote; VNote *vnote;
QPointer<VFile> m_curFile;
QString curEditNotebook;
QString curEditRelativePath;
QLabel *notebookLabel; QLabel *notebookLabel;
QLabel *directoryLabel; QLabel *directoryLabel;

View File

@ -9,13 +9,13 @@
#include <QMessageBox> #include <QMessageBox>
#include "vmdeditoperations.h" #include "vmdeditoperations.h"
#include "dialog/vinsertimagedialog.h" #include "dialog/vinsertimagedialog.h"
#include "vnotefile.h"
#include "utils/vutils.h" #include "utils/vutils.h"
#include "vedit.h" #include "vedit.h"
#include "vdownloader.h" #include "vdownloader.h"
#include "vfile.h"
VMdEditOperations::VMdEditOperations(VEdit *editor, VNoteFile *noteFile) VMdEditOperations::VMdEditOperations(VEdit *p_editor, VFile *p_file)
: VEditOperations(editor, noteFile) : VEditOperations(p_editor, p_file)
{ {
} }
@ -26,13 +26,11 @@ bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
return false; return false;
} }
VInsertImageDialog dialog(QObject::tr("Insert image from clipboard"), QObject::tr("image_title"), VInsertImageDialog dialog(QObject::tr("Insert image from clipboard"), QObject::tr("image_title"),
"", (QWidget *)editor); "", (QWidget *)m_editor);
dialog.setBrowseable(false); dialog.setBrowseable(false);
dialog.setImage(image); dialog.setImage(image);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
insertImageFromQImage(dialog.getImageTitleInput(), insertImageFromQImage(dialog.getImageTitleInput(), m_file->retriveImagePath(), image);
QDir::cleanPath(QDir(noteFile->basePath).filePath("images")),
image);
} }
return true; return true;
} }
@ -48,7 +46,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin
bool ret = image.save(filePath); bool ret = image.save(filePath);
if (!ret) { if (!ret) {
QMessageBox msgBox(QMessageBox::Warning, QObject::tr("Warning"), QString("Fail to save image %1").arg(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(); msgBox.exec();
return; return;
} }
@ -56,7 +54,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin
QString md = QString("![%1](images/%2)").arg(title).arg(fileName); QString md = QString("![%1](images/%2)").arg(title).arg(fileName);
insertTextAtCurPos(md); insertTextAtCurPos(md);
editor->insertImage(fileName); m_editor->insertImage(fileName);
} }
void VMdEditOperations::insertImageFromPath(const QString &title, void VMdEditOperations::insertImageFromPath(const QString &title,
@ -71,7 +69,7 @@ void VMdEditOperations::insertImageFromPath(const QString &title,
if (!ret) { if (!ret) {
qWarning() << "error: fail to copy" << oriImagePath << "to" << filePath; qWarning() << "error: fail to copy" << oriImagePath << "to" << filePath;
QMessageBox msgBox(QMessageBox::Warning, QObject::tr("Warning"), QString("Fail to save image %1").arg(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(); msgBox.exec();
return; return;
} }
@ -79,7 +77,7 @@ void VMdEditOperations::insertImageFromPath(const QString &title,
QString md = QString("![%1](images/%2)").arg(title).arg(fileName); QString md = QString("![%1](images/%2)").arg(title).arg(fileName);
insertTextAtCurPos(md); insertTextAtCurPos(md);
editor->insertImage(fileName); m_editor->insertImage(fileName);
} }
bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl) 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); dialog.setBrowseable(false);
if (isLocal) { if (isLocal) {
dialog.setImage(image); dialog.setImage(image);
@ -118,12 +116,10 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
} }
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
if (isLocal) { if (isLocal) {
insertImageFromPath(dialog.getImageTitleInput(), insertImageFromPath(dialog.getImageTitleInput(), m_file->retriveImagePath(),
QDir::cleanPath(QDir(noteFile->basePath).filePath("images")),
imagePath); imagePath);
} else { } else {
insertImageFromQImage(dialog.getImageTitleInput(), insertImageFromQImage(dialog.getImageTitleInput(), m_file->retriveImagePath(),
QDir::cleanPath(QDir(noteFile->basePath).filePath("images")),
dialog.getImage()); dialog.getImage());
} }
} }

View File

@ -11,7 +11,7 @@
class VMdEditOperations : public VEditOperations class VMdEditOperations : public VEditOperations
{ {
public: public:
VMdEditOperations(VEdit *editor, VNoteFile *noteFile); VMdEditOperations(VEdit *p_editor, VFile *p_file);
bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
bool insertURLFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertURLFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
bool insertImageFromURL(const QUrl &imageUrl); bool insertImageFromURL(const QUrl &imageUrl);

View File

@ -10,8 +10,7 @@
#include <QHash> #include <QHash>
#include <QPalette> #include <QPalette>
#include "vnotebook.h" #include "vnotebook.h"
#include "vconstants.h"
enum OpenFileMode {Read = 0, Edit};
class VNote : public QObject class VNote : public QObject
{ {

View File

@ -1,14 +1,16 @@
#include "vnotebook.h" #include "vnotebook.h"
#include "vdirectory.h"
VNotebook::VNotebook(QObject *parent) #include "utils/vutils.h"
: QObject(parent)
{
}
VNotebook::VNotebook(const QString &name, const QString &path, QObject *parent) VNotebook::VNotebook(const QString &name, const QString &path, QObject *parent)
: QObject(parent), m_name(name), m_path(path) : 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 QString VNotebook::getName() const
@ -33,5 +35,10 @@ void VNotebook::setPath(const QString &path)
void VNotebook::close(bool p_forced) void VNotebook::close(bool p_forced)
{ {
//TODO m_rootDir->close();
}
bool VNotebook::open()
{
return m_rootDir->open();
} }

View File

@ -4,13 +4,17 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
class VDirectory;
class VNotebook : public QObject class VNotebook : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
VNotebook(QObject *parent = 0);
VNotebook(const QString &name, const QString &path, 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. // Close all the directory and files of this notebook.
// If @p_forced, unsaved files will also be closed without a confirm. // If @p_forced, unsaved files will also be closed without a confirm.
void close(bool p_forced); void close(bool p_forced);
@ -19,10 +23,21 @@ public:
QString getPath() const; QString getPath() const;
void setName(const QString &name); void setName(const QString &name);
void setPath(const QString &path); void setPath(const QString &path);
inline VDirectory *getRootDir();
signals:
void contentChanged();
private: private:
QString m_name; QString m_name;
QString m_path; QString m_path;
// Parent is NULL for root directory
VDirectory *m_rootDir;
}; };
inline VDirectory *VNotebook::getRootDir()
{
return m_rootDir;
}
#endif // VNOTEBOOK_H #endif // VNOTEBOOK_H

View File

@ -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)
{
}

View File

@ -1,20 +0,0 @@
#ifndef VNOTEFILE_H
#define VNOTEFILE_H
#include <QString>
#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