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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,6 @@
#include <QJsonObject>
#include <QJsonDocument>
#include <QDateTime>
VUtils::VUtils()
{
}
@ -202,3 +201,38 @@ bool VUtils::copyFile(const QString &p_srcFilePath, const QString &p_destFilePat
}
return true;
}
int VUtils::showMessage(QMessageBox::Icon p_icon, const QString &p_title, const QString &p_text, const QString &p_infoText,
QMessageBox::StandardButtons p_buttons, QMessageBox::StandardButton p_defaultBtn, QWidget *p_parent)
{
QMessageBox msgBox(p_icon, p_title, p_text, p_buttons, p_parent);
msgBox.setInformativeText(p_infoText);
msgBox.setDefaultButton(p_defaultBtn);
return msgBox.exec();
}
QString VUtils::generateCopiedFileName(const QString &p_dirPath, const QString &p_fileName)
{
QString suffix;
QString base = p_fileName;
int dotIdx = p_fileName.lastIndexOf('.');
if (dotIdx != -1) {
// .md
suffix = p_fileName.right(p_fileName.size() - dotIdx);
base = p_fileName.left(dotIdx);
}
QDir dir(p_dirPath);
QString name = p_fileName;
QString filePath = dir.filePath(name);
int index = 0;
while (QFile(filePath).exists()) {
QString seq;
if (index > 0) {
seq = QString::number(index);
}
index++;
name = QString("%1_copy%2%3").arg(base).arg(seq).arg(suffix);
filePath = dir.filePath(name);
}
return name;
}

View File

@ -5,6 +5,7 @@
#include <QColor>
#include <QVector>
#include <QPair>
#include <QMessageBox>
#include "vconfigmanager.h"
#include "vconstants.h"
@ -19,6 +20,7 @@ public:
static QRgb QRgbFromString(const QString &str);
static QString generateImageFileName(const QString &path, const QString &title,
const QString &format = "png");
static QString generateCopiedFileName(const QString &p_dirPath, const QString &p_fileName);
static void processStyle(QString &style, const QVector<QPair<QString, QString> > &varMap);
static bool isMarkdown(const QString &fileName);
static inline QString directoryNameFromPath(const QString& path);
@ -28,6 +30,9 @@ public:
static void makeDirectory(const QString &path);
static ClipboardOpType opTypeInClipboard();
static bool copyFile(const QString &p_srcFilePath, const QString &p_destFilePath, bool p_isCut);
static int showMessage(QMessageBox::Icon p_icon, const QString &p_title, const QString &p_text,
const QString &p_infoText, QMessageBox::StandardButtons p_buttons,
QMessageBox::StandardButton p_defaultBtn, QWidget *p_parent);
};
inline QString VUtils::directoryNameFromPath(const QString &path)

View File

@ -3,5 +3,6 @@
enum class DocType { Html, Markdown };
enum class ClipboardOpType { Invalid, CopyFile, CopyDir };
enum class OpenFileMode {Read = 0, Edit};
#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 "dialog/vdirinfodialog.h"
#include "vnote.h"
#include "vdirectory.h"
#include "utils/vutils.h"
VDirectoryTree::VDirectoryTree(VNote *vnote, QWidget *parent)
: QTreeWidget(parent), vnote(vnote)
@ -14,7 +16,7 @@ VDirectoryTree::VDirectoryTree(VNote *vnote, QWidget *parent)
initActions();
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
this, SLOT(updateItemSubtree(QTreeWidgetItem*)));
this, SLOT(updateChildren(QTreeWidgetItem*)));
connect(this, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(contextMenuRequested(QPoint)));
connect(this, &VDirectoryTree::currentItemChanged,
@ -29,11 +31,6 @@ void VDirectoryTree::initActions()
connect(newRootDirAct, &QAction::triggered,
this, &VDirectoryTree::newRootDirectory);
newSiblingDirAct = new QAction(tr("New &sibling directory"), this);
newSiblingDirAct->setStatusTip(tr("Create a new sibling directory at current level"));
connect(newSiblingDirAct, &QAction::triggered,
this, &VDirectoryTree::newSiblingDirectory);
newSubDirAct = new QAction(tr("&New sub-directory"), this);
newSubDirAct->setStatusTip(tr("Create a new sub-directory"));
connect(newSubDirAct, &QAction::triggered,
@ -52,208 +49,159 @@ void VDirectoryTree::initActions()
this, &VDirectoryTree::editDirectoryInfo);
}
void VDirectoryTree::setNotebook(const QString& notebookName)
void VDirectoryTree::setNotebook(VNotebook *p_notebook)
{
if (notebook == notebookName) {
if (m_notebook == p_notebook) {
return;
}
notebook = notebookName;
treePath = "";
if (notebook.isEmpty()) {
if (m_notebook) {
// Disconnect
disconnect((VNotebook *)m_notebook, &VNotebook::contentChanged,
this, &VDirectoryTree::updateDirectoryTree);
}
m_notebook = p_notebook;
if (m_notebook) {
connect((VNotebook *)m_notebook, &VNotebook::contentChanged,
this, &VDirectoryTree::updateDirectoryTree);
} else {
clear();
return;
}
const QVector<VNotebook *> &notebooks = vnote->getNotebooks();
for (int i = 0; i < notebooks.size(); ++i) {
if (notebooks[i]->getName() == notebook) {
treePath = notebooks[i]->getPath();
break;
}
if (!m_notebook->open()) {
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Failed to open notebook %1").arg(m_notebook->getName()), "",
QMessageBox::Ok, QMessageBox::Ok, this);
clear();
return;
}
Q_ASSERT(!treePath.isEmpty());
updateDirectoryTree();
if (topLevelItemCount() > 0) {
setCurrentItem(topLevelItem(0));
}
}
bool VDirectoryTree::validatePath(const QString &path)
void VDirectoryTree::fillTreeItem(QTreeWidgetItem &p_item, const QString &p_name,
VDirectory *p_directory, const QIcon &p_icon)
{
return QDir(path).exists();
p_item.setText(0, p_name);
p_item.setData(0, Qt::UserRole, QVariant::fromValue(p_directory));
p_item.setIcon(0, p_icon);
}
void VDirectoryTree::updateDirectoryTree()
{
updateDirectoryTreeTopLevel();
int nrTopLevelItems = topLevelItemCount();
for (int i = 0; i < nrTopLevelItems; ++i) {
QTreeWidgetItem *item = topLevelItem(i);
Q_ASSERT(item);
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
Q_ASSERT(!itemJson.isEmpty());
updateDirectoryTreeOne(*item, itemJson["name"].toString(), 1);
}
}
void VDirectoryTree::fillDirectoryTreeItem(QTreeWidgetItem &item, QJsonObject itemJson)
{
item.setText(0, itemJson["name"].toString());
item.setData(0, Qt::UserRole, itemJson);
item.setIcon(0, QIcon(":/resources/icons/dir_item.svg"));
}
QTreeWidgetItem* VDirectoryTree::insertDirectoryTreeItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding,
const QJsonObject &newItem)
{
QTreeWidgetItem *item;
if (parent) {
if (preceding) {
item = new QTreeWidgetItem(parent, preceding);
} else {
item = new QTreeWidgetItem(parent);
}
} else {
if (preceding) {
item = new QTreeWidgetItem(this, preceding);
} else {
item = new QTreeWidgetItem(this);
}
}
fillDirectoryTreeItem(*item, newItem);
return item;
}
void VDirectoryTree::removeDirectoryTreeItem(QTreeWidgetItem *item)
{
delete item;
}
void VDirectoryTree::updateDirectoryTreeTopLevel()
{
const QString &path = treePath;
clear();
VDirectory *rootDir = m_notebook->getRootDir();
const QVector<VDirectory *> &subDirs = rootDir->getSubDirs();
for (int i = 0; i < subDirs.size(); ++i) {
VDirectory *dir = subDirs[i];
QTreeWidgetItem *item = new QTreeWidgetItem(this);
if (!validatePath(path)) {
qDebug() << "invalid notebook path:" << path;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook path."),
QMessageBox::Ok, this);
msgBox.setInformativeText(QString("Notebook path \"%1\" either does not exist or is not valid.")
.arg(path));
msgBox.exec();
return;
fillTreeItem(*item, dir->getName(), dir,
QIcon(":/resources/icons/dir_item.svg"));
updateDirectoryTreeOne(item, 1);
}
QJsonObject configJson = VConfigManager::readDirectoryConfig(path);
if (configJson.isEmpty()) {
qDebug() << "invalid notebook configuration for path:" << path;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook configuration."),
QMessageBox::Ok, this);
msgBox.setInformativeText(QString("Notebook path \"%1\" does not contain a valid configuration file.")
.arg(path));
msgBox.exec();
return;
}
// Handle sub_directories section
QJsonArray dirJson = configJson["sub_directories"].toArray();
QTreeWidgetItem *preItem = NULL;
for (int i = 0; i < dirJson.size(); ++i) {
QJsonObject dirItem = dirJson[i].toObject();
QTreeWidgetItem *treeItem = insertDirectoryTreeItem(NULL, preItem, dirItem);
preItem = treeItem;
}
qDebug() << "updated" << dirJson.size() << "top-level items";
setCurrentItem(topLevelItem(0));
}
void VDirectoryTree::updateDirectoryTreeOne(QTreeWidgetItem &parent, const QString &relativePath,
int depth)
void VDirectoryTree::updateDirectoryTreeOne(QTreeWidgetItem *p_parent, int depth)
{
Q_ASSERT(parent.childCount() == 0);
// Going deep enough
Q_ASSERT(p_parent->childCount() == 0);
if (depth <= 0) {
return;
}
qDebug() << "update directory" << relativePath;
QString path(QDir::cleanPath(treePath + QDir::separator() + relativePath));
if (!validatePath(path)) {
qDebug() << "invalide notebook directory:" << path;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory."),
QMessageBox::Ok, this);
msgBox.setInformativeText(QString("Notebook directory \"%1\" either does not exist or is not a valid notebook directory.")
.arg(path));
msgBox.exec();
VDirectory *dir = getVDirectory(p_parent);
if (!dir->open()) {
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Failed to open directory %1").arg(dir->getName()), "",
QMessageBox::Ok, QMessageBox::Ok, this);
return;
}
const QVector<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);
if (configJson.isEmpty()) {
qDebug() << "invalid notebook configuration for directory:" << path;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory configuration."),
QMessageBox::Ok, this);
msgBox.setInformativeText(QString("Notebook directory \"%1\" does not contain a valid configuration file.")
.arg(path));
msgBox.exec();
return;
}
fillTreeItem(*item, subDir->getName(), subDir,
QIcon(":/resources/icons/dir_item.svg"));
// Handle sub_directories section
QJsonArray dirJson = configJson["sub_directories"].toArray();
QTreeWidgetItem *preItem = NULL;
for (int i = 0; i < dirJson.size(); ++i) {
QJsonObject dirItem = dirJson[i].toObject();
QTreeWidgetItem *treeItem = insertDirectoryTreeItem(&parent, preItem, dirItem);
preItem = treeItem;
// Update its sub-directory recursively
updateDirectoryTreeOne(*treeItem, QDir::cleanPath(QDir(relativePath).filePath(dirItem["name"].toString())), depth - 1);
updateDirectoryTreeOne(item, depth - 1);
}
}
QString VDirectoryTree::calculateItemRelativePath(QTreeWidgetItem *item)
// Update @p_item's children items
void VDirectoryTree::updateChildren(QTreeWidgetItem *p_item)
{
if (!item) {
return ".";
}
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
Q_ASSERT(!itemJson.isEmpty());
QString name = itemJson["name"].toString();
Q_ASSERT(!name.isEmpty());
return QDir::cleanPath(calculateItemRelativePath(item->parent()) +
QDir::separator() + name);
}
void VDirectoryTree::updateItemSubtree(QTreeWidgetItem *item)
{
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
Q_ASSERT(!itemJson.isEmpty());
QString relativePath = calculateItemRelativePath(item);
int nrChild = item->childCount();
Q_ASSERT(p_item);
int nrChild = p_item->childCount();
if (nrChild == 0) {
updateDirectoryTreeOne(*item, relativePath, 2);
return;
}
for (int i = 0; i < nrChild; ++i) {
QTreeWidgetItem *childItem = p_item->child(i);
if (childItem->childCount() > 0) {
continue;
}
updateDirectoryTreeOne(childItem, 1);
}
}
QVector<QTreeWidgetItem *> VDirectoryTree::updateItemChildrenAdded(QTreeWidgetItem *p_item)
{
QVector<QTreeWidgetItem *> ret;
QPointer<VDirectory> parentDir;
if (p_item) {
parentDir = getVDirectory(p_item);
} else {
for (int i = 0; i < nrChild; ++i) {
QTreeWidgetItem *childItem = item->child(i);
if (childItem->childCount() > 0) {
continue;
parentDir = m_notebook->getRootDir();
}
const QVector<VDirectory *> &subDirs = parentDir->getSubDirs();
for (int i = 0; i < subDirs.size(); ++i) {
int nrChild;
if (p_item) {
nrChild = p_item->childCount();
} else {
nrChild = this->topLevelItemCount();
}
VDirectory *dir = subDirs[i];
QTreeWidgetItem *item;
if (i >= nrChild) {
if (p_item) {
item = new QTreeWidgetItem(p_item);
} else {
item = new QTreeWidgetItem(this);
}
fillTreeItem(*item, dir->getName(), dir, QIcon(":/resources/icons/dir_item.svg"));
updateDirectoryTreeOne(item, 1);
ret.append(item);
} else {
VDirectory *itemDir;
if (p_item) {
itemDir = getVDirectory(p_item->child(i));
} else {
itemDir = getVDirectory(topLevelItem(i));
}
if (itemDir != dir) {
item = new QTreeWidgetItem();
if (p_item) {
p_item->insertChild(i, item);
} else {
this->insertTopLevelItem(i, item);
}
fillTreeItem(*item, dir->getName(), dir, QIcon(":/resources/icons/dir_item.svg"));
updateDirectoryTreeOne(item, 1);
ret.append(item);
}
QJsonObject childJson = childItem->data(0, Qt::UserRole).toJsonObject();
Q_ASSERT(!childJson.isEmpty());
updateDirectoryTreeOne(*childItem,
QDir::cleanPath(QDir(relativePath).filePath(childJson["name"].toString())), 1);
}
}
qDebug() << ret.size() << "items added";
return ret;
}
void VDirectoryTree::contextMenuRequested(QPoint pos)
{
if (!m_notebook) {
return;
}
QTreeWidgetItem *item = itemAt(pos);
QMenu menu(this);
@ -265,7 +213,6 @@ void VDirectoryTree::contextMenuRequested(QPoint pos)
if (item->parent()) {
// Low-level item
menu.addAction(newSubDirAct);
menu.addAction(newSiblingDirAct);
} else {
// Top-level item
menu.addAction(newRootDirAct);
@ -277,60 +224,40 @@ void VDirectoryTree::contextMenuRequested(QPoint pos)
menu.exec(mapToGlobal(pos));
}
void VDirectoryTree::newSiblingDirectory()
{
QTreeWidgetItem *parentItem = currentItem()->parent();
Q_ASSERT(parentItem);
QJsonObject parentItemJson = parentItem->data(0, Qt::UserRole).toJsonObject();
QString parentItemName = parentItemJson["name"].toString();
QString text("&Directory name:");
QString defaultText("new_directory");
do {
VNewDirDialog dialog(QString("Create a new directory under %1").arg(parentItemName), text,
defaultText, this);
if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput();
if (isConflictNameWithChildren(parentItem, name)) {
text = "Name already exists.\nPlease choose another name:";
defaultText = name;
continue;
}
QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(parentItem, name);
if (newItem) {
this->setCurrentItem(newItem);
}
}
break;
} while (true);
}
void VDirectoryTree::newSubDirectory()
{
if (!m_notebook) {
return;
}
QTreeWidgetItem *curItem = currentItem();
if (!curItem) {
return;
}
QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject();
QString curItemName = curItemJson["name"].toString();
VDirectory *curDir = getVDirectory(curItem);
QString info = QString("Create sub-directory under %1.").arg(curDir->getName());
QString text("&Directory name:");
QString defaultText("new_directory");
do {
VNewDirDialog dialog(QString("Create a new directory under %1").arg(curItemName), text,
defaultText, this);
VNewDirDialog dialog(tr("Create directory"), info, text, defaultText, this);
if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput();
if (isConflictNameWithChildren(curItem, name)) {
text = "Name already exists.\nPlease choose another name:";
if (curDir->findSubDirectory(name)) {
info = QString("Name already exists under %1.\nPlease choose another name.").arg(curDir->getName());
defaultText = name;
continue;
}
QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(curItem, name);
if (newItem) {
this->setCurrentItem(newItem);
VDirectory *subDir = curDir->createSubDirectory(name);
if (!subDir) {
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Failed to create directory %1.").arg(name), "",
QMessageBox::Ok, QMessageBox::Ok, this);
return;
}
QVector<QTreeWidgetItem *> items = updateItemChildrenAdded(curItem);
Q_ASSERT(items.size() == 1);
setCurrentItem(items[0]);
}
break;
} while (true);
@ -338,23 +265,32 @@ void VDirectoryTree::newSubDirectory()
void VDirectoryTree::newRootDirectory()
{
if (!m_notebook) {
return;
}
QString info = QString("Create root directory in notebook %1.").arg(m_notebook->getName());
QString text("&Directory name:");
QString defaultText("new_directory");
VDirectory *rootDir = m_notebook->getRootDir();
do {
VNewDirDialog dialog(tr("Create a new root directory"), text,
defaultText, this);
VNewDirDialog dialog(tr("Create root directory"), info, text, defaultText, this);
if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput();
if (isConflictNameWithChildren(NULL, name)) {
text = "Name already exists.\nPlease choose another name:";
if (rootDir->findSubDirectory(name)) {
info = QString("Name already exists in notebook %1.\nPlease choose another name.").arg(m_notebook->getName());
defaultText = name;
continue;
}
QTreeWidgetItem *newItem = createDirectoryAndUpdateTree(NULL, name);
if (newItem) {
this->setCurrentItem(newItem);
VDirectory *subDir = rootDir->createSubDirectory(name);
if (!subDir) {
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Failed to create directory %1.").arg(name), "",
QMessageBox::Ok, QMessageBox::Ok, this);
return;
}
QVector<QTreeWidgetItem *> items = updateItemChildrenAdded(NULL);
Q_ASSERT(items.size() == 1);
setCurrentItem(items[0]);
}
break;
} while (true);
@ -366,140 +302,26 @@ void VDirectoryTree::deleteDirectory()
if (!curItem) {
return;
}
QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject();
QString curItemName = curItemJson["name"].toString();
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Are you sure you want to delete directory \"%1\"?")
.arg(curItemName), QMessageBox::Ok | QMessageBox::Cancel, this);
msgBox.setInformativeText(tr("This will delete any files under this directory."));
msgBox.setDefaultButton(QMessageBox::Cancel);
if (msgBox.exec() == QMessageBox::Ok) {
deleteDirectoryAndUpdateTree(curItem);
VDirectory *curDir = getVDirectory(curItem);
int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Are you sure to delete directory %1?").arg(curDir->getName()),
tr("This will delete any files under this directory."), QMessageBox::Ok | QMessageBox::Cancel,
QMessageBox::Ok, this);
if (ret == QMessageBox::Ok) {
VDirectory *parentDir = curDir->getParentDirectory();
Q_ASSERT(parentDir);
parentDir->deleteSubDirectory(curDir);
delete curItem;
}
}
QTreeWidgetItem* VDirectoryTree::createDirectoryAndUpdateTree(QTreeWidgetItem *parent,
const QString &name)
{
QString relativePath = calculateItemRelativePath(parent);
QString path = QDir::cleanPath(QDir(treePath).filePath(relativePath));
QDir dir(path);
if (!dir.mkdir(name)) {
qWarning() << "error: fail to create directory" << name << "under" << path;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Could not create directory \"%1\" under \"%2\".")
.arg(name).arg(path), QMessageBox::Ok, this);
msgBox.setInformativeText(QString("Please check if there already exists a directory named \"%1\".").arg(name));
msgBox.exec();
return NULL;
}
QJsonObject configJson;
configJson["version"] = "1";
configJson["sub_directories"] = QJsonArray();
configJson["files"] = QJsonArray();
if (!VConfigManager::writeDirectoryConfig(QDir::cleanPath(QDir(path).filePath(name)), configJson)) {
return NULL;
}
// Update parent's config file to include this new directory
configJson = VConfigManager::readDirectoryConfig(path);
Q_ASSERT(!configJson.isEmpty());
QJsonObject itemJson;
itemJson["name"] = name;
QJsonArray subDirArray = configJson["sub_directories"].toArray();
subDirArray.append(itemJson);
configJson["sub_directories"] = subDirArray;
if (!VConfigManager::writeDirectoryConfig(path, configJson)) {
VConfigManager::deleteDirectoryConfig(QDir::cleanPath(QDir(path).filePath(name)));
dir.rmdir(name);
return NULL;
}
return insertDirectoryTreeItem(parent, NULL, itemJson);
}
void VDirectoryTree::deleteDirectoryAndUpdateTree(QTreeWidgetItem *item)
{
if (!item) {
return;
}
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
QString itemName = itemJson["name"].toString();
QString parentRelativePath = calculateItemRelativePath(item->parent());
// Update parent's config file to exclude this directory
QString path = QDir::cleanPath(QDir(treePath).filePath(parentRelativePath));
QJsonObject configJson = VConfigManager::readDirectoryConfig(path);
Q_ASSERT(!configJson.isEmpty());
QJsonArray subDirArray = configJson["sub_directories"].toArray();
bool deleted = false;
for (int i = 0; i < subDirArray.size(); ++i) {
QJsonObject ele = subDirArray[i].toObject();
if (ele["name"].toString() == itemName) {
subDirArray.removeAt(i);
deleted = true;
break;
}
}
if (!deleted) {
qWarning() << "error: fail to find" << itemName << "to delete in its parent's configuration file";
return;
}
configJson["sub_directories"] = subDirArray;
if (!VConfigManager::writeDirectoryConfig(path, configJson)) {
qWarning() << "error: fail to update parent's configuration file to delete" << itemName;
return;
}
// Delete the entire directory
QString dirName = QDir::cleanPath(QDir(path).filePath(itemName));
QDir dir(dirName);
if (!dir.removeRecursively()) {
qWarning() << "error: fail to delete" << dirName << "recursively";
} else {
qDebug() << "delete" << dirName << "recursively";
}
// Update the tree
removeDirectoryTreeItem(item);
}
bool VDirectoryTree::isConflictNameWithChildren(const QTreeWidgetItem *parent, const QString &name)
{
if (parent) {
int nrChild = parent->childCount();
for (int i = 0; i < nrChild; ++i) {
QJsonObject childItemJson = parent->child(i)->data(0, Qt::UserRole).toJsonObject();
Q_ASSERT(!childItemJson.isEmpty());
if (childItemJson["name"].toString() == name) {
return true;
}
}
} else {
int nrTopLevelItems = topLevelItemCount();
for (int i = 0; i < nrTopLevelItems; ++i) {
QJsonObject itemJson = topLevelItem(i)->data(0, Qt::UserRole).toJsonObject();
Q_ASSERT(!itemJson.isEmpty());
if (itemJson["name"].toString() == name) {
return true;
}
}
}
return false;
}
void VDirectoryTree::currentDirectoryItemChanged(QTreeWidgetItem *currentItem)
{
if (!currentItem) {
emit currentDirectoryChanged(QJsonObject());
emit currentDirectoryChanged(NULL);
return;
}
QJsonObject itemJson = currentItem->data(0, Qt::UserRole).toJsonObject();
Q_ASSERT(!itemJson.isEmpty());
itemJson["notebook"] = notebook;
itemJson["relative_path"] = calculateItemRelativePath(currentItem);
emit currentDirectoryChanged(itemJson);
emit currentDirectoryChanged(getVDirectory(currentItem));
}
void VDirectoryTree::editDirectoryInfo()
@ -508,80 +330,33 @@ void VDirectoryTree::editDirectoryInfo()
if (!curItem) {
return;
}
QJsonObject curItemJson = curItem->data(0, Qt::UserRole).toJsonObject();
QString curItemName = curItemJson["name"].toString();
VDirectory *curDir = getVDirectory(curItem);
VDirectory *parentDir = curDir->getParentDirectory();
QString curName = curDir->getName();
QString info;
QString defaultName = curItemName;
QString defaultName = curName;
do {
VDirInfoDialog dialog(tr("Directory Information"), info, defaultName, this);
if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.getNameInput();
if (name == curItemName) {
if (name == curName) {
return;
}
if (isConflictNameWithChildren(curItem->parent(), name)) {
info = "Name already exists.\nPlease choose another name:";
if (parentDir->findSubDirectory(name)) {
info = "Name already exists.\nPlease choose another name.";
defaultName = name;
continue;
}
renameDirectory(curItem, name);
if (!curDir->rename(name)) {
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
QString("Failed to rename directory %1.").arg(curName), "",
QMessageBox::Ok, QMessageBox::Ok, this);
return;
}
curItem->setText(0, name);
}
break;
} while (true);
}
void VDirectoryTree::renameDirectory(QTreeWidgetItem *item, const QString &newName)
{
if (!item) {
return;
}
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
QString name = itemJson["name"].toString();
QTreeWidgetItem *parent = item->parent();
QString parentRelativePath = calculateItemRelativePath(parent);
QString path = QDir::cleanPath(QDir(treePath).filePath(parentRelativePath));
QDir dir(path);
if (!dir.rename(name, newName)) {
qWarning() << "error: fail to rename directory" << name << "under" << path;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Could not rename directory \"%1\" under \"%2\".")
.arg(name).arg(path), QMessageBox::Ok, this);
msgBox.setInformativeText(QString("Please check if there already exists a directory named \"%1\".").arg(name));
msgBox.exec();
return;
}
// Update parent's config file
QJsonObject configJson = VConfigManager::readDirectoryConfig(path);
Q_ASSERT(!configJson.isEmpty());
QJsonArray subDirArray = configJson["sub_directories"].toArray();
int index = 0;
for (index = 0; index < subDirArray.size(); ++index) {
QJsonObject tmp = subDirArray[index].toObject();
if (tmp["name"].toString() == name) {
tmp["name"] = newName;
subDirArray[index] = tmp;
break;
}
}
Q_ASSERT(index != subDirArray.size());
configJson["sub_directories"] = subDirArray;
if (!VConfigManager::writeDirectoryConfig(path, configJson)) {
dir.rename(newName, name);
return;
}
// Update item
itemJson["name"] = newName;
item->setData(0, Qt::UserRole, itemJson);
item->setText(0, newName);
QString oldPath = QDir::cleanPath(QDir(parentRelativePath).filePath(name));
QString newPath = QDir::cleanPath(QDir(parentRelativePath).filePath(newName));
qDebug() << "directory renamed" << oldPath << "to" << newPath;
emit directoryRenamed(notebook, oldPath, newPath);
}

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@
#include "vedittab.h"
#include "vnote.h"
#include "vconfigmanager.h"
#include "vfile.h"
VEditArea::VEditArea(VNote *vnote, QWidget *parent)
: QWidget(parent), vnote(vnote), curWindowIndex(0)
@ -79,25 +80,17 @@ void VEditArea::removeSplitWindow(VEditWindow *win)
// A given file can be opened in multiple split windows. A given file could be
// opened at most in one tab inside a window.
void VEditArea::openFile(QJsonObject fileJson)
void VEditArea::openFile(VFile *p_file, OpenFileMode p_mode)
{
if (fileJson.isEmpty()) {
if (!p_file) {
return;
}
QString notebook = fileJson["notebook"].toString();
QString relativePath = fileJson["relative_path"].toString();
int mode = OpenFileMode::Read;
if (fileJson.contains("mode")) {
mode = fileJson["mode"].toInt();
}
qDebug() << "open notebook" << notebook << "path" << relativePath << mode;
qDebug() << "VEditArea open" << p_file->getName() << (int)p_mode;
// Find if it has been opened already
int winIdx, tabIdx;
bool setFocus = false;
auto tabs = findTabsByFile(notebook, relativePath);
auto tabs = findTabsByFile(p_file);
if (!tabs.empty()) {
// Current window first
winIdx = tabs[0].first;
@ -115,19 +108,19 @@ void VEditArea::openFile(QJsonObject fileJson)
// Open it in current window
winIdx = curWindowIndex;
tabIdx = openFileInWindow(winIdx, notebook, relativePath, mode);
tabIdx = openFileInWindow(winIdx, p_file, p_mode);
out:
setCurrentTab(winIdx, tabIdx, setFocus);
}
QVector<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;
int nrWin = splitter->count();
for (int winIdx = 0; winIdx < nrWin; ++winIdx) {
VEditWindow *win = getWindow(winIdx);
int tabIdx = win->findTabByFile(notebook, relativePath);
int tabIdx = win->findTabByFile(p_file);
if (tabIdx != -1) {
QPair<int, int> match;
match.first = winIdx;
@ -138,12 +131,11 @@ QVector<QPair<int, int> > VEditArea::findTabsByFile(const QString &notebook, con
return tabs;
}
int VEditArea::openFileInWindow(int windowIndex, const QString &notebook, const QString &relativePath,
int mode)
int VEditArea::openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode)
{
Q_ASSERT(windowIndex < splitter->count());
VEditWindow *win = getWindow(windowIndex);
return win->openFile(notebook, relativePath, mode);
return win->openFile(p_file, p_mode);
}
void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus)
@ -178,21 +170,18 @@ void VEditArea::updateWindowStatus()
win->requestUpdateCurHeader();
}
bool VEditArea::closeFile(QJsonObject fileJson)
bool VEditArea::closeFile(const VFile *p_file, bool p_forced)
{
if (fileJson.isEmpty()) {
if (!p_file) {
return true;
}
QString notebook = fileJson["notebook"].toString();
QString relativePath = fileJson["relative_path"].toString();
bool isForced = fileJson["is_forced"].toBool();
int nrWin = splitter->count();
bool ret = false;
for (int i = 0; i < nrWin; ++i) {
VEditWindow *win = getWindow(i);
ret = ret || win->closeFile(notebook, relativePath, isForced);
ret = ret || win->closeFile(p_file, p_forced);
}
updateWindowStatus();
return ret;
}
@ -206,6 +195,7 @@ bool VEditArea::closeAllFiles(bool p_forced)
return false;
}
}
updateWindowStatus();
return true;
}
@ -233,29 +223,6 @@ void VEditArea::saveAndReadFile()
win->saveAndReadFile();
}
void VEditArea::handleDirectoryRenamed(const QString &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)
{
if (!curWindow) {
@ -287,7 +254,6 @@ void VEditArea::handleRemoveSplitRequest(VEditWindow *curWindow)
void VEditArea::mousePressEvent(QMouseEvent *event)
{
return;
qDebug() << "VEditArea press event" << event;
QPoint pos = event->pos();
int nrWin = splitter->count();
@ -335,7 +301,15 @@ void VEditArea::handleOutlineItemActivated(const VAnchor &anchor)
getWindow(curWindowIndex)->scrollCurTab(anchor);
}
bool VEditArea::isFileOpened(const QString &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"
class VNote;
class VFile;
class VEditArea : public QWidget
{
Q_OBJECT
public:
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);
signals:
void curTabStatusChanged(const QString &notebook, const QString &relativePath,
bool editMode, bool modifiable, bool modified);
void curTabStatusChanged(const VFile *p_file, bool p_editMode);
void outlineChanged(const VToc &toc);
void curHeaderChanged(const VAnchor &anchor);
@ -34,17 +34,14 @@ protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
public slots:
void openFile(QJsonObject fileJson);
bool closeFile(QJsonObject fileJson);
void openFile(VFile *p_file, OpenFileMode p_mode);
bool closeFile(const VFile *p_file, bool p_forced);
void editFile();
void saveFile();
void readFile();
void saveAndReadFile();
void handleOutlineItemActivated(const VAnchor &anchor);
void handleDirectoryRenamed(const QString &notebook,
const QString &oldRelativePath, const QString &newRelativePath);
void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &p_destNotebook, const QString &p_destRelativePath);
void handleFileUpdated(const VFile *p_file);
private slots:
void handleSplitWindowRequest(VEditWindow *curWindow);
@ -55,9 +52,8 @@ private slots:
private:
void setupUI();
QVector<QPair<int, int> > findTabsByFile(const QString &notebook, const QString &relativePath);
int openFileInWindow(int windowIndex, const QString &notebook, const QString &relativePath,
int mode);
QVector<QPair<int, int> > findTabsByFile(const VFile *p_file);
int openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode);
void setCurrentTab(int windowIndex, int tabIndex, bool setFocus);
void setCurrentWindow(int windowIndex, bool setFocus);
inline VEditWindow *getWindow(int windowIndex) const;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,14 +1,16 @@
#include "vnotebook.h"
VNotebook::VNotebook(QObject *parent)
: QObject(parent)
{
}
#include "vdirectory.h"
#include "utils/vutils.h"
VNotebook::VNotebook(const QString &name, const QString &path, QObject *parent)
: QObject(parent), m_name(name), m_path(path)
{
m_rootDir = new VDirectory(this, VUtils::directoryNameFromPath(path));
}
VNotebook::~VNotebook()
{
delete m_rootDir;
}
QString VNotebook::getName() const
@ -33,5 +35,10 @@ void VNotebook::setPath(const QString &path)
void VNotebook::close(bool p_forced)
{
//TODO
m_rootDir->close();
}
bool VNotebook::open()
{
return m_rootDir->open();
}

View File

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

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