mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-04 21:39:52 +08:00
hard days for VNoteX project
Never say "refactor" again!!!
This commit is contained in:
parent
05d89745c1
commit
52702a32e9
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
vnotex.pro.user
|
||||
vnotex.pro.user.*
|
||||
.ccls
|
||||
compile_commands.json
|
||||
compile_flags.txt
|
||||
*.plist
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "libs/vtextedit"]
|
||||
path = libs/vtextedit
|
||||
url = https://github.com/vnotex/vtextedit.git
|
33
coc_update.cmd
Normal file
33
coc_update.cmd
Normal file
@ -0,0 +1,33 @@
|
||||
@echo off
|
||||
rem Update .ccls project file for ccls LPS and compile_flags.txt for clangd
|
||||
|
||||
if "%~1"=="" (
|
||||
echo missing argument: the location of Qt's include directory
|
||||
EXIT /B 0
|
||||
)
|
||||
|
||||
set qt_inc=%~1
|
||||
set qt_inc=%qt_inc:\=\\%
|
||||
|
||||
(
|
||||
echo clang
|
||||
echo -fcxx-exceptions
|
||||
echo -std=c++14
|
||||
echo -Isrc\\core
|
||||
echo -Isrc
|
||||
echo -Ilibs\\vtextedit\\src\\editor\\include
|
||||
echo -Ilibs\\vtitlebar\\src
|
||||
echo -I%qt_inc%
|
||||
echo -I%qt_inc%\\QtCore
|
||||
echo -I%qt_inc%\\QtWebEngineWidgets
|
||||
echo -I%qt_inc%\\QtSvg
|
||||
echo -I%qt_inc%\\QtPrintSupport
|
||||
echo -I%qt_inc%\\QtWidgets
|
||||
echo -I%qt_inc%\\QtWebEngineCore
|
||||
echo -I%qt_inc%\\QtGui
|
||||
echo -I%qt_inc%\\QtWebChannel
|
||||
echo -I%qt_inc%\\QtNetwork
|
||||
echo -I%qt_inc%\\QtTest
|
||||
) > ".ccls"
|
||||
|
||||
copy /Y .ccls compile_flags.txt
|
5
libs/libs.pro
Normal file
5
libs/libs.pro
Normal file
@ -0,0 +1,5 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += \
|
||||
vtextedit \
|
||||
vtitlebar
|
1
libs/vtextedit
Submodule
1
libs/vtextedit
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a75c9b8dd374dd4fed1cfd66b79408ecfea990c9
|
22
libs/vtitlebar/src/vtitlebar.cpp
Normal file
22
libs/vtitlebar/src/vtitlebar.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "vtitlebar.h"
|
||||
|
||||
#include <QStyleOption>
|
||||
#include <QPainter>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
VTitleBar::VTitleBar(QWidget *p_parent)
|
||||
: QWidget(p_parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void VTitleBar::paintEvent(QPaintEvent *p_event)
|
||||
{
|
||||
QStyleOption opt;
|
||||
opt.init(this);
|
||||
QPainter p(this);
|
||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||
|
||||
QWidget::paintEvent(p_event);
|
||||
}
|
19
libs/vtitlebar/src/vtitlebar.h
Normal file
19
libs/vtitlebar/src/vtitlebar.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef VTITLEBAR_H
|
||||
#define VTITLEBAR_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class VTitleBar : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VTitleBar(QWidget *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *p_event) Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // VTITLEBAR_H
|
118
libs/vtitlebar/src/vtoolbar.cpp
Normal file
118
libs/vtitlebar/src/vtoolbar.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "vtoolbar.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMouseEvent>
|
||||
#include <QCoreApplication>
|
||||
#include <QToolButton>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
VToolBar::VToolBar(QWidget *p_parent)
|
||||
: QToolBar(p_parent),
|
||||
m_window(p_parent)
|
||||
{
|
||||
setupUI();
|
||||
m_window->installEventFilter(this);
|
||||
}
|
||||
|
||||
VToolBar::VToolBar(const QString &p_title, QWidget *p_parent)
|
||||
: QToolBar(p_title, p_parent),
|
||||
m_window(p_parent)
|
||||
{
|
||||
setupUI();
|
||||
m_window->installEventFilter(this);
|
||||
}
|
||||
|
||||
void VToolBar::setupUI()
|
||||
{
|
||||
}
|
||||
|
||||
void VToolBar::mousePressEvent(QMouseEvent *p_event)
|
||||
{
|
||||
QToolBar::mousePressEvent(p_event);
|
||||
m_lastPos = p_event->pos();
|
||||
}
|
||||
|
||||
void VToolBar::mouseDoubleClickEvent(QMouseEvent *p_event)
|
||||
{
|
||||
QToolBar::mouseDoubleClickEvent(p_event);
|
||||
m_ignoreNextMove = true;
|
||||
maximizeRestoreWindow();
|
||||
}
|
||||
|
||||
void VToolBar::maximizeRestoreWindow()
|
||||
{
|
||||
m_window->isMaximized() ? m_window->showNormal() : m_window->showMaximized();
|
||||
}
|
||||
|
||||
void VToolBar::mouseMoveEvent(QMouseEvent *p_event)
|
||||
{
|
||||
auto delta = p_event->pos() - m_lastPos;
|
||||
if (!m_ignoreNextMove && !m_lastPos.isNull() && (qAbs(delta.x()) > 10 || qAbs(delta.y()) > 10)) {
|
||||
if (m_window->isMaximized()) {
|
||||
m_window->showNormal();
|
||||
} else {
|
||||
m_window->move(p_event->globalPos() - m_lastPos);
|
||||
}
|
||||
}
|
||||
QToolBar::mouseMoveEvent(p_event);
|
||||
}
|
||||
|
||||
void VToolBar::mouseReleaseEvent(QMouseEvent *p_event)
|
||||
{
|
||||
QToolBar::mouseReleaseEvent(p_event);
|
||||
m_ignoreNextMove = false;
|
||||
m_lastPos = QPoint();
|
||||
}
|
||||
|
||||
void VToolBar::addTitleBarIcons(const QIcon &p_minimizeIcon,
|
||||
const QIcon &p_maximizeIcon,
|
||||
const QIcon &p_restoreIcon,
|
||||
const QIcon &p_closeIcon)
|
||||
{
|
||||
addSeparator();
|
||||
|
||||
addAction(p_minimizeIcon, tr("Minimize"),
|
||||
this, [this]() {
|
||||
m_window->showMinimized();
|
||||
});
|
||||
|
||||
m_maximizeIcon = p_maximizeIcon;
|
||||
m_restoreIcon = p_restoreIcon;
|
||||
m_maximizeAct = addAction(p_maximizeIcon, tr("Maximize"),
|
||||
this, [this]() {
|
||||
maximizeRestoreWindow();
|
||||
});
|
||||
|
||||
{
|
||||
auto closeAct = addAction(p_closeIcon, tr("Close"),
|
||||
this, [this]() {
|
||||
m_window->close();
|
||||
});
|
||||
auto btn = static_cast<QToolButton *>(widgetForAction(closeAct));
|
||||
btn->setProperty("DangerousButton", true);
|
||||
}
|
||||
|
||||
updateMaximizeAct();
|
||||
}
|
||||
|
||||
bool VToolBar::eventFilter(QObject *p_obj, QEvent *p_event)
|
||||
{
|
||||
if (p_obj == m_window) {
|
||||
if (p_event->type() == QEvent::WindowStateChange) {
|
||||
updateMaximizeAct();
|
||||
}
|
||||
}
|
||||
return QToolBar::eventFilter(p_obj, p_event);
|
||||
}
|
||||
|
||||
void VToolBar::updateMaximizeAct()
|
||||
{
|
||||
if (m_window->isMaximized()) {
|
||||
m_maximizeAct->setIcon(m_restoreIcon);
|
||||
m_maximizeAct->setText(tr("Restore Down"));
|
||||
} else {
|
||||
m_maximizeAct->setIcon(m_maximizeIcon);
|
||||
m_maximizeAct->setText(tr("Maximize"));
|
||||
}
|
||||
}
|
54
libs/vtitlebar/src/vtoolbar.h
Normal file
54
libs/vtitlebar/src/vtoolbar.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef VTOOLBAR_H
|
||||
#define VTOOLBAR_H
|
||||
|
||||
#include <QToolBar>
|
||||
#include <QIcon>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class VToolBar : public QToolBar
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VToolBar(QWidget *p_parent = nullptr);
|
||||
|
||||
VToolBar(const QString &p_title, QWidget *p_parent = nullptr);
|
||||
|
||||
void addTitleBarIcons(const QIcon &p_minimizeIcon,
|
||||
const QIcon &p_maximizeIcon,
|
||||
const QIcon &p_restoreIcon,
|
||||
const QIcon &p_closeIcon);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
void mouseReleaseEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
void mouseDoubleClickEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
void maximizeRestoreWindow();
|
||||
|
||||
void updateMaximizeAct();
|
||||
|
||||
QPoint m_lastPos;
|
||||
|
||||
bool m_ignoreNextMove = false;
|
||||
|
||||
QWidget *m_window = nullptr;
|
||||
|
||||
QAction *m_maximizeAct = nullptr;
|
||||
|
||||
QIcon m_maximizeIcon;
|
||||
|
||||
QIcon m_restoreIcon;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // VTOOLBAR_H
|
16
libs/vtitlebar/vtitlebar.pro
Normal file
16
libs/vtitlebar/vtitlebar.pro
Normal file
@ -0,0 +1,16 @@
|
||||
QT += core gui widgets
|
||||
|
||||
TARGET = vtitlebar
|
||||
|
||||
TEMPLATE = lib
|
||||
|
||||
# CONFIG += warn_off
|
||||
CONFIG += staticlib
|
||||
|
||||
SOURCES += \
|
||||
src/vtitlebar.cpp \
|
||||
src/vtoolbar.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/vtitlebar.h \
|
||||
src/vtoolbar.h
|
17
libs/vtitlebar/vtitlebar_export.pri
Normal file
17
libs/vtitlebar/vtitlebar_export.pri
Normal file
@ -0,0 +1,17 @@
|
||||
INCLUDEPATH *= $$PWD/src
|
||||
|
||||
DEPENDPATH *= $$PWD/src
|
||||
|
||||
OUT_FOLDER = $$absolute_path($$relative_path($$PWD, $$_PRO_FILE_PWD_), $$OUT_PWD)
|
||||
win32:CONFIG(release, debug|release) {
|
||||
LIBS += $$OUT_FOLDER/release/vtitlebar.lib
|
||||
# For static library, we need to add this depends to let Qt re-build the target
|
||||
# when there is a change in the library.
|
||||
PRE_TARGETDEPS += $$OUT_FOLDER/release/vtitlebar.lib
|
||||
} else:win32:CONFIG(debug, debug|release) {
|
||||
LIBS += $$OUT_FOLDER/debug/vtitlebar.lib
|
||||
PRE_TARGETDEPS += $$OUT_FOLDER/debug/vtitlebar.lib
|
||||
} else:unix {
|
||||
LIBS += $$OUT_FOLDER/libvtitlebar.a
|
||||
PRE_TARGETDEPS += $$OUT_FOLDER/libvtitlebar.a
|
||||
}
|
552
src/core/buffer/buffer.cpp
Normal file
552
src/core/buffer/buffer.cpp
Normal file
@ -0,0 +1,552 @@
|
||||
#include "buffer.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include <notebook/node.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <widgets/viewwindow.h>
|
||||
#include <utils/pathutils.h>
|
||||
|
||||
#include <core/configmgr.h>
|
||||
#include <core/editorconfig.h>
|
||||
|
||||
#include "bufferprovider.h"
|
||||
#include "exception.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
static vnotex::ID generateBufferID()
|
||||
{
|
||||
static vnotex::ID id = 0;
|
||||
return ++id;
|
||||
}
|
||||
|
||||
Buffer::Buffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_provider(p_parameters.m_provider),
|
||||
c_id(generateBufferID()),
|
||||
m_readOnly(m_provider->isReadOnly())
|
||||
{
|
||||
m_autoSaveTimer = new QTimer(this);
|
||||
m_autoSaveTimer->setSingleShot(true);
|
||||
m_autoSaveTimer->setInterval(1000);
|
||||
connect(m_autoSaveTimer, &QTimer::timeout,
|
||||
this, &Buffer::autoSave);
|
||||
|
||||
readContent();
|
||||
|
||||
checkBackupFileOfPreviousSession();
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
Q_ASSERT(m_attachedViewWindowCount == 0);
|
||||
Q_ASSERT(!m_viewWindowToSync);
|
||||
Q_ASSERT(!isModified());
|
||||
Q_ASSERT(m_backupFilePath.isEmpty());
|
||||
}
|
||||
|
||||
int Buffer::getAttachViewWindowCount() const
|
||||
{
|
||||
return m_attachedViewWindowCount;
|
||||
}
|
||||
|
||||
void Buffer::attachViewWindow(ViewWindow *p_win)
|
||||
{
|
||||
Q_UNUSED(p_win);
|
||||
Q_ASSERT(!(m_state & StateFlag::Discarded));
|
||||
++m_attachedViewWindowCount;
|
||||
}
|
||||
|
||||
void Buffer::detachViewWindow(ViewWindow *p_win)
|
||||
{
|
||||
Q_ASSERT(p_win != m_viewWindowToSync);
|
||||
|
||||
--m_attachedViewWindowCount;
|
||||
Q_ASSERT(m_attachedViewWindowCount >= 0);
|
||||
|
||||
if (m_attachedViewWindowCount == 0) {
|
||||
emit attachedViewWindowEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
ViewWindow *Buffer::createViewWindow(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent)
|
||||
{
|
||||
auto window = createViewWindowInternal(p_paras, p_parent);
|
||||
Q_ASSERT(window);
|
||||
window->attachToBuffer(this);
|
||||
return window;
|
||||
}
|
||||
|
||||
bool Buffer::match(const Node *p_node) const
|
||||
{
|
||||
Q_ASSERT(p_node);
|
||||
return m_provider->match(p_node);
|
||||
}
|
||||
|
||||
bool Buffer::match(const QString &p_filePath) const
|
||||
{
|
||||
return m_provider->match(p_filePath);
|
||||
}
|
||||
|
||||
QString Buffer::getName() const
|
||||
{
|
||||
return m_provider->getName();
|
||||
}
|
||||
|
||||
QString Buffer::getPath() const
|
||||
{
|
||||
return m_provider->getPath();
|
||||
}
|
||||
|
||||
QString Buffer::getContentPath() const
|
||||
{
|
||||
return m_provider->getContentPath();
|
||||
}
|
||||
|
||||
QString Buffer::getContentBasePath() const
|
||||
{
|
||||
return PathUtils::parentDirPath(getContentPath());
|
||||
}
|
||||
|
||||
ID Buffer::getID() const
|
||||
{
|
||||
return c_id;
|
||||
}
|
||||
|
||||
const QString &Buffer::getContent() const
|
||||
{
|
||||
const_cast<Buffer *>(this)->syncContent();
|
||||
return m_content;
|
||||
}
|
||||
|
||||
void Buffer::setContent(const QString &p_content, int &p_revision)
|
||||
{
|
||||
m_viewWindowToSync = nullptr;
|
||||
m_content = p_content;
|
||||
p_revision = ++m_revision;
|
||||
setModified(true);
|
||||
m_autoSaveTimer->start();
|
||||
emit contentsChanged();
|
||||
}
|
||||
|
||||
void Buffer::invalidateContent(const ViewWindow *p_win,
|
||||
const std::function<void(int)> &p_setRevision)
|
||||
{
|
||||
Q_ASSERT(!m_viewWindowToSync || m_viewWindowToSync == p_win);
|
||||
++m_revision;
|
||||
p_setRevision(m_revision);
|
||||
m_viewWindowToSync = p_win;
|
||||
m_autoSaveTimer->start();
|
||||
emit contentsChanged();
|
||||
}
|
||||
|
||||
int Buffer::getRevision() const
|
||||
{
|
||||
return m_revision;
|
||||
}
|
||||
|
||||
void Buffer::syncContent(const ViewWindow *p_win)
|
||||
{
|
||||
if (m_viewWindowToSync == p_win) {
|
||||
syncContent();
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::syncContent()
|
||||
{
|
||||
if (m_viewWindowToSync) {
|
||||
// Need to sync content.
|
||||
m_content = m_viewWindowToSync->getLatestContent();
|
||||
m_viewWindowToSync = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Buffer::isModified() const
|
||||
{
|
||||
return m_modified;
|
||||
}
|
||||
|
||||
void Buffer::setModified(bool p_modified)
|
||||
{
|
||||
if (m_modified == p_modified) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_modified = p_modified;
|
||||
emit modified(m_modified);
|
||||
}
|
||||
|
||||
bool Buffer::isReadOnly() const
|
||||
{
|
||||
return m_readOnly;
|
||||
}
|
||||
|
||||
Buffer::OperationCode Buffer::save(bool p_force)
|
||||
{
|
||||
Q_ASSERT(!m_readOnly);
|
||||
if (m_readOnly) {
|
||||
return OperationCode::Failed;
|
||||
}
|
||||
|
||||
if (m_modified
|
||||
|| p_force
|
||||
|| m_state & (StateFlag::FileMissingOnDisk | StateFlag::FileChangedOutside)) {
|
||||
syncContent();
|
||||
|
||||
// We do not involve user here to handle file missing and changed outside cases.
|
||||
// The active ViewWindow will check this periodically.
|
||||
// Check if file still exists.
|
||||
if (!p_force && !checkFileExistsOnDisk()) {
|
||||
qWarning() << "failed to save buffer due to file missing on disk" << getPath();
|
||||
return OperationCode::FileMissingOnDisk;
|
||||
}
|
||||
|
||||
// Check if file is modified outside.
|
||||
if (!p_force && checkFileChangedOutside()) {
|
||||
qWarning() << "failed to save buffer due to file changed from outside" << getPath();
|
||||
return OperationCode::FileChangedOutside;
|
||||
}
|
||||
|
||||
m_provider->write(m_content);
|
||||
setModified(false);
|
||||
m_state &= ~(StateFlag::FileMissingOnDisk | StateFlag::FileChangedOutside);
|
||||
}
|
||||
return OperationCode::Success;
|
||||
}
|
||||
|
||||
Buffer::OperationCode Buffer::reload()
|
||||
{
|
||||
// Check if file is missing.
|
||||
if (!checkFileExistsOnDisk()) {
|
||||
qWarning() << "failed to save buffer due to file missing on disk" << getPath();
|
||||
return OperationCode::FileMissingOnDisk;
|
||||
}
|
||||
|
||||
if (m_modified
|
||||
|| m_state & (StateFlag::FileMissingOnDisk | StateFlag::FileChangedOutside)) {
|
||||
readContent();
|
||||
|
||||
emit modified(m_modified);
|
||||
emit contentsChanged();
|
||||
}
|
||||
return OperationCode::Success;
|
||||
}
|
||||
|
||||
void Buffer::readContent()
|
||||
{
|
||||
m_content = m_provider->read();
|
||||
++m_revision;
|
||||
|
||||
// Reset state.
|
||||
m_viewWindowToSync = nullptr;
|
||||
m_modified = false;
|
||||
}
|
||||
|
||||
void Buffer::discard()
|
||||
{
|
||||
Q_ASSERT(!(m_state & StateFlag::Discarded));
|
||||
Q_ASSERT(m_attachedViewWindowCount == 1);
|
||||
m_autoSaveTimer->stop();
|
||||
m_content.clear();
|
||||
m_state |= StateFlag::Discarded;
|
||||
++m_revision;
|
||||
|
||||
m_viewWindowToSync = nullptr;
|
||||
m_modified = false;
|
||||
}
|
||||
|
||||
void Buffer::close()
|
||||
{
|
||||
// Delete the backup file if exists.
|
||||
m_autoSaveTimer->stop();
|
||||
if (!m_backupFilePath.isEmpty()) {
|
||||
FileUtils::removeFile(m_backupFilePath);
|
||||
m_backupFilePath.clear();
|
||||
}
|
||||
}
|
||||
|
||||
QString Buffer::getImageFolderPath() const
|
||||
{
|
||||
return const_cast<Buffer *>(this)->m_provider->fetchImageFolderPath();
|
||||
}
|
||||
|
||||
QString Buffer::insertImage(const QString &p_srcImagePath, const QString &p_imageFileName)
|
||||
{
|
||||
Q_UNUSED(p_srcImagePath);
|
||||
Q_UNUSED(p_imageFileName);
|
||||
Q_ASSERT_X(false, "insertImage", "image insert is not supported");
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString Buffer::insertImage(const QImage &p_image, const QString &p_imageFileName)
|
||||
{
|
||||
Q_UNUSED(p_image);
|
||||
Q_UNUSED(p_imageFileName);
|
||||
Q_ASSERT_X(false, "insertImage", "image insert is not supported");
|
||||
return QString();
|
||||
}
|
||||
|
||||
void Buffer::removeImage(const QString &p_imagePath)
|
||||
{
|
||||
Q_UNUSED(p_imagePath);
|
||||
Q_ASSERT_X(false, "removeImage", "image remove is not supported");
|
||||
}
|
||||
|
||||
void Buffer::autoSave()
|
||||
{
|
||||
if (m_readOnly) {
|
||||
m_autoSaveTimer->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state & (StateFlag::FileMissingOnDisk | StateFlag::FileChangedOutside)) {
|
||||
qDebug() << "disable AutoSave due to file missing on disk or changed outside";
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(!(m_state & StateFlag::Discarded));
|
||||
auto policy = ConfigMgr::getInst().getEditorConfig().getAutoSavePolicy();
|
||||
switch (policy) {
|
||||
case EditorConfig::AutoSavePolicy::None:
|
||||
return;
|
||||
|
||||
case EditorConfig::AutoSavePolicy::AutoSave:
|
||||
save(false);
|
||||
break;
|
||||
|
||||
case EditorConfig::AutoSavePolicy::BackupFile:
|
||||
writeBackupFile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Buffer::writeBackupFile()
|
||||
{
|
||||
if (m_backupFilePath.isEmpty()) {
|
||||
const auto &config = ConfigMgr::getInst().getEditorConfig();
|
||||
QString backupDirPath(QDir(getContentBasePath()).filePath(config.getBackupFileDirectory()));
|
||||
backupDirPath = QDir::cleanPath(backupDirPath);
|
||||
auto backupFileName = FileUtils::generateFileNameWithSequence(backupDirPath,
|
||||
getName(),
|
||||
config.getBackupFileExtension());
|
||||
QDir backupDir(backupDirPath);
|
||||
backupDir.mkpath(backupDirPath);
|
||||
m_backupFilePath = backupDir.filePath(backupFileName);
|
||||
}
|
||||
|
||||
Q_ASSERT(m_backupFilePathOfPreviousSession.isEmpty());
|
||||
|
||||
// Just use FileUtils instead of notebook backend.
|
||||
FileUtils::writeFile(m_backupFilePath, generateBackupFileHead() + getContent());
|
||||
}
|
||||
|
||||
QString Buffer::generateBackupFileHead() const
|
||||
{
|
||||
return QString("vnotex_backup_file %1|").arg(getContentPath());
|
||||
}
|
||||
|
||||
void Buffer::checkBackupFileOfPreviousSession()
|
||||
{
|
||||
const auto &config = ConfigMgr::getInst().getEditorConfig();
|
||||
if (config.getAutoSavePolicy() != EditorConfig::AutoSavePolicy::BackupFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString backupDirPath(QDir(getContentBasePath()).filePath(config.getBackupFileDirectory()));
|
||||
backupDirPath = QDir::cleanPath(backupDirPath);
|
||||
QDir backupDir(backupDirPath);
|
||||
QStringList backupFiles;
|
||||
{
|
||||
const QString nameFilter = QString("%1*%2").arg(getName(), config.getBackupFileExtension());
|
||||
backupFiles = backupDir.entryList(QStringList(nameFilter),
|
||||
QDir::Files | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot);
|
||||
}
|
||||
|
||||
if (backupFiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &file : backupFiles) {
|
||||
const auto filePath = backupDir.filePath(file);
|
||||
if (isBackupFileOfBuffer(filePath)) {
|
||||
const auto backupContent = readBackupFile(filePath);
|
||||
if (backupContent == getContent()) {
|
||||
// Found backup file with identical content.
|
||||
// Just discard the backup file.
|
||||
FileUtils::removeFile(filePath);
|
||||
qInfo() << "delete identical backup file of previous session" << filePath;
|
||||
} else {
|
||||
m_backupFilePathOfPreviousSession = filePath;
|
||||
qInfo() << "found backup file of previous session" << filePath;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Buffer::isBackupFileOfBuffer(const QString &p_file) const
|
||||
{
|
||||
QFile file(p_file);
|
||||
if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream st(&file);
|
||||
const auto head = st.readLine();
|
||||
return head.startsWith(generateBackupFileHead());
|
||||
}
|
||||
|
||||
const QString &Buffer::getBackupFileOfPreviousSession() const
|
||||
{
|
||||
return m_backupFilePathOfPreviousSession;
|
||||
}
|
||||
|
||||
QString Buffer::readBackupFile(const QString &p_filePath)
|
||||
{
|
||||
auto content = FileUtils::readTextFile(p_filePath);
|
||||
return content.mid(content.indexOf(QLatin1Char('|')) + 1);
|
||||
}
|
||||
|
||||
void Buffer::discardBackupFileOfPreviousSession()
|
||||
{
|
||||
Q_ASSERT(!m_backupFilePathOfPreviousSession.isEmpty());
|
||||
|
||||
FileUtils::removeFile(m_backupFilePathOfPreviousSession);
|
||||
qInfo() << "discard backup file of previous session" << m_backupFilePathOfPreviousSession;
|
||||
m_backupFilePathOfPreviousSession.clear();
|
||||
}
|
||||
|
||||
void Buffer::recoverFromBackupFileOfPreviousSession()
|
||||
{
|
||||
Q_ASSERT(!m_backupFilePathOfPreviousSession.isEmpty());
|
||||
|
||||
m_content = readBackupFile(m_backupFilePathOfPreviousSession);
|
||||
m_provider->write(m_content);
|
||||
++m_revision;
|
||||
|
||||
FileUtils::removeFile(m_backupFilePathOfPreviousSession);
|
||||
qInfo() << "recover from backup file of previous session" << m_backupFilePathOfPreviousSession;
|
||||
m_backupFilePathOfPreviousSession.clear();
|
||||
|
||||
// Reset state.
|
||||
m_viewWindowToSync = nullptr;
|
||||
m_modified = false;
|
||||
|
||||
emit modified(m_modified);
|
||||
emit contentsChanged();
|
||||
}
|
||||
|
||||
bool Buffer::isChildOf(const Node *p_node) const
|
||||
{
|
||||
return m_provider->isChildOf(p_node);
|
||||
}
|
||||
|
||||
bool Buffer::isAttachmentSupported() const
|
||||
{
|
||||
return !m_readOnly && m_provider->isAttachmentSupported();
|
||||
}
|
||||
|
||||
bool Buffer::hasAttachment() const
|
||||
{
|
||||
if (!isAttachmentSupported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_provider->getAttachmentFolder().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir dir(getAttachmentFolderPath());
|
||||
return !dir.isEmpty();
|
||||
}
|
||||
|
||||
QString Buffer::getAttachmentFolderPath() const
|
||||
{
|
||||
Q_ASSERT(isAttachmentSupported());
|
||||
return const_cast<Buffer *>(this)->m_provider->fetchAttachmentFolderPath();
|
||||
}
|
||||
|
||||
QStringList Buffer::addAttachment(const QString &p_destFolderPath, const QStringList &p_files)
|
||||
{
|
||||
if (p_files.isEmpty()) {
|
||||
return QStringList();
|
||||
}
|
||||
auto destFolderPath = p_destFolderPath.isEmpty() ? getAttachmentFolderPath() : p_destFolderPath;
|
||||
Q_ASSERT(PathUtils::pathContains(getAttachmentFolderPath(), destFolderPath));
|
||||
auto files = m_provider->addAttachment(destFolderPath, p_files);
|
||||
if (!files.isEmpty()) {
|
||||
emit attachmentChanged();
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
QString Buffer::newAttachmentFile(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(PathUtils::pathContains(getAttachmentFolderPath(), p_destFolderPath));
|
||||
auto filePath = m_provider->newAttachmentFile(p_destFolderPath, p_name);
|
||||
emit attachmentChanged();
|
||||
return filePath;
|
||||
}
|
||||
|
||||
QString Buffer::newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(PathUtils::pathContains(getAttachmentFolderPath(), p_destFolderPath));
|
||||
auto folderPath = m_provider->newAttachmentFolder(p_destFolderPath, p_name);
|
||||
emit attachmentChanged();
|
||||
return folderPath;
|
||||
}
|
||||
|
||||
QString Buffer::renameAttachment(const QString &p_path, const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(PathUtils::pathContains(getAttachmentFolderPath(), p_path));
|
||||
return m_provider->renameAttachment(p_path, p_name);
|
||||
}
|
||||
|
||||
void Buffer::removeAttachment(const QStringList &p_paths)
|
||||
{
|
||||
m_provider->removeAttachment(p_paths);
|
||||
emit attachmentChanged();
|
||||
}
|
||||
|
||||
bool Buffer::isAttachment(const QString &p_path) const
|
||||
{
|
||||
return PathUtils::pathContains(getAttachmentFolderPath(), p_path);
|
||||
}
|
||||
|
||||
Buffer::ProviderType Buffer::getProviderType() const
|
||||
{
|
||||
return m_provider->getType();
|
||||
}
|
||||
|
||||
Node *Buffer::getNode() const
|
||||
{
|
||||
return m_provider->getNode();
|
||||
}
|
||||
|
||||
bool Buffer::checkFileExistsOnDisk()
|
||||
{
|
||||
if (m_provider->checkFileExistsOnDisk()) {
|
||||
m_state &= ~StateFlag::FileMissingOnDisk;
|
||||
return true;
|
||||
} else {
|
||||
m_state |= StateFlag::FileMissingOnDisk;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Buffer::checkFileChangedOutside()
|
||||
{
|
||||
if (m_provider->checkFileChangedOutside()) {
|
||||
m_state |= StateFlag::FileChangedOutside;
|
||||
return true;
|
||||
} else {
|
||||
m_state &= ~StateFlag::FileChangedOutside;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::StateFlags Buffer::state() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
241
src/core/buffer/buffer.h
Normal file
241
src/core/buffer/buffer.h
Normal file
@ -0,0 +1,241 @@
|
||||
#ifndef BUFFER_H
|
||||
#define BUFFER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <global.h>
|
||||
|
||||
class QWidget;
|
||||
class QTimer;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Node;
|
||||
class Buffer;
|
||||
class ViewWindow;
|
||||
struct FileOpenParameters;
|
||||
class BufferProvider;
|
||||
|
||||
struct BufferParameters
|
||||
{
|
||||
QSharedPointer<BufferProvider> m_provider;
|
||||
};
|
||||
|
||||
class Buffer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class ProviderType
|
||||
{
|
||||
Internal,
|
||||
External
|
||||
};
|
||||
|
||||
enum class OperationCode
|
||||
{
|
||||
Success,
|
||||
FileMissingOnDisk,
|
||||
FileChangedOutside,
|
||||
Failed
|
||||
};
|
||||
|
||||
enum StateFlag
|
||||
{
|
||||
Normal = 0,
|
||||
FileMissingOnDisk = 0x1,
|
||||
FileChangedOutside = 0x2,
|
||||
Discarded = 0x4
|
||||
};
|
||||
Q_DECLARE_FLAGS(StateFlags, StateFlag);
|
||||
|
||||
Buffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
virtual ~Buffer();
|
||||
|
||||
int getAttachViewWindowCount() const;
|
||||
|
||||
void attachViewWindow(ViewWindow *p_win);
|
||||
void detachViewWindow(ViewWindow *p_win);
|
||||
|
||||
// Create a view window to show the content of this buffer.
|
||||
// Attach the created view window to this buffer.
|
||||
ViewWindow *createViewWindow(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent);
|
||||
|
||||
// Whether this buffer matches @p_node.
|
||||
bool match(const Node *p_node) const;
|
||||
|
||||
// Whether this buffer matches @p_filePath.
|
||||
bool match(const QString &p_filePath) const;
|
||||
|
||||
// Buffer name.
|
||||
QString getName() const;
|
||||
|
||||
QString getPath() const;
|
||||
|
||||
// In some cases, getPath() may point to a ocntainer containting all the stuffs.
|
||||
// getContentPath() will return the real path to the file providing the content.
|
||||
QString getContentPath() const;
|
||||
|
||||
// Get the base path to resolve resources.
|
||||
QString getContentBasePath() const;
|
||||
|
||||
ID getID() const;
|
||||
|
||||
// Get buffer content.
|
||||
// It may differ from the content on disk.
|
||||
// For performance, we need to sync the content with ViewWindow before returning
|
||||
// the latest content.
|
||||
const QString &getContent() const;
|
||||
|
||||
// @p_revision will be set before contentsChanged is emitted.
|
||||
void setContent(const QString &p_content, int &p_revision);
|
||||
|
||||
// Invalidate the content of buffer.
|
||||
// Need to sync with @p_win to get the latest content.
|
||||
// @p_setRevision will be called to set revision before contentsChanged is emitted.
|
||||
void invalidateContent(const ViewWindow *p_win,
|
||||
const std::function<void(int)> &p_setRevision);
|
||||
|
||||
// Sync content with @p_win if @p_win is the window needed to sync.
|
||||
void syncContent(const ViewWindow *p_win);
|
||||
|
||||
int getRevision() const;
|
||||
|
||||
bool isModified() const;
|
||||
void setModified(bool p_modified);
|
||||
|
||||
bool isReadOnly() const;
|
||||
|
||||
// Save buffer content to file.
|
||||
OperationCode save(bool p_force);
|
||||
|
||||
// Discard changes and reload file.
|
||||
OperationCode reload();
|
||||
|
||||
// Discard the buffer which will invalidate the buffer.
|
||||
void discard();
|
||||
|
||||
// Buffer is about to be deleted.
|
||||
void close();
|
||||
|
||||
// Insert image from @p_srcImagePath.
|
||||
// Return inserted image file path.
|
||||
virtual QString insertImage(const QString &p_srcImagePath, const QString &p_imageFileName);
|
||||
|
||||
virtual QString insertImage(const QImage &p_image, const QString &p_imageFileName);
|
||||
|
||||
virtual void removeImage(const QString &p_imagePath);
|
||||
|
||||
const QString &getBackupFileOfPreviousSession() const;
|
||||
|
||||
void discardBackupFileOfPreviousSession();
|
||||
|
||||
void recoverFromBackupFileOfPreviousSession();
|
||||
|
||||
// Whether this buffer's provider is a child of @p_node or an attachment of @p_node.
|
||||
bool isChildOf(const Node *p_node) const;
|
||||
|
||||
Node *getNode() const;
|
||||
|
||||
bool isAttachmentSupported() const;
|
||||
|
||||
bool hasAttachment() const;
|
||||
|
||||
QString getAttachmentFolderPath() const;
|
||||
|
||||
// @p_destFolderPath: folder path locating in attachment folder. Use the root folder if empty.
|
||||
QStringList addAttachment(const QString &p_destFolderPath, const QStringList &p_files);
|
||||
|
||||
QString newAttachmentFile(const QString &p_destFolderPath, const QString &p_name);
|
||||
|
||||
QString newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name);
|
||||
|
||||
QString renameAttachment(const QString &p_path, const QString &p_name);
|
||||
|
||||
void removeAttachment(const QStringList &p_paths);
|
||||
|
||||
// Judge whether file @p_path is attachment.
|
||||
bool isAttachment(const QString &p_path) const;
|
||||
|
||||
ProviderType getProviderType() const;
|
||||
|
||||
bool checkFileExistsOnDisk();
|
||||
|
||||
bool checkFileChangedOutside();
|
||||
|
||||
StateFlags state() const;
|
||||
|
||||
static QString readBackupFile(const QString &p_filePath);
|
||||
|
||||
signals:
|
||||
void attachedViewWindowEmpty();
|
||||
|
||||
void modified(bool p_modified);
|
||||
|
||||
void contentsChanged();
|
||||
|
||||
void nameChanged();
|
||||
|
||||
void attachmentChanged();
|
||||
|
||||
protected:
|
||||
virtual ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent) = 0;
|
||||
|
||||
QSharedPointer<BufferProvider> m_provider;
|
||||
|
||||
private slots:
|
||||
void autoSave();
|
||||
|
||||
private:
|
||||
void syncContent();
|
||||
|
||||
void readContent();
|
||||
|
||||
// Get the path of the image folder.
|
||||
QString getImageFolderPath() const;
|
||||
|
||||
void writeBackupFile();
|
||||
|
||||
// Generate backup file head.
|
||||
QString generateBackupFileHead() const;
|
||||
|
||||
void checkBackupFileOfPreviousSession();
|
||||
|
||||
bool isBackupFileOfBuffer(const QString &p_file) const;
|
||||
|
||||
// Will be assigned uniquely once created.
|
||||
const ID c_id = 0;
|
||||
|
||||
// Revision of contents.
|
||||
int m_revision = 0;
|
||||
|
||||
// If the buffer is modified, m_content reflect the latest changes instead
|
||||
// of the file content.
|
||||
QString m_content;
|
||||
|
||||
bool m_readOnly = false;
|
||||
|
||||
bool m_modified = false;
|
||||
|
||||
int m_attachedViewWindowCount = 0;
|
||||
|
||||
const ViewWindow *m_viewWindowToSync = nullptr;
|
||||
|
||||
// Managed by QObject.
|
||||
QTimer *m_autoSaveTimer = nullptr;
|
||||
|
||||
QString m_backupFilePath;
|
||||
|
||||
QString m_backupFilePathOfPreviousSession;
|
||||
|
||||
StateFlags m_state = StateFlag::Normal;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::Buffer::StateFlags)
|
||||
|
||||
#endif // BUFFER_H
|
22
src/core/buffer/buffer.pri
Normal file
22
src/core/buffer/buffer.pri
Normal file
@ -0,0 +1,22 @@
|
||||
SOURCES += \
|
||||
$$PWD/buffer.cpp \
|
||||
$$PWD/bufferprovider.cpp \
|
||||
$$PWD/filebufferprovider.cpp \
|
||||
$$PWD/markdownbuffer.cpp \
|
||||
$$PWD/markdownbufferfactory.cpp \
|
||||
$$PWD/filetypehelper.cpp \
|
||||
$$PWD/nodebufferprovider.cpp \
|
||||
$$PWD/textbuffer.cpp \
|
||||
$$PWD/textbufferfactory.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/bufferprovider.h \
|
||||
$$PWD/buffer.h \
|
||||
$$PWD/filebufferprovider.h \
|
||||
$$PWD/ibufferfactory.h \
|
||||
$$PWD/markdownbuffer.h \
|
||||
$$PWD/markdownbufferfactory.h \
|
||||
$$PWD/filetypehelper.h \
|
||||
$$PWD/nodebufferprovider.h \
|
||||
$$PWD/textbuffer.h \
|
||||
$$PWD/textbufferfactory.h
|
24
src/core/buffer/bufferprovider.cpp
Normal file
24
src/core/buffer/bufferprovider.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "bufferprovider.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
bool BufferProvider::checkFileExistsOnDisk() const
|
||||
{
|
||||
return QFileInfo::exists(getContentPath());
|
||||
}
|
||||
|
||||
QDateTime BufferProvider::getLastModifiedFromFile() const
|
||||
{
|
||||
return QFileInfo(getContentPath()).lastModified();
|
||||
}
|
||||
|
||||
bool BufferProvider::checkFileChangedOutside() const
|
||||
{
|
||||
QFileInfo info(getContentPath());
|
||||
if (!info.exists() || m_lastModified != info.lastModified()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
82
src/core/buffer/bufferprovider.h
Normal file
82
src/core/buffer/bufferprovider.h
Normal file
@ -0,0 +1,82 @@
|
||||
#ifndef BUFFERPROVIDER_H
|
||||
#define BUFFERPROVIDER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Node;
|
||||
|
||||
// Content provider for Buffer.
|
||||
class BufferProvider : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BufferProvider(QObject *p_parent = nullptr)
|
||||
: QObject(p_parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~BufferProvider() {}
|
||||
|
||||
virtual Buffer::ProviderType getType() const = 0;
|
||||
|
||||
virtual bool match(const Node *p_node) const = 0;
|
||||
|
||||
virtual bool match(const QString &p_filePath) const = 0;
|
||||
|
||||
virtual QString getName() const = 0;
|
||||
|
||||
virtual QString getPath() const = 0;
|
||||
|
||||
virtual QString getContentPath() const = 0;
|
||||
|
||||
virtual void write(const QString &p_content) = 0;
|
||||
|
||||
virtual QString read() const = 0;
|
||||
|
||||
virtual QString fetchImageFolderPath() = 0;
|
||||
|
||||
virtual bool isChildOf(const Node *p_node) const = 0;
|
||||
|
||||
virtual Node *getNode() const = 0;
|
||||
|
||||
virtual QString getAttachmentFolder() const = 0;
|
||||
|
||||
virtual QString fetchAttachmentFolderPath() = 0;
|
||||
|
||||
virtual QStringList addAttachment(const QString &p_destFolderPath, const QStringList &p_files) = 0;
|
||||
|
||||
virtual QString newAttachmentFile(const QString &p_destFolderPath, const QString &p_name) = 0;
|
||||
|
||||
virtual QString newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name) = 0;
|
||||
|
||||
virtual QString renameAttachment(const QString &p_path, const QString &p_name) = 0;
|
||||
|
||||
virtual void removeAttachment(const QStringList &p_paths) = 0;
|
||||
|
||||
virtual QString insertImage(const QString &p_srcImagePath, const QString &p_imageFileName) = 0;
|
||||
|
||||
virtual QString insertImage(const QImage &p_image, const QString &p_imageFileName) = 0;
|
||||
|
||||
virtual void removeImage(const QString &p_imagePath) = 0;
|
||||
|
||||
virtual bool isAttachmentSupported() const = 0;
|
||||
|
||||
virtual bool checkFileExistsOnDisk() const;
|
||||
|
||||
virtual bool checkFileChangedOutside() const;
|
||||
|
||||
virtual bool isReadOnly() const = 0;
|
||||
|
||||
protected:
|
||||
virtual QDateTime getLastModifiedFromFile() const;
|
||||
|
||||
QDateTime m_lastModified;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BUFFERPROVIDER_H
|
165
src/core/buffer/filebufferprovider.cpp
Normal file
165
src/core/buffer/filebufferprovider.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
#include "filebufferprovider.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <utils/pathutils.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <notebook/node.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
FileBufferProvider::FileBufferProvider(const QString &p_filePath,
|
||||
Node *p_nodeAttachedTo,
|
||||
bool p_readOnly,
|
||||
QObject *p_parent)
|
||||
: BufferProvider(p_parent),
|
||||
c_filePath(p_filePath),
|
||||
c_nodeAttachedTo(p_nodeAttachedTo),
|
||||
m_readOnly(p_readOnly)
|
||||
{
|
||||
}
|
||||
|
||||
Buffer::ProviderType FileBufferProvider::getType() const
|
||||
{
|
||||
return Buffer::ProviderType::External;
|
||||
}
|
||||
|
||||
bool FileBufferProvider::match(const Node *p_node) const
|
||||
{
|
||||
Q_UNUSED(p_node);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileBufferProvider::match(const QString &p_filePath) const
|
||||
{
|
||||
return PathUtils::areSamePaths(c_filePath, p_filePath);
|
||||
}
|
||||
|
||||
QString FileBufferProvider::getName() const
|
||||
{
|
||||
return PathUtils::fileName(c_filePath);
|
||||
}
|
||||
|
||||
QString FileBufferProvider::getPath() const
|
||||
{
|
||||
return c_filePath;
|
||||
}
|
||||
|
||||
QString FileBufferProvider::getContentPath() const
|
||||
{
|
||||
// TODO.
|
||||
return getPath();
|
||||
}
|
||||
|
||||
void FileBufferProvider::write(const QString &p_content)
|
||||
{
|
||||
FileUtils::writeFile(getContentPath(), p_content);
|
||||
m_lastModified = getLastModifiedFromFile();
|
||||
}
|
||||
|
||||
QString FileBufferProvider::read() const
|
||||
{
|
||||
const_cast<FileBufferProvider *>(this)->m_lastModified = getLastModifiedFromFile();
|
||||
return FileUtils::readTextFile(getContentPath());
|
||||
}
|
||||
|
||||
QString FileBufferProvider::fetchImageFolderPath()
|
||||
{
|
||||
auto pa = PathUtils::concatenateFilePath(PathUtils::parentDirPath(getContentPath()), QStringLiteral("vx_images"));
|
||||
QDir().mkpath(pa);
|
||||
return pa;
|
||||
}
|
||||
|
||||
bool FileBufferProvider::isChildOf(const Node *p_node) const
|
||||
{
|
||||
if (c_nodeAttachedTo) {
|
||||
return c_nodeAttachedTo == p_node || Node::isAncestor(p_node, c_nodeAttachedTo);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString FileBufferProvider::getAttachmentFolder() const
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString FileBufferProvider::fetchAttachmentFolderPath()
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
QStringList FileBufferProvider::addAttachment(const QString &p_destFolderPath, const QStringList &p_files)
|
||||
{
|
||||
Q_UNUSED(p_destFolderPath);
|
||||
Q_UNUSED(p_files);
|
||||
Q_ASSERT(false);
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QString FileBufferProvider::newAttachmentFile(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
Q_UNUSED(p_destFolderPath);
|
||||
Q_UNUSED(p_name);
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString FileBufferProvider::newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
Q_UNUSED(p_destFolderPath);
|
||||
Q_UNUSED(p_name);
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString FileBufferProvider::renameAttachment(const QString &p_path, const QString &p_name)
|
||||
{
|
||||
Q_UNUSED(p_path);
|
||||
Q_UNUSED(p_name);
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
void FileBufferProvider::removeAttachment(const QStringList &p_paths)
|
||||
{
|
||||
Q_UNUSED(p_paths);
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
QString FileBufferProvider::insertImage(const QString &p_srcImagePath, const QString &p_imageFileName)
|
||||
{
|
||||
const auto imageFolderPath = fetchImageFolderPath();
|
||||
auto destFilePath = FileUtils::renameIfExistsCaseInsensitive(PathUtils::concatenateFilePath(imageFolderPath, p_imageFileName));
|
||||
FileUtils::copyFile(p_srcImagePath, destFilePath);
|
||||
return destFilePath;
|
||||
}
|
||||
|
||||
QString FileBufferProvider::insertImage(const QImage &p_image, const QString &p_imageFileName)
|
||||
{
|
||||
const auto imageFolderPath = fetchImageFolderPath();
|
||||
auto destFilePath = FileUtils::renameIfExistsCaseInsensitive(PathUtils::concatenateFilePath(imageFolderPath, p_imageFileName));
|
||||
p_image.save(destFilePath);
|
||||
return destFilePath;
|
||||
}
|
||||
|
||||
void FileBufferProvider::removeImage(const QString &p_imagePath)
|
||||
{
|
||||
FileUtils::removeFile(p_imagePath);
|
||||
}
|
||||
|
||||
bool FileBufferProvider::isAttachmentSupported() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Node *FileBufferProvider::getNode() const
|
||||
{
|
||||
return c_nodeAttachedTo;
|
||||
}
|
||||
|
||||
bool FileBufferProvider::isReadOnly() const
|
||||
{
|
||||
return m_readOnly;
|
||||
}
|
73
src/core/buffer/filebufferprovider.h
Normal file
73
src/core/buffer/filebufferprovider.h
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef FILEBUFFERPROVIDER_H
|
||||
#define FILEBUFFERPROVIDER_H
|
||||
|
||||
#include "bufferprovider.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Buffer provider based on external file.
|
||||
class FileBufferProvider : public BufferProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FileBufferProvider(const QString &p_filePath,
|
||||
Node *p_nodeAttachedTo,
|
||||
bool p_readOnly,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
Buffer::ProviderType getType() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool match(const Node *p_node) const Q_DECL_OVERRIDE;
|
||||
|
||||
bool match(const QString &p_filePath) const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getPath() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getContentPath() const Q_DECL_OVERRIDE;
|
||||
|
||||
void write(const QString &p_content) Q_DECL_OVERRIDE;
|
||||
|
||||
QString read() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString fetchImageFolderPath() Q_DECL_OVERRIDE;
|
||||
|
||||
bool isChildOf(const Node *p_node) const Q_DECL_OVERRIDE;
|
||||
|
||||
Node *getNode() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getAttachmentFolder() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString fetchAttachmentFolderPath() Q_DECL_OVERRIDE;
|
||||
|
||||
QStringList addAttachment(const QString &p_destFolderPath, const QStringList &p_files) Q_DECL_OVERRIDE;
|
||||
|
||||
QString newAttachmentFile(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
QString newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
QString renameAttachment(const QString &p_path, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeAttachment(const QStringList &p_paths) Q_DECL_OVERRIDE;
|
||||
|
||||
QString insertImage(const QString &p_srcImagePath, const QString &p_imageFileName) Q_DECL_OVERRIDE;
|
||||
|
||||
QString insertImage(const QImage &p_image, const QString &p_imageFileName) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeImage(const QString &p_imagePath) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isAttachmentSupported() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isReadOnly() const Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
const QString c_filePath;
|
||||
|
||||
Node *c_nodeAttachedTo = nullptr;
|
||||
|
||||
bool m_readOnly = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FILEBUFFERPROVIDER_H
|
54
src/core/buffer/filetypehelper.cpp
Normal file
54
src/core/buffer/filetypehelper.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "filetypehelper.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const FileType FileTypeHelper::s_markdownFileType = "markdown";
|
||||
|
||||
const FileType FileTypeHelper::s_textFileType = "text";
|
||||
|
||||
const FileType FileTypeHelper::s_unknownFileType = "unknown";
|
||||
|
||||
QSharedPointer<QMap<QString, FileType>> FileTypeHelper::s_fileTypeMap;
|
||||
|
||||
FileType FileTypeHelper::fileType(const QString &p_filePath)
|
||||
{
|
||||
Q_ASSERT(!p_filePath.isEmpty());
|
||||
|
||||
if (!s_fileTypeMap) {
|
||||
init();
|
||||
}
|
||||
|
||||
QFileInfo fi(p_filePath);
|
||||
auto suffix = fi.suffix().toLower();
|
||||
auto it = s_fileTypeMap->find(suffix);
|
||||
if (it != s_fileTypeMap->end()) {
|
||||
return it.value();
|
||||
}
|
||||
|
||||
// Treat all unknown text files as plain text files.
|
||||
if (FileUtils::isText(p_filePath)) {
|
||||
return s_fileTypeMap->value(QStringLiteral("txt"));
|
||||
}
|
||||
|
||||
return s_unknownFileType;
|
||||
}
|
||||
|
||||
#define ADD(x, y) s_fileTypeMap->insert((x), (y))
|
||||
|
||||
void FileTypeHelper::init()
|
||||
{
|
||||
// TODO: load mapping from configuration file.
|
||||
s_fileTypeMap.reset(new QMap<QString, FileType>());
|
||||
|
||||
ADD(QStringLiteral("md"), s_markdownFileType);
|
||||
ADD(QStringLiteral("markdown"), s_markdownFileType);
|
||||
ADD(QStringLiteral("mkd"), s_markdownFileType);
|
||||
|
||||
ADD(QStringLiteral("txt"), s_textFileType);
|
||||
ADD(QStringLiteral("text"), s_textFileType);
|
||||
ADD(QStringLiteral("log"), s_textFileType);
|
||||
}
|
33
src/core/buffer/filetypehelper.h
Normal file
33
src/core/buffer/filetypehelper.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef FILETYPEHELPER_H
|
||||
#define FILETYPEHELPER_H
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
typedef QString FileType;
|
||||
|
||||
// Map file suffix to file type.
|
||||
class FileTypeHelper
|
||||
{
|
||||
public:
|
||||
FileTypeHelper() = delete;
|
||||
|
||||
static FileType fileType(const QString &p_filePath);
|
||||
|
||||
static const FileType s_markdownFileType;
|
||||
|
||||
static const FileType s_textFileType;
|
||||
|
||||
static const FileType s_unknownFileType;
|
||||
|
||||
private:
|
||||
static void init();
|
||||
|
||||
static QSharedPointer<QMap<QString, FileType>> s_fileTypeMap;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // FILETYPEHELPER_H
|
24
src/core/buffer/ibufferfactory.h
Normal file
24
src/core/buffer/ibufferfactory.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef IBUFFERFACTORY_H
|
||||
#define IBUFFERFACTORY_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Buffer;
|
||||
struct BufferParameters;
|
||||
|
||||
// Abstract factory to create buffer.
|
||||
class IBufferFactory
|
||||
{
|
||||
public:
|
||||
virtual ~IBufferFactory()
|
||||
{
|
||||
}
|
||||
|
||||
virtual Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) = 0;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // IBUFFERFACTORY_H
|
94
src/core/buffer/markdownbuffer.cpp
Normal file
94
src/core/buffer/markdownbuffer.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "markdownbuffer.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <widgets/markdownviewwindow.h>
|
||||
#include <notebook/node.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <buffer/bufferprovider.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
MarkdownBuffer::MarkdownBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
: Buffer(p_parameters, p_parent)
|
||||
{
|
||||
fetchInitialImages();
|
||||
}
|
||||
|
||||
ViewWindow *MarkdownBuffer::createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent)
|
||||
{
|
||||
return new MarkdownViewWindow(p_paras, p_parent);
|
||||
}
|
||||
|
||||
QString MarkdownBuffer::insertImage(const QString &p_srcImagePath, const QString &p_imageFileName)
|
||||
{
|
||||
return m_provider->insertImage(p_srcImagePath, p_imageFileName);
|
||||
}
|
||||
|
||||
QString MarkdownBuffer::insertImage(const QImage &p_image, const QString &p_imageFileName)
|
||||
{
|
||||
return m_provider->insertImage(p_image, p_imageFileName);
|
||||
}
|
||||
|
||||
void MarkdownBuffer::fetchInitialImages()
|
||||
{
|
||||
Q_ASSERT(m_initialImages.isEmpty());
|
||||
m_initialImages = vte::MarkdownUtils::fetchImagesFromMarkdownText(getContent(),
|
||||
getContentBasePath(),
|
||||
vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||
}
|
||||
|
||||
void MarkdownBuffer::addInsertedImage(const QString &p_imagePath, const QString &p_urlInLink)
|
||||
{
|
||||
vte::MarkdownLink link;
|
||||
link.m_path = p_imagePath;
|
||||
link.m_urlInLink = p_urlInLink;
|
||||
link.m_type = vte::MarkdownLink::TypeFlag::LocalRelativeInternal;
|
||||
m_insertedImages.append(link);
|
||||
}
|
||||
|
||||
QSet<QString> MarkdownBuffer::clearObsoleteImages()
|
||||
{
|
||||
QSet<QString> obsoleteImages;
|
||||
|
||||
Q_ASSERT(!isModified());
|
||||
const bool discarded = state() & StateFlag::Discarded;
|
||||
const auto latestImages =
|
||||
vte::MarkdownUtils::fetchImagesFromMarkdownText(!discarded ? getContent() : m_provider->read(),
|
||||
getContentBasePath(),
|
||||
vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||
QSet<QString> latestImagesPath;
|
||||
for (const auto &link : latestImages) {
|
||||
latestImagesPath.insert(PathUtils::normalizePath(link.m_path));
|
||||
}
|
||||
|
||||
for (const auto &link : m_insertedImages) {
|
||||
if (!(link.m_type & vte::MarkdownLink::TypeFlag::LocalRelativeInternal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!latestImagesPath.contains(PathUtils::normalizePath(link.m_path))) {
|
||||
obsoleteImages.insert(link.m_path);
|
||||
}
|
||||
}
|
||||
|
||||
m_insertedImages.clear();
|
||||
|
||||
for (const auto &link : m_initialImages) {
|
||||
Q_ASSERT(link.m_type & vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||
if (!latestImagesPath.contains(PathUtils::normalizePath(link.m_path))) {
|
||||
obsoleteImages.insert(link.m_path);
|
||||
}
|
||||
}
|
||||
|
||||
m_initialImages = latestImages;
|
||||
|
||||
return obsoleteImages;
|
||||
}
|
||||
|
||||
void MarkdownBuffer::removeImage(const QString &p_imagePath)
|
||||
{
|
||||
qDebug() << "remove obsolete image" << p_imagePath;
|
||||
m_provider->removeImage(p_imagePath);
|
||||
}
|
47
src/core/buffer/markdownbuffer.h
Normal file
47
src/core/buffer/markdownbuffer.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef MARKDOWNBUFFER_H
|
||||
#define MARKDOWNBUFFER_H
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
#include <QVector>
|
||||
#include <QSet>
|
||||
|
||||
#include <vtextedit/markdownutils.h>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class MarkdownBuffer : public Buffer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MarkdownBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
QString insertImage(const QString &p_srcImagePath, const QString &p_imageFileName) Q_DECL_OVERRIDE;
|
||||
|
||||
QString insertImage(const QImage &p_image, const QString &p_imageFileName) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeImage(const QString &p_imagePath) Q_DECL_OVERRIDE;
|
||||
|
||||
void addInsertedImage(const QString &p_imagePath, const QString &p_urlInLink);
|
||||
|
||||
// Clear obsolete images.
|
||||
// Won't delete images, just return a list of obsolete images path.
|
||||
// Will re-init m_initialImages and clear m_insertedImages.
|
||||
QSet<QString> clearObsoleteImages();
|
||||
|
||||
protected:
|
||||
ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void fetchInitialImages();
|
||||
|
||||
// Images referenced in the file before opening this buffer.
|
||||
QVector<vte::MarkdownLink> m_initialImages;
|
||||
|
||||
// Images newly inserted during this buffer's lifetime.
|
||||
QVector<vte::MarkdownLink> m_insertedImages;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // MARKDOWNBUFFER_H
|
11
src/core/buffer/markdownbufferfactory.cpp
Normal file
11
src/core/buffer/markdownbufferfactory.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "markdownbufferfactory.h"
|
||||
|
||||
#include "markdownbuffer.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
Buffer *MarkdownBufferFactory::createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
{
|
||||
return new MarkdownBuffer(p_parameters, p_parent);
|
||||
}
|
17
src/core/buffer/markdownbufferfactory.h
Normal file
17
src/core/buffer/markdownbufferfactory.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef MARKDOWNBUFFERFACTORY_H
|
||||
#define MARKDOWNBUFFERFACTORY_H
|
||||
|
||||
#include "ibufferfactory.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Buffer factory for Markdown file.
|
||||
class MarkdownBufferFactory : public IBufferFactory
|
||||
{
|
||||
public:
|
||||
Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) Q_DECL_OVERRIDE;
|
||||
};
|
||||
} // vnotex
|
||||
|
||||
#endif // MARKDOWNBUFFERFACTORY_H
|
133
src/core/buffer/nodebufferprovider.cpp
Normal file
133
src/core/buffer/nodebufferprovider.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "nodebufferprovider.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <notebook/node.h>
|
||||
#include <utils/pathutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
NodeBufferProvider::NodeBufferProvider(Node *p_node, QObject *p_parent)
|
||||
: BufferProvider(p_parent),
|
||||
m_node(p_node),
|
||||
m_path(m_node->fetchAbsolutePath()),
|
||||
m_contentPath(m_node->fetchContentPath())
|
||||
{
|
||||
}
|
||||
|
||||
Buffer::ProviderType NodeBufferProvider::getType() const
|
||||
{
|
||||
return Buffer::ProviderType::Internal;
|
||||
}
|
||||
|
||||
bool NodeBufferProvider::match(const Node *p_node) const
|
||||
{
|
||||
return m_node == p_node;
|
||||
}
|
||||
|
||||
bool NodeBufferProvider::match(const QString &p_filePath) const
|
||||
{
|
||||
return PathUtils::areSamePaths(getPath(), p_filePath);
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::getName() const
|
||||
{
|
||||
return m_node->getName();
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::getPath() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::getContentPath() const
|
||||
{
|
||||
return m_contentPath;
|
||||
}
|
||||
|
||||
void NodeBufferProvider::write(const QString &p_content)
|
||||
{
|
||||
m_node->write(p_content);
|
||||
m_lastModified = getLastModifiedFromFile();
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::read() const
|
||||
{
|
||||
const_cast<NodeBufferProvider *>(this)->m_lastModified = getLastModifiedFromFile();
|
||||
return m_node->read();
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::fetchImageFolderPath()
|
||||
{
|
||||
return m_node->fetchImageFolderPath();
|
||||
}
|
||||
|
||||
bool NodeBufferProvider::isChildOf(const Node *p_node) const
|
||||
{
|
||||
return Node::isAncestor(p_node, m_node);
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::getAttachmentFolder() const
|
||||
{
|
||||
return m_node->getAttachmentFolder();
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::fetchAttachmentFolderPath()
|
||||
{
|
||||
return m_node->fetchAttachmentFolderPath();
|
||||
}
|
||||
|
||||
QStringList NodeBufferProvider::addAttachment(const QString &p_destFolderPath, const QStringList &p_files)
|
||||
{
|
||||
return m_node->addAttachment(p_destFolderPath, p_files);
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::newAttachmentFile(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
return m_node->newAttachmentFile(p_destFolderPath, p_name);
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
return m_node->newAttachmentFolder(p_destFolderPath, p_name);
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::renameAttachment(const QString &p_path, const QString &p_name)
|
||||
{
|
||||
return m_node->renameAttachment(p_path, p_name);
|
||||
}
|
||||
|
||||
void NodeBufferProvider::removeAttachment(const QStringList &p_paths)
|
||||
{
|
||||
return m_node->removeAttachment(p_paths);
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::insertImage(const QString &p_srcImagePath, const QString &p_imageFileName)
|
||||
{
|
||||
return m_node->insertImage(p_srcImagePath, p_imageFileName);
|
||||
}
|
||||
|
||||
QString NodeBufferProvider::insertImage(const QImage &p_image, const QString &p_imageFileName)
|
||||
{
|
||||
return m_node->insertImage(p_image, p_imageFileName);
|
||||
}
|
||||
|
||||
void NodeBufferProvider::removeImage(const QString &p_imagePath)
|
||||
{
|
||||
m_node->removeImage(p_imagePath);
|
||||
}
|
||||
|
||||
bool NodeBufferProvider::isAttachmentSupported() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Node *NodeBufferProvider::getNode() const
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
bool NodeBufferProvider::isReadOnly() const
|
||||
{
|
||||
return m_node->isReadOnly();
|
||||
}
|
72
src/core/buffer/nodebufferprovider.h
Normal file
72
src/core/buffer/nodebufferprovider.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef NODEBUFFERPROVIDER_H
|
||||
#define NODEBUFFERPROVIDER_H
|
||||
|
||||
#include "bufferprovider.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Buffer provider based on an internal node.
|
||||
class NodeBufferProvider : public BufferProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeBufferProvider(Node *p_node, QObject *p_parent = nullptr);
|
||||
|
||||
Buffer::ProviderType getType() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool match(const Node *p_node) const Q_DECL_OVERRIDE;
|
||||
|
||||
bool match(const QString &p_filePath) const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getPath() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getContentPath() const Q_DECL_OVERRIDE;
|
||||
|
||||
void write(const QString &p_content) Q_DECL_OVERRIDE;
|
||||
|
||||
QString read() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString fetchImageFolderPath() Q_DECL_OVERRIDE;
|
||||
|
||||
bool isChildOf(const Node *p_node) const Q_DECL_OVERRIDE;
|
||||
|
||||
Node *getNode() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getAttachmentFolder() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString fetchAttachmentFolderPath() Q_DECL_OVERRIDE;
|
||||
|
||||
QStringList addAttachment(const QString &p_destFolderPath, const QStringList &p_files) Q_DECL_OVERRIDE;
|
||||
|
||||
QString newAttachmentFile(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
QString newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
QString renameAttachment(const QString &p_path, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeAttachment(const QStringList &p_paths) Q_DECL_OVERRIDE;
|
||||
|
||||
QString insertImage(const QString &p_srcImagePath, const QString &p_imageFileName) Q_DECL_OVERRIDE;
|
||||
|
||||
QString insertImage(const QImage &p_image, const QString &p_imageFileName) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeImage(const QString &p_imagePath) Q_DECL_OVERRIDE;
|
||||
|
||||
bool isAttachmentSupported() const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isReadOnly() const Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
Node *m_node = nullptr;
|
||||
|
||||
// Used as cache.
|
||||
QString m_path;
|
||||
|
||||
// Used as cache.
|
||||
QString m_contentPath;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NODEBUFFERPROVIDER_H
|
17
src/core/buffer/textbuffer.cpp
Normal file
17
src/core/buffer/textbuffer.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "textbuffer.h"
|
||||
|
||||
#include <widgets/textviewwindow.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
TextBuffer::TextBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
: Buffer(p_parameters, p_parent)
|
||||
{
|
||||
}
|
||||
|
||||
ViewWindow *TextBuffer::createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent)
|
||||
{
|
||||
Q_UNUSED(p_paras);
|
||||
return new TextViewWindow(p_parent);
|
||||
}
|
20
src/core/buffer/textbuffer.h
Normal file
20
src/core/buffer/textbuffer.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef TEXTBUFFER_H
|
||||
#define TEXTBUFFER_H
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class TextBuffer : public Buffer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
protected:
|
||||
ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent) Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TEXTBUFFER_H
|
11
src/core/buffer/textbufferfactory.cpp
Normal file
11
src/core/buffer/textbufferfactory.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "textbufferfactory.h"
|
||||
|
||||
#include "textbuffer.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
Buffer *TextBufferFactory::createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent)
|
||||
{
|
||||
return new TextBuffer(p_parameters, p_parent);
|
||||
}
|
17
src/core/buffer/textbufferfactory.h
Normal file
17
src/core/buffer/textbufferfactory.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef TEXTBUFFERFACTORY_H
|
||||
#define TEXTBUFFERFACTORY_H
|
||||
|
||||
#include "ibufferfactory.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Buffer factory for text file.
|
||||
class TextBufferFactory : public IBufferFactory
|
||||
{
|
||||
public:
|
||||
Buffer *createBuffer(const BufferParameters &p_parameters,
|
||||
QObject *p_parent) Q_DECL_OVERRIDE;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TEXTBUFFERFACTORY_H
|
178
src/core/buffermgr.cpp
Normal file
178
src/core/buffermgr.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
#include "buffermgr.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QDebug>
|
||||
|
||||
#include <notebook/node.h>
|
||||
#include <buffer/filetypehelper.h>
|
||||
#include <buffer/markdownbufferfactory.h>
|
||||
#include <buffer/textbufferfactory.h>
|
||||
#include <buffer/buffer.h>
|
||||
#include <buffer/nodebufferprovider.h>
|
||||
#include <buffer/filebufferprovider.h>
|
||||
#include <utils/widgetutils.h>
|
||||
#include "notebookmgr.h"
|
||||
#include "vnotex.h"
|
||||
|
||||
#include "fileopenparameters.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
BufferMgr::BufferMgr(QObject *p_parent)
|
||||
: QObject(p_parent)
|
||||
{
|
||||
}
|
||||
|
||||
BufferMgr::~BufferMgr()
|
||||
{
|
||||
Q_ASSERT(m_buffers.isEmpty());
|
||||
}
|
||||
|
||||
void BufferMgr::init()
|
||||
{
|
||||
initBufferServer();
|
||||
}
|
||||
|
||||
void BufferMgr::initBufferServer()
|
||||
{
|
||||
m_bufferServer.reset(new NameBasedServer<IBufferFactory>);
|
||||
|
||||
// Markdown.
|
||||
auto markdownFactory = QSharedPointer<MarkdownBufferFactory>::create();
|
||||
m_bufferServer->registerItem(FileTypeHelper::s_markdownFileType, markdownFactory);
|
||||
|
||||
// Text.
|
||||
auto textFactory = QSharedPointer<TextBufferFactory>::create();
|
||||
m_bufferServer->registerItem(FileTypeHelper::s_textFileType, textFactory);
|
||||
}
|
||||
|
||||
void BufferMgr::open(Node *p_node, const QSharedPointer<FileOpenParameters> &p_paras)
|
||||
{
|
||||
if (!p_node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_node->getType() == Node::Type::Folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto buffer = findBuffer(p_node);
|
||||
if (!buffer) {
|
||||
auto nodePath = p_node->fetchAbsolutePath();
|
||||
auto fileType = FileTypeHelper::fileType(nodePath);
|
||||
auto factory = m_bufferServer->getItem(fileType);
|
||||
if (!factory) {
|
||||
// No factory to open this file type.
|
||||
qInfo() << "File will be opened by system:" << nodePath;
|
||||
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(nodePath));
|
||||
return;
|
||||
}
|
||||
|
||||
BufferParameters paras;
|
||||
paras.m_provider.reset(new NodeBufferProvider(p_node));
|
||||
buffer = factory->createBuffer(paras, this);
|
||||
addBuffer(buffer);
|
||||
}
|
||||
|
||||
Q_ASSERT(buffer);
|
||||
emit bufferRequested(buffer, p_paras);
|
||||
}
|
||||
|
||||
void BufferMgr::open(const QString &p_filePath, const QSharedPointer<FileOpenParameters> &p_paras)
|
||||
{
|
||||
if (p_filePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QFileInfo info(p_filePath);
|
||||
if (!info.exists() || info.isDir()) {
|
||||
qWarning() << QString("failed to open file %1 exists:%2 isDir:%3").arg(p_filePath).arg(info.exists()).arg(info.isDir());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it is an internal node or not.
|
||||
auto node = loadNodeByPath(p_filePath);
|
||||
if (node) {
|
||||
open(node.data(), p_paras);
|
||||
return;
|
||||
}
|
||||
|
||||
auto buffer = findBuffer(p_filePath);
|
||||
if (!buffer) {
|
||||
// Open it as external file.
|
||||
auto fileType = FileTypeHelper::fileType(p_filePath);
|
||||
auto factory = m_bufferServer->getItem(fileType);
|
||||
if (!factory) {
|
||||
// No factory to open this file type.
|
||||
qInfo() << "File will be opened by system:" << p_filePath;
|
||||
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(p_filePath));
|
||||
return;
|
||||
}
|
||||
|
||||
BufferParameters paras;
|
||||
paras.m_provider.reset(new FileBufferProvider(p_filePath,
|
||||
p_paras->m_nodeAttachedTo,
|
||||
p_paras->m_readOnly));
|
||||
buffer = factory->createBuffer(paras, this);
|
||||
addBuffer(buffer);
|
||||
}
|
||||
|
||||
Q_ASSERT(buffer);
|
||||
emit bufferRequested(buffer, p_paras);
|
||||
}
|
||||
|
||||
Buffer *BufferMgr::findBuffer(const Node *p_node) const
|
||||
{
|
||||
auto it = std::find_if(m_buffers.constBegin(),
|
||||
m_buffers.constEnd(),
|
||||
[p_node](const Buffer *p_buffer) {
|
||||
return p_buffer->match(p_node);
|
||||
});
|
||||
if (it != m_buffers.constEnd()) {
|
||||
return *it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Buffer *BufferMgr::findBuffer(const QString &p_filePath) const
|
||||
{
|
||||
auto it = std::find_if(m_buffers.constBegin(),
|
||||
m_buffers.constEnd(),
|
||||
[p_filePath](const Buffer *p_buffer) {
|
||||
return p_buffer->match(p_filePath);
|
||||
});
|
||||
if (it != m_buffers.constEnd()) {
|
||||
return *it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BufferMgr::addBuffer(Buffer *p_buffer)
|
||||
{
|
||||
m_buffers.push_back(p_buffer);
|
||||
connect(p_buffer, &Buffer::attachedViewWindowEmpty,
|
||||
this, [this, p_buffer]() {
|
||||
qDebug() << "delete buffer without attached view window"
|
||||
<< p_buffer->getName();
|
||||
m_buffers.removeAll(p_buffer);
|
||||
p_buffer->close();
|
||||
p_buffer->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
QSharedPointer<Node> BufferMgr::loadNodeByPath(const QString &p_path)
|
||||
{
|
||||
const auto ¬ebooks = VNoteX::getInst().getNotebookMgr().getNotebooks();
|
||||
for (const auto &nb : notebooks) {
|
||||
auto node = nb->loadNodeByPath(p_path);
|
||||
if (node) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
55
src/core/buffermgr.h
Normal file
55
src/core/buffermgr.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef BUFFERMGR_H
|
||||
#define BUFFERMGR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
#include <QSharedPointer>
|
||||
#include <QVector>
|
||||
|
||||
#include "namebasedserver.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class IBufferFactory;
|
||||
class Node;
|
||||
class Buffer;
|
||||
struct FileOpenParameters;
|
||||
|
||||
class BufferMgr : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BufferMgr(QObject *p_parent = nullptr);
|
||||
|
||||
~BufferMgr();
|
||||
|
||||
void init();
|
||||
|
||||
public slots:
|
||||
void open(Node *p_node, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||
|
||||
void open(const QString &p_filePath, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||
|
||||
signals:
|
||||
void bufferRequested(Buffer *p_buffer, const QSharedPointer<FileOpenParameters> &p_paras);
|
||||
|
||||
private:
|
||||
void initBufferServer();
|
||||
|
||||
Buffer *findBuffer(const Node *p_node) const;
|
||||
|
||||
Buffer *findBuffer(const QString &p_filePath) const;
|
||||
|
||||
void addBuffer(Buffer *p_buffer);
|
||||
|
||||
// Try to load @p_path as a node if it is within one notebook.
|
||||
QSharedPointer<Node> loadNodeByPath(const QString &p_path);
|
||||
|
||||
QSharedPointer<NameBasedServer<IBufferFactory>> m_bufferServer;
|
||||
|
||||
// Managed by QObject.
|
||||
QVector<Buffer *> m_buffers;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // BUFFERMGR_H
|
187
src/core/clipboarddata.cpp
Normal file
187
src/core/clipboarddata.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
#include "clipboarddata.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "exception.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const QString NodeClipboardDataItem::c_notebookId = "notebook_id";
|
||||
|
||||
const QString NodeClipboardDataItem::c_nodePath = "node_path";
|
||||
|
||||
NodeClipboardDataItem::NodeClipboardDataItem()
|
||||
{
|
||||
}
|
||||
|
||||
NodeClipboardDataItem::NodeClipboardDataItem(ID p_notebookId, const QString &p_nodePath)
|
||||
: m_notebookId(p_notebookId),
|
||||
m_nodeRelativePath(p_nodePath)
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject NodeClipboardDataItem::toJson() const
|
||||
{
|
||||
QJsonObject jobj;
|
||||
jobj[c_notebookId] = QString::number(m_notebookId);
|
||||
jobj[c_nodePath] = m_nodeRelativePath;
|
||||
return jobj;
|
||||
}
|
||||
|
||||
void NodeClipboardDataItem::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
Q_ASSERT(p_jobj.contains(c_notebookId) && p_jobj.contains(c_nodePath));
|
||||
auto idRet = stringToID(p_jobj[c_notebookId].toString());
|
||||
Q_ASSERT(idRet.first);
|
||||
m_notebookId = idRet.second;
|
||||
m_nodeRelativePath = p_jobj[c_nodePath].toString();
|
||||
}
|
||||
|
||||
|
||||
const QString ClipboardData::c_instanceId = "instance_id";
|
||||
|
||||
const QString ClipboardData::c_action = "action";
|
||||
|
||||
const QString ClipboardData::c_data = "data";
|
||||
|
||||
ClipboardData::ClipboardData()
|
||||
{
|
||||
}
|
||||
|
||||
ClipboardData::ClipboardData(ID p_instanceId, Action p_action)
|
||||
: m_instanceId(p_instanceId),
|
||||
m_action(p_action)
|
||||
{
|
||||
}
|
||||
|
||||
void ClipboardData::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (!p_jobj.contains(c_instanceId)
|
||||
|| !p_jobj.contains(c_action)
|
||||
|| !p_jobj.contains(c_data)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
return;
|
||||
}
|
||||
|
||||
auto idRet = stringToID(p_jobj[c_instanceId].toString());
|
||||
if (!idRet.first) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
return;
|
||||
}
|
||||
m_instanceId = idRet.second;
|
||||
|
||||
int act = p_jobj[c_action].toInt(Action::Invalid);
|
||||
m_action = intToAction(act);
|
||||
if (m_action == Action::Invalid) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to parse ClipboardData from json (%1)").arg(p_jobj.keys().join(',')));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &item : p_jobj[c_data].toArray()) {
|
||||
auto dataItem = createClipboardDataItem(m_action);
|
||||
dataItem->fromJson(item.toObject());
|
||||
m_data.push_back(dataItem);
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject ClipboardData::toJson() const
|
||||
{
|
||||
QJsonObject jobj;
|
||||
jobj[c_instanceId] = QString::number(m_instanceId);
|
||||
jobj[c_action] = static_cast<int>(m_action);
|
||||
|
||||
QJsonArray data;
|
||||
for (const auto& item : m_data) {
|
||||
data.append(item->toJson());
|
||||
}
|
||||
jobj[c_data] = data;
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
ClipboardData::Action ClipboardData::intToAction(int p_act) const
|
||||
{
|
||||
Action act = Action::Invalid;
|
||||
if (p_act >= Action::CopyNode && p_act < Action::Invalid) {
|
||||
act = static_cast<Action>(p_act);
|
||||
}
|
||||
|
||||
return act;
|
||||
}
|
||||
|
||||
void ClipboardData::clear()
|
||||
{
|
||||
m_instanceId = 0;
|
||||
m_action = Action::Invalid;
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
QSharedPointer<ClipboardDataItem> ClipboardData::createClipboardDataItem(Action p_act)
|
||||
{
|
||||
switch (p_act) {
|
||||
case Action::CopyNode:
|
||||
case Action::MoveNode:
|
||||
return QSharedPointer<NodeClipboardDataItem>::create();
|
||||
|
||||
case Action::Invalid:
|
||||
Q_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ClipboardData::addItem(const QSharedPointer<ClipboardDataItem> &p_item)
|
||||
{
|
||||
Q_ASSERT(p_item);
|
||||
m_data.push_back(p_item);
|
||||
}
|
||||
|
||||
QString ClipboardData::toJsonText() const
|
||||
{
|
||||
auto data = QJsonDocument(toJson()).toJson();
|
||||
return QString::fromUtf8(data);
|
||||
}
|
||||
|
||||
QSharedPointer<ClipboardData> ClipboardData::fromJsonText(const QString &p_json)
|
||||
{
|
||||
if (p_json.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto data = QSharedPointer<ClipboardData>::create();
|
||||
auto jobj = QJsonDocument::fromJson(p_json.toUtf8()).object();
|
||||
if (jobj.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
try {
|
||||
data->fromJson(jobj);
|
||||
} catch (Exception &p_e) {
|
||||
Q_UNUSED(p_e);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
const QVector<QSharedPointer<ClipboardDataItem>> &ClipboardData::getData() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
ID ClipboardData::getInstanceId() const
|
||||
{
|
||||
return m_instanceId;
|
||||
}
|
||||
|
||||
ClipboardData::Action ClipboardData::getAction() const
|
||||
{
|
||||
return m_action;
|
||||
}
|
85
src/core/clipboarddata.h
Normal file
85
src/core/clipboarddata.h
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef CLIPBOARDDATA_H
|
||||
#define CLIPBOARDDATA_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QJsonObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class ClipboardDataItem
|
||||
{
|
||||
public:
|
||||
virtual ~ClipboardDataItem()
|
||||
{
|
||||
}
|
||||
|
||||
virtual QJsonObject toJson() const = 0;
|
||||
|
||||
virtual void fromJson(const QJsonObject &p_jobj) = 0;
|
||||
};
|
||||
|
||||
class NodeClipboardDataItem : public ClipboardDataItem
|
||||
{
|
||||
public:
|
||||
NodeClipboardDataItem();
|
||||
|
||||
NodeClipboardDataItem(ID p_notebookId, const QString &p_nodePath);
|
||||
|
||||
QJsonObject toJson() const Q_DECL_OVERRIDE;
|
||||
|
||||
void fromJson(const QJsonObject &p_jobj) Q_DECL_OVERRIDE;
|
||||
|
||||
ID m_notebookId;
|
||||
QString m_nodeRelativePath;
|
||||
|
||||
private:
|
||||
static const QString c_notebookId;
|
||||
static const QString c_nodePath;
|
||||
};
|
||||
|
||||
class ClipboardData
|
||||
{
|
||||
public:
|
||||
enum Action { CopyNode, MoveNode, Invalid };
|
||||
|
||||
ClipboardData();
|
||||
|
||||
ClipboardData(ID p_instanceId, Action p_action);
|
||||
|
||||
ID getInstanceId() const;
|
||||
|
||||
ClipboardData::Action getAction() const;
|
||||
|
||||
const QVector<QSharedPointer<ClipboardDataItem>> &getData() const;
|
||||
|
||||
void addItem(const QSharedPointer<ClipboardDataItem> &p_item);
|
||||
|
||||
QString toJsonText() const;
|
||||
|
||||
static QSharedPointer<ClipboardData> fromJsonText(const QString &p_json);
|
||||
|
||||
private:
|
||||
void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
QJsonObject toJson() const;
|
||||
|
||||
ClipboardData::Action intToAction(int p_act) const;
|
||||
|
||||
void clear();
|
||||
|
||||
static QSharedPointer<ClipboardDataItem> createClipboardDataItem(Action p_act);
|
||||
|
||||
ID m_instanceId = 0;
|
||||
Action m_action = Action::Invalid;
|
||||
QVector<QSharedPointer<ClipboardDataItem>> m_data;
|
||||
|
||||
static const QString c_instanceId;
|
||||
static const QString c_action;
|
||||
static const QString c_data;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // CLIPBOARDDATA_H
|
376
src/core/configmgr.cpp
Normal file
376
src/core/configmgr.cpp
Normal file
@ -0,0 +1,376 @@
|
||||
#include "configmgr.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
#include <QStandardPaths>
|
||||
#include <QJsonDocument>
|
||||
#include <QScopeGuard>
|
||||
#include <QResource>
|
||||
#include <QPixmap>
|
||||
#include <QSplashScreen>
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include <utils/pathutils.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include "exception.h"
|
||||
#include <utils/utils.h>
|
||||
|
||||
#include "mainconfig.h"
|
||||
#include "coreconfig.h"
|
||||
#include "sessionconfig.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
#define VX_DEBUG_WEB
|
||||
#endif
|
||||
|
||||
const QString ConfigMgr::c_orgName = "VNote";
|
||||
|
||||
const QString ConfigMgr::c_appName = "VNoteX";
|
||||
|
||||
const QString ConfigMgr::c_configFileName = "vnotex.json";
|
||||
|
||||
const QString ConfigMgr::c_sessionFileName = "session.json";
|
||||
|
||||
const QJsonObject &ConfigMgr::Settings::getJson() const
|
||||
{
|
||||
return m_jobj;
|
||||
}
|
||||
|
||||
QSharedPointer<ConfigMgr::Settings> ConfigMgr::Settings::fromFile(const QString &p_jsonFilePath)
|
||||
{
|
||||
if (!QFileInfo::exists(p_jsonFilePath)) {
|
||||
qWarning() << "return empty Settings from non-exist config file" << p_jsonFilePath;
|
||||
return QSharedPointer<Settings>::create();
|
||||
}
|
||||
|
||||
auto bytes = FileUtils::readFile(p_jsonFilePath);
|
||||
return QSharedPointer<Settings>::create(QJsonDocument::fromJson(bytes).object());
|
||||
}
|
||||
|
||||
void ConfigMgr::Settings::writeToFile(const QString &p_jsonFilePath) const
|
||||
{
|
||||
FileUtils::writeFile(p_jsonFilePath, QJsonDocument(this->m_jobj).toJson());
|
||||
}
|
||||
|
||||
ConfigMgr::ConfigMgr(QObject *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_config(new MainConfig(this)),
|
||||
m_sessionConfig(new SessionConfig(this))
|
||||
{
|
||||
locateConfigFolder();
|
||||
|
||||
checkAppConfig();
|
||||
|
||||
m_config->init();
|
||||
m_sessionConfig->init();
|
||||
}
|
||||
|
||||
ConfigMgr::~ConfigMgr()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ConfigMgr::locateConfigFolder()
|
||||
{
|
||||
// Check app config.
|
||||
{
|
||||
const QString configFolderName("vnotex_files");
|
||||
QString folderPath(QCoreApplication::applicationDirPath()
|
||||
+ '/' + configFolderName);
|
||||
if (QDir(folderPath).exists()) {
|
||||
// Config folder in app/.
|
||||
m_appConfigFolderPath = PathUtils::cleanPath(folderPath);
|
||||
} else {
|
||||
m_appConfigFolderPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
}
|
||||
}
|
||||
|
||||
// Check user config.
|
||||
{
|
||||
const QString configFolderName("user_files");
|
||||
QString folderPath(QCoreApplication::applicationDirPath()
|
||||
+ '/' + configFolderName);
|
||||
if (QDir(folderPath).exists()) {
|
||||
// Config folder in app/.
|
||||
m_userConfigFolderPath = PathUtils::cleanPath(folderPath);
|
||||
} else {
|
||||
m_userConfigFolderPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
|
||||
// Make sure it exists.
|
||||
QDir dir(m_userConfigFolderPath);
|
||||
dir.mkpath(m_userConfigFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(m_appConfigFolderPath != m_userConfigFolderPath);
|
||||
qInfo() << "app config folder" << m_appConfigFolderPath;
|
||||
qInfo() << "user config folder" << m_userConfigFolderPath;
|
||||
}
|
||||
|
||||
void ConfigMgr::checkAppConfig()
|
||||
{
|
||||
bool needUpdate = false;
|
||||
QDir appConfigDir(m_appConfigFolderPath);
|
||||
if (!appConfigDir.exists()) {
|
||||
needUpdate = true;
|
||||
appConfigDir.mkpath(m_appConfigFolderPath);
|
||||
} else {
|
||||
if (!appConfigDir.exists(c_configFileName)) {
|
||||
needUpdate = true;
|
||||
} else {
|
||||
// Check version of config file.
|
||||
auto defaultSettings = getSettings(Source::Default);
|
||||
auto appSettings = getSettings(Source::App);
|
||||
auto defaultVersion = MainConfig::getVersion(defaultSettings->getJson());
|
||||
auto appVersion = MainConfig::getVersion(appSettings->getJson());
|
||||
if (defaultVersion != appVersion) {
|
||||
needUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needUpdate) {
|
||||
FileUtils::removeDir(m_appConfigFolderPath);
|
||||
// Wait for the OS delete the folder.
|
||||
Utils::sleepWait(1000);
|
||||
appConfigDir.mkpath(m_appConfigFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
const auto mainConfigFilePath = appConfigDir.filePath(c_configFileName);
|
||||
|
||||
#ifndef VX_DEBUG_WEB
|
||||
if (!needUpdate) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
qInfo() << "update app config files in" << m_appConfigFolderPath;
|
||||
|
||||
Q_ASSERT(appConfigDir.exists());
|
||||
|
||||
QPixmap pixmap(":/vnotex/data/core/logo/vnote.png");
|
||||
QScopedPointer<QSplashScreen> splash(new QSplashScreen(pixmap));
|
||||
splash->show();
|
||||
|
||||
// Load extra data.
|
||||
splash->showMessage("Loading extra resource data");
|
||||
const QString extraRcc(QStringLiteral("extra.rcc"));
|
||||
bool ret = QResource::registerResource(extraRcc);
|
||||
if (!ret) {
|
||||
Exception::throwOne(Exception::Type::FailToReadFile,
|
||||
QString("failed to register resource file %1").arg(extraRcc));
|
||||
}
|
||||
auto cleanup = qScopeGuard([extraRcc]() {
|
||||
QResource::unregisterResource(extraRcc);
|
||||
});
|
||||
|
||||
const QString extraDataRoot(QStringLiteral(":/vnotex/data/extra"));
|
||||
|
||||
#ifdef VX_DEBUG_WEB
|
||||
if (!needUpdate) {
|
||||
// Always update main config file and web folder.
|
||||
qDebug() << "forced to update main config file and web folder for debugging";
|
||||
splash->showMessage("update main config file and web folder for debugging");
|
||||
|
||||
// Cancel the read-only permission of the main config file.
|
||||
QFile::setPermissions(mainConfigFilePath, QFile::WriteUser);
|
||||
FileUtils::removeFile(mainConfigFilePath);
|
||||
FileUtils::removeDir(appConfigDir.filePath(QStringLiteral("web")));
|
||||
|
||||
// Wait for the OS delete the folder.
|
||||
Utils::sleepWait(1000);
|
||||
|
||||
FileUtils::copyFile(getConfigFilePath(Source::Default), mainConfigFilePath);
|
||||
FileUtils::copyDir(extraDataRoot + QStringLiteral("/web"),
|
||||
appConfigDir.filePath(QStringLiteral("web")));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
Q_ASSERT(needUpdate);
|
||||
#endif
|
||||
|
||||
// Copy themes.
|
||||
qApp->processEvents();
|
||||
splash->showMessage("Copying themes");
|
||||
FileUtils::copyDir(extraDataRoot + QStringLiteral("/themes"),
|
||||
appConfigDir.filePath(QStringLiteral("themes")));
|
||||
|
||||
// Copy docs.
|
||||
qApp->processEvents();
|
||||
splash->showMessage("Copying docs");
|
||||
FileUtils::copyDir(extraDataRoot + QStringLiteral("/docs"),
|
||||
appConfigDir.filePath(QStringLiteral("docs")));
|
||||
|
||||
// Copy syntax-highlighting.
|
||||
qApp->processEvents();
|
||||
splash->showMessage("Copying syntax-highlighting");
|
||||
FileUtils::copyDir(extraDataRoot + QStringLiteral("/syntax-highlighting"),
|
||||
appConfigDir.filePath(QStringLiteral("syntax-highlighting")));
|
||||
|
||||
// Copy web.
|
||||
qApp->processEvents();
|
||||
splash->showMessage("Copying web");
|
||||
FileUtils::copyDir(extraDataRoot + QStringLiteral("/web"),
|
||||
appConfigDir.filePath(QStringLiteral("web")));
|
||||
|
||||
// Main config file.
|
||||
FileUtils::copyFile(getConfigFilePath(Source::Default), appConfigDir.filePath(c_configFileName));
|
||||
|
||||
}
|
||||
|
||||
QString ConfigMgr::getConfigFilePath(Source p_src) const
|
||||
{
|
||||
QString configPath;
|
||||
switch (p_src) {
|
||||
case Source::Default:
|
||||
configPath = QStringLiteral(":/vnotex/data/core/") + c_configFileName;
|
||||
break;
|
||||
|
||||
case Source::App:
|
||||
configPath = m_appConfigFolderPath + QLatin1Char('/') + c_configFileName;
|
||||
break;
|
||||
|
||||
case Source::User:
|
||||
{
|
||||
configPath = m_userConfigFolderPath + QLatin1Char('/') + c_configFileName;
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::Session:
|
||||
{
|
||||
configPath = m_userConfigFolderPath + QLatin1Char('/') + c_sessionFileName;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
return configPath;
|
||||
}
|
||||
|
||||
QSharedPointer<ConfigMgr::Settings> ConfigMgr::getSettings(Source p_src) const
|
||||
{
|
||||
|
||||
return ConfigMgr::Settings::fromFile(getConfigFilePath(p_src));
|
||||
}
|
||||
|
||||
void ConfigMgr::writeUserSettings(const QJsonObject &p_jobj)
|
||||
{
|
||||
Settings settings(p_jobj);
|
||||
settings.writeToFile(getConfigFilePath(Source::User));
|
||||
}
|
||||
|
||||
void ConfigMgr::writeSessionSettings(const QJsonObject &p_jobj)
|
||||
{
|
||||
Settings settings(p_jobj);
|
||||
settings.writeToFile(getConfigFilePath(Source::Session));
|
||||
}
|
||||
|
||||
MainConfig &ConfigMgr::getConfig()
|
||||
{
|
||||
return *m_config;
|
||||
}
|
||||
|
||||
SessionConfig &ConfigMgr::getSessionConfig()
|
||||
{
|
||||
return *m_sessionConfig;
|
||||
}
|
||||
|
||||
CoreConfig &ConfigMgr::getCoreConfig()
|
||||
{
|
||||
return m_config->getCoreConfig();
|
||||
}
|
||||
|
||||
EditorConfig &ConfigMgr::getEditorConfig()
|
||||
{
|
||||
return m_config->getEditorConfig();
|
||||
}
|
||||
|
||||
WidgetConfig &ConfigMgr::getWidgetConfig()
|
||||
{
|
||||
return m_config->getWidgetConfig();
|
||||
}
|
||||
|
||||
QString ConfigMgr::getAppFolder() const
|
||||
{
|
||||
return m_appConfigFolderPath;
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserFolder() const
|
||||
{
|
||||
return m_userConfigFolderPath;
|
||||
}
|
||||
|
||||
QString ConfigMgr::getAppThemeFolder() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_appConfigFolderPath, QStringLiteral("themes"));
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserThemeFolder() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_userConfigFolderPath, QStringLiteral("themes"));
|
||||
}
|
||||
|
||||
QString ConfigMgr::getAppDocsFolder() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_appConfigFolderPath, QStringLiteral("docs"));
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserDocsFolder() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_userConfigFolderPath, QStringLiteral("docs"));
|
||||
}
|
||||
|
||||
QString ConfigMgr::getAppSyntaxHighlightingFolder() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_appConfigFolderPath,
|
||||
QStringLiteral("syntax-highlighting"));
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserSyntaxHighlightingFolder() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_userConfigFolderPath,
|
||||
QStringLiteral("syntax-highlighting"));
|
||||
}
|
||||
|
||||
QString ConfigMgr::getUserOrAppFile(const QString &p_filePath) const
|
||||
{
|
||||
QFileInfo fi(p_filePath);
|
||||
if (fi.isAbsolute()) {
|
||||
return p_filePath;
|
||||
}
|
||||
|
||||
// Check user folder first.
|
||||
QDir userConfigDir(m_userConfigFolderPath);
|
||||
if (userConfigDir.exists(p_filePath)) {
|
||||
return userConfigDir.absoluteFilePath(p_filePath);
|
||||
}
|
||||
|
||||
// App folder.
|
||||
QDir appConfigDir(m_appConfigFolderPath);
|
||||
return appConfigDir.absoluteFilePath(p_filePath);
|
||||
}
|
||||
|
||||
QString ConfigMgr::locateSessionConfigFilePathAtBootstrap()
|
||||
{
|
||||
// QApplication is not init yet, so org and app name are empty here.
|
||||
auto folderPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
folderPath = PathUtils::concatenateFilePath(folderPath, c_orgName + "/" + c_appName);
|
||||
QDir dir(folderPath);
|
||||
if (dir.exists(c_sessionFileName)) {
|
||||
qInfo() << "locateSessionConfigFilePathAtBootstrap" << folderPath;
|
||||
return dir.filePath(c_sessionFileName);
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString ConfigMgr::getLogFile() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(ConfigMgr::getInst().getUserFolder(), "vnotex.log");
|
||||
}
|
141
src/core/configmgr.h
Normal file
141
src/core/configmgr.h
Normal file
@ -0,0 +1,141 @@
|
||||
#ifndef CONFIGMGR_H
|
||||
#define CONFIGMGR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QJsonObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class MainConfig;
|
||||
class SessionConfig;
|
||||
class CoreConfig;
|
||||
class EditorConfig;
|
||||
class WidgetConfig;
|
||||
|
||||
class ConfigMgr : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class Source
|
||||
{
|
||||
Default,
|
||||
App,
|
||||
User,
|
||||
Session
|
||||
};
|
||||
|
||||
class Settings
|
||||
{
|
||||
public:
|
||||
Settings() = default;
|
||||
|
||||
Settings(const QJsonObject &p_jobj)
|
||||
: m_jobj(p_jobj)
|
||||
{
|
||||
}
|
||||
|
||||
const QJsonObject &getJson() const;
|
||||
|
||||
void writeToFile(const QString &p_jsonFilePath) const;
|
||||
|
||||
static QSharedPointer<Settings> fromFile(const QString &p_jsonFilePath);
|
||||
|
||||
private:
|
||||
QJsonObject m_jobj;
|
||||
};
|
||||
|
||||
static ConfigMgr &getInst()
|
||||
{
|
||||
static ConfigMgr inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
~ConfigMgr();
|
||||
|
||||
ConfigMgr(const ConfigMgr &) = delete;
|
||||
void operator=(const ConfigMgr &) = delete;
|
||||
|
||||
MainConfig &getConfig();
|
||||
|
||||
SessionConfig &getSessionConfig();
|
||||
|
||||
CoreConfig &getCoreConfig();
|
||||
|
||||
EditorConfig &getEditorConfig();
|
||||
|
||||
WidgetConfig &getWidgetConfig();
|
||||
|
||||
QString getAppFolder() const;
|
||||
|
||||
QString getUserFolder() const;
|
||||
|
||||
QString getLogFile() const;
|
||||
|
||||
QString getAppThemeFolder() const;
|
||||
|
||||
QString getUserThemeFolder() const;
|
||||
|
||||
QString getAppDocsFolder() const;
|
||||
|
||||
QString getUserDocsFolder() const;
|
||||
|
||||
QString getAppSyntaxHighlightingFolder() const;
|
||||
|
||||
QString getUserSyntaxHighlightingFolder() const;
|
||||
|
||||
// If @p_filePath is absolute, just return it.
|
||||
// Otherwise, first try to find it in user folder, then in app folder.
|
||||
QString getUserOrAppFile(const QString &p_filePath) const;
|
||||
|
||||
QString getConfigFilePath(Source p_src) const;
|
||||
|
||||
// Called at boostrap without QApplication instance.
|
||||
static QString locateSessionConfigFilePathAtBootstrap();
|
||||
|
||||
static const QString c_orgName;
|
||||
|
||||
static const QString c_appName;
|
||||
|
||||
public:
|
||||
// Used by IConfig.
|
||||
QSharedPointer<Settings> getSettings(Source p_src) const;
|
||||
|
||||
void writeUserSettings(const QJsonObject &p_jobj);
|
||||
|
||||
void writeSessionSettings(const QJsonObject &p_jobj);
|
||||
|
||||
signals:
|
||||
void editorConfigChanged();
|
||||
|
||||
private:
|
||||
explicit ConfigMgr(QObject *p_parent = nullptr);
|
||||
|
||||
// Locate the folder path where the config file exists.
|
||||
void locateConfigFolder();
|
||||
|
||||
// Check if app config exists and is updated.
|
||||
// Update it if in need.
|
||||
void checkAppConfig();
|
||||
|
||||
QScopedPointer<MainConfig> m_config;;
|
||||
|
||||
// Session config.
|
||||
QScopedPointer<SessionConfig> m_sessionConfig;
|
||||
|
||||
// Absolute path of the app config folder.
|
||||
QString m_appConfigFolderPath;
|
||||
|
||||
// Absolute path of the user config folder.
|
||||
QString m_userConfigFolderPath;
|
||||
|
||||
// Name of the core config file.
|
||||
static const QString c_configFileName;
|
||||
|
||||
// Name of the session config file.
|
||||
static const QString c_sessionFileName;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // CONFIGMGR_H
|
57
src/core/core.pri
Normal file
57
src/core/core.pri
Normal file
@ -0,0 +1,57 @@
|
||||
INCLUDEPATH *= $$PWD
|
||||
|
||||
include($$PWD/notebookbackend/notebookbackend.pri)
|
||||
|
||||
include($$PWD/versioncontroller/versioncontroller.pri)
|
||||
|
||||
include($$PWD/notebookconfigmgr/notebookconfigmgr.pri)
|
||||
|
||||
include($$PWD/notebook/notebook.pri)
|
||||
|
||||
include($$PWD/buffer/buffer.pri)
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/buffermgr.cpp \
|
||||
$$PWD/configmgr.cpp \
|
||||
$$PWD/coreconfig.cpp \
|
||||
$$PWD/editorconfig.cpp \
|
||||
$$PWD/htmltemplatehelper.cpp \
|
||||
$$PWD/logger.cpp \
|
||||
$$PWD/mainconfig.cpp \
|
||||
$$PWD/markdowneditorconfig.cpp \
|
||||
$$PWD/singleinstanceguard.cpp \
|
||||
$$PWD/texteditorconfig.cpp \
|
||||
$$PWD/vnotex.cpp \
|
||||
$$PWD/thememgr.cpp \
|
||||
$$PWD/notebookmgr.cpp \
|
||||
$$PWD/theme.cpp \
|
||||
$$PWD/sessionconfig.cpp \
|
||||
$$PWD/clipboarddata.cpp \
|
||||
$$PWD/widgetconfig.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/ViewerResource.h \
|
||||
$$PWD/buffermgr.h \
|
||||
$$PWD/configmgr.h \
|
||||
$$PWD/coreconfig.h \
|
||||
$$PWD/editorconfig.h \
|
||||
$$PWD/events.h \
|
||||
$$PWD/filelocator.h \
|
||||
$$PWD/fileopenparameters.h \
|
||||
$$PWD/htmltemplatehelper.h \
|
||||
$$PWD/logger.h \
|
||||
$$PWD/mainconfig.h \
|
||||
$$PWD/markdowneditorconfig.h \
|
||||
$$PWD/singleinstanceguard.h \
|
||||
$$PWD/iconfig.h \
|
||||
$$PWD/texteditorconfig.h \
|
||||
$$PWD/vnotex.h \
|
||||
$$PWD/thememgr.h \
|
||||
$$PWD/global.h \
|
||||
$$PWD/namebasedserver.h \
|
||||
$$PWD/exception.h \
|
||||
$$PWD/notebookmgr.h \
|
||||
$$PWD/theme.h \
|
||||
$$PWD/sessionconfig.h \
|
||||
$$PWD/clipboarddata.h \
|
||||
$$PWD/widgetconfig.h
|
124
src/core/coreconfig.cpp
Normal file
124
src/core/coreconfig.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
#include "coreconfig.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
#include <QLocale>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
#define READSTR(key) readString(appObj, userObj, (key))
|
||||
#define READINT(key) readInt(appObj, userObj, (key))
|
||||
|
||||
QStringList CoreConfig::s_availableLocales;
|
||||
|
||||
CoreConfig::CoreConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
|
||||
: IConfig(p_mgr, p_topConfig)
|
||||
{
|
||||
m_sessionName = QStringLiteral("core");
|
||||
}
|
||||
|
||||
const QString &CoreConfig::getTheme() const
|
||||
{
|
||||
return m_theme;
|
||||
}
|
||||
|
||||
void CoreConfig::init(const QJsonObject &p_app,
|
||||
const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(m_sessionName).toObject();
|
||||
const auto userObj = p_user.value(m_sessionName).toObject();
|
||||
|
||||
m_theme = READSTR(QStringLiteral("theme"));
|
||||
|
||||
m_locale = READSTR(QStringLiteral("locale"));
|
||||
if (!m_locale.isEmpty() && !getAvailableLocales().contains(m_locale)) {
|
||||
m_locale = QStringLiteral("en_US");
|
||||
}
|
||||
|
||||
loadShortcuts(appObj, userObj);
|
||||
|
||||
m_toolBarIconSize = READINT(QStringLiteral("toolbar_icon_size"));
|
||||
if (m_toolBarIconSize <= 0) {
|
||||
m_toolBarIconSize = 16;
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject CoreConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("theme")] = m_theme;
|
||||
obj[QStringLiteral("locale")] = m_locale;
|
||||
obj[QStringLiteral("shortcuts")] = saveShortcuts();
|
||||
obj[QStringLiteral("toolbar_icon_size")] = m_toolBarIconSize;
|
||||
return obj;
|
||||
}
|
||||
|
||||
const QString &CoreConfig::getLocale() const
|
||||
{
|
||||
return m_locale;
|
||||
}
|
||||
|
||||
void CoreConfig::setLocale(const QString &p_locale)
|
||||
{
|
||||
updateConfig(m_locale,
|
||||
p_locale,
|
||||
this);
|
||||
}
|
||||
|
||||
QString CoreConfig::getLocaleToUse() const
|
||||
{
|
||||
return QLocale().name();
|
||||
}
|
||||
|
||||
const QStringList &CoreConfig::getAvailableLocales()
|
||||
{
|
||||
if (s_availableLocales.isEmpty()) {
|
||||
s_availableLocales << QStringLiteral("en_US");
|
||||
s_availableLocales << QStringLiteral("zh_CN");
|
||||
}
|
||||
|
||||
return s_availableLocales;
|
||||
}
|
||||
|
||||
void CoreConfig::loadShortcuts(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(QStringLiteral("shortcuts")).toObject();
|
||||
const auto userObj = p_user.value(QStringLiteral("shortcuts")).toObject();
|
||||
|
||||
static const auto indexOfShortcutEnum = CoreConfig::staticMetaObject.indexOfEnumerator("Shortcut");
|
||||
Q_ASSERT(indexOfShortcutEnum >= 0);
|
||||
const auto metaEnum = CoreConfig::staticMetaObject.enumerator(indexOfShortcutEnum);
|
||||
// Skip the Max flag.
|
||||
for (int i = 0; i < metaEnum.keyCount() - 1; ++i) {
|
||||
m_shortcuts[i] = READSTR(metaEnum.key(i));
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject CoreConfig::saveShortcuts() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
static const auto indexOfShortcutEnum = CoreConfig::staticMetaObject.indexOfEnumerator("Shortcut");
|
||||
Q_ASSERT(indexOfShortcutEnum >= 0);
|
||||
const auto metaEnum = CoreConfig::staticMetaObject.enumerator(indexOfShortcutEnum);
|
||||
// Skip the Max flag.
|
||||
for (int i = 0; i < metaEnum.keyCount() - 1; ++i) {
|
||||
obj[metaEnum.key(i)] = m_shortcuts[i];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
const QString &CoreConfig::getShortcut(Shortcut p_shortcut) const
|
||||
{
|
||||
Q_ASSERT(p_shortcut < Shortcut::MaxShortcut);
|
||||
return m_shortcuts[p_shortcut];
|
||||
}
|
||||
|
||||
int CoreConfig::getToolBarIconSize() const
|
||||
{
|
||||
return m_toolBarIconSize;
|
||||
}
|
||||
|
||||
void CoreConfig::setToolBarIconSize(int p_size)
|
||||
{
|
||||
Q_ASSERT(p_size > 0);
|
||||
updateConfig(m_toolBarIconSize, p_size, this);
|
||||
}
|
73
src/core/coreconfig.h
Normal file
73
src/core/coreconfig.h
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef CORECONFIG_H
|
||||
#define CORECONFIG_H
|
||||
|
||||
#include "iconfig.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class CoreConfig : public IConfig
|
||||
{
|
||||
Q_GADGET
|
||||
public:
|
||||
enum Shortcut
|
||||
{
|
||||
FullScreen,
|
||||
ExpandContentArea,
|
||||
Settings,
|
||||
NewNote,
|
||||
CloseTab,
|
||||
NavigationDock,
|
||||
OutlineDock,
|
||||
NavigationMode,
|
||||
LocateNode,
|
||||
MaxShortcut
|
||||
};
|
||||
Q_ENUM(Shortcut)
|
||||
|
||||
CoreConfig(ConfigMgr *p_mgr, IConfig *p_topConfig);
|
||||
|
||||
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
|
||||
|
||||
QJsonObject toJson() const Q_DECL_OVERRIDE;
|
||||
|
||||
const QString &getTheme() const;
|
||||
|
||||
const QString &getLocale() const;
|
||||
void setLocale(const QString &p_locale);
|
||||
|
||||
// Should be called after locale is properly set.
|
||||
QString getLocaleToUse() const;
|
||||
|
||||
const QString &getShortcut(Shortcut p_shortcut) const;
|
||||
|
||||
int getToolBarIconSize() const;
|
||||
void setToolBarIconSize(int p_size);
|
||||
|
||||
static const QStringList &getAvailableLocales();
|
||||
|
||||
private:
|
||||
void loadShortcuts(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
|
||||
QJsonObject saveShortcuts() const;
|
||||
|
||||
// Theme name.
|
||||
QString m_theme;
|
||||
|
||||
// User-specified locale, such as zh_CN, en_US.
|
||||
// Empty if not specified.
|
||||
QString m_locale;
|
||||
|
||||
QString m_shortcuts[Shortcut::MaxShortcut];
|
||||
|
||||
// Icon size of MainWindow tool bar.
|
||||
int m_toolBarIconSize = 16;
|
||||
|
||||
static QStringList s_availableLocales;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // CORECONFIG_H
|
184
src/core/editorconfig.cpp
Normal file
184
src/core/editorconfig.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
#include "editorconfig.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
#include <QDebug>
|
||||
|
||||
#include "texteditorconfig.h"
|
||||
#include "markdowneditorconfig.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
#define READINT(key) readInt(appObj, userObj, (key))
|
||||
#define READSTR(key) readString(appObj, userObj, (key))
|
||||
|
||||
EditorConfig::EditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig)
|
||||
: IConfig(p_mgr, p_topConfig),
|
||||
m_textEditorConfig(new TextEditorConfig(p_mgr, p_topConfig)),
|
||||
m_markdownEditorConfig(new MarkdownEditorConfig(p_mgr, p_topConfig, m_textEditorConfig))
|
||||
{
|
||||
m_sessionName = QStringLiteral("editor");
|
||||
}
|
||||
|
||||
EditorConfig::~EditorConfig()
|
||||
{
|
||||
}
|
||||
|
||||
void EditorConfig::init(const QJsonObject &p_app,
|
||||
const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(m_sessionName).toObject();
|
||||
const auto userObj = p_user.value(m_sessionName).toObject();
|
||||
|
||||
loadCore(appObj, userObj);
|
||||
|
||||
m_textEditorConfig->init(appObj, userObj);
|
||||
m_markdownEditorConfig->init(appObj, userObj);
|
||||
}
|
||||
|
||||
void EditorConfig::loadCore(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(QStringLiteral("core")).toObject();
|
||||
const auto userObj = p_user.value(QStringLiteral("core")).toObject();
|
||||
|
||||
{
|
||||
m_toolBarIconSize = READINT(QStringLiteral("toolbar_icon_size"));
|
||||
if (m_toolBarIconSize <= 0) {
|
||||
m_toolBarIconSize = 14;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto autoSavePolicy = READSTR(QStringLiteral("auto_save_policy"));
|
||||
m_autoSavePolicy = stringToAutoSavePolicy(autoSavePolicy);
|
||||
}
|
||||
|
||||
m_backupFileDirectory = READSTR(QStringLiteral("backup_file_directory"));
|
||||
|
||||
m_backupFileExtension = READSTR(QStringLiteral("backup_file_extension"));
|
||||
|
||||
loadShortcuts(appObj, userObj);
|
||||
}
|
||||
|
||||
QJsonObject EditorConfig::saveCore() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("toolbar_icon_size")] = m_toolBarIconSize;
|
||||
obj[QStringLiteral("auto_save_policy")] = autoSavePolicyToString(m_autoSavePolicy);
|
||||
obj[QStringLiteral("backup_file_directory")] = m_backupFileDirectory;
|
||||
obj[QStringLiteral("backup_file_extension")] = m_backupFileExtension;
|
||||
obj[QStringLiteral("shortcuts")] = saveShortcuts();
|
||||
return obj;
|
||||
}
|
||||
|
||||
void EditorConfig::loadShortcuts(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(QStringLiteral("shortcuts")).toObject();
|
||||
const auto userObj = p_user.value(QStringLiteral("shortcuts")).toObject();
|
||||
|
||||
static const auto indexOfShortcutEnum = EditorConfig::staticMetaObject.indexOfEnumerator("Shortcut");
|
||||
Q_ASSERT(indexOfShortcutEnum >= 0);
|
||||
const auto metaEnum = EditorConfig::staticMetaObject.enumerator(indexOfShortcutEnum);
|
||||
// Skip the Max flag.
|
||||
for (int i = 0; i < metaEnum.keyCount() - 1; ++i) {
|
||||
m_shortcuts[i] = READSTR(metaEnum.key(i));
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject EditorConfig::saveShortcuts() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
static const auto indexOfShortcutEnum = EditorConfig::staticMetaObject.indexOfEnumerator("Shortcut");
|
||||
Q_ASSERT(indexOfShortcutEnum >= 0);
|
||||
const auto metaEnum = EditorConfig::staticMetaObject.enumerator(indexOfShortcutEnum);
|
||||
// Skip the Max flag.
|
||||
for (int i = 0; i < metaEnum.keyCount() - 1; ++i) {
|
||||
obj[metaEnum.key(i)] = m_shortcuts[i];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
QJsonObject EditorConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[m_textEditorConfig->getSessionName()] = m_textEditorConfig->toJson();
|
||||
obj[m_markdownEditorConfig->getSessionName()] = m_markdownEditorConfig->toJson();
|
||||
obj[QStringLiteral("core")] = saveCore();
|
||||
return obj;
|
||||
}
|
||||
|
||||
TextEditorConfig &EditorConfig::getTextEditorConfig()
|
||||
{
|
||||
return *m_textEditorConfig;
|
||||
}
|
||||
|
||||
const TextEditorConfig &EditorConfig::getTextEditorConfig() const
|
||||
{
|
||||
return *m_textEditorConfig;
|
||||
}
|
||||
|
||||
MarkdownEditorConfig &EditorConfig::getMarkdownEditorConfig()
|
||||
{
|
||||
return *m_markdownEditorConfig;
|
||||
}
|
||||
|
||||
const MarkdownEditorConfig &EditorConfig::getMarkdownEditorConfig() const
|
||||
{
|
||||
return *m_markdownEditorConfig;
|
||||
}
|
||||
|
||||
int EditorConfig::getToolBarIconSize() const
|
||||
{
|
||||
return m_toolBarIconSize;
|
||||
}
|
||||
|
||||
const QString &EditorConfig::getShortcut(Shortcut p_shortcut) const
|
||||
{
|
||||
Q_ASSERT(p_shortcut < Shortcut::MaxShortcut);
|
||||
return m_shortcuts[p_shortcut];
|
||||
}
|
||||
|
||||
QString EditorConfig::autoSavePolicyToString(AutoSavePolicy p_policy) const
|
||||
{
|
||||
switch (p_policy) {
|
||||
case AutoSavePolicy::None:
|
||||
return QStringLiteral("none");
|
||||
|
||||
case AutoSavePolicy::AutoSave:
|
||||
return QStringLiteral("autosave");
|
||||
|
||||
default:
|
||||
return QStringLiteral("backupfile");
|
||||
}
|
||||
}
|
||||
|
||||
EditorConfig::AutoSavePolicy EditorConfig::stringToAutoSavePolicy(const QString &p_str) const
|
||||
{
|
||||
auto policy = p_str.toLower();
|
||||
if (policy == QStringLiteral("none")) {
|
||||
return AutoSavePolicy::None;
|
||||
} else if (policy == QStringLiteral("autosave")) {
|
||||
return AutoSavePolicy::AutoSave;
|
||||
} else {
|
||||
return AutoSavePolicy::BackupFile;
|
||||
}
|
||||
}
|
||||
|
||||
EditorConfig::AutoSavePolicy EditorConfig::getAutoSavePolicy() const
|
||||
{
|
||||
return m_autoSavePolicy;
|
||||
}
|
||||
|
||||
void EditorConfig::setAutoSavePolicy(EditorConfig::AutoSavePolicy p_policy)
|
||||
{
|
||||
updateConfig(m_autoSavePolicy, p_policy, this);
|
||||
}
|
||||
|
||||
const QString &EditorConfig::getBackupFileDirectory() const
|
||||
{
|
||||
return m_backupFileDirectory;
|
||||
}
|
||||
|
||||
const QString &EditorConfig::getBackupFileExtension() const
|
||||
{
|
||||
return m_backupFileExtension;
|
||||
}
|
118
src/core/editorconfig.h
Normal file
118
src/core/editorconfig.h
Normal file
@ -0,0 +1,118 @@
|
||||
#ifndef VNOTEX_EDITORCONFIG_H
|
||||
#define VNOTEX_EDITORCONFIG_H
|
||||
|
||||
#include "iconfig.h"
|
||||
|
||||
#include <QScopedPointer>
|
||||
#include <QSharedPointer>
|
||||
#include <QObject>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class TextEditorConfig;
|
||||
class MarkdownEditorConfig;
|
||||
|
||||
class EditorConfig : public IConfig
|
||||
{
|
||||
Q_GADGET
|
||||
public:
|
||||
enum Shortcut
|
||||
{
|
||||
Save,
|
||||
EditRead,
|
||||
Discard,
|
||||
TypeHeading1,
|
||||
TypeHeading2,
|
||||
TypeHeading3,
|
||||
TypeHeading4,
|
||||
TypeHeading5,
|
||||
TypeHeading6,
|
||||
TypeHeadingNone,
|
||||
TypeBold,
|
||||
TypeItalic,
|
||||
TypeStrikethrough,
|
||||
TypeUnorderedList,
|
||||
TypeOrderedList,
|
||||
TypeTodoList,
|
||||
TypeCheckedTodoList,
|
||||
TypeCode,
|
||||
TypeCodeBlock,
|
||||
TypeMath,
|
||||
TypeMathBlock,
|
||||
TypeQuote,
|
||||
TypeLink,
|
||||
TypeImage,
|
||||
TypeTable,
|
||||
Outline,
|
||||
RichPaste,
|
||||
FindAndReplace,
|
||||
MaxShortcut
|
||||
};
|
||||
Q_ENUM(Shortcut)
|
||||
|
||||
enum AutoSavePolicy
|
||||
{
|
||||
None,
|
||||
AutoSave,
|
||||
BackupFile
|
||||
};
|
||||
Q_ENUM(AutoSavePolicy)
|
||||
|
||||
EditorConfig(ConfigMgr *p_mgr, IConfig *p_topConfig);
|
||||
|
||||
~EditorConfig();
|
||||
|
||||
TextEditorConfig &getTextEditorConfig();
|
||||
const TextEditorConfig &getTextEditorConfig() const;
|
||||
|
||||
MarkdownEditorConfig &getMarkdownEditorConfig();
|
||||
const MarkdownEditorConfig &getMarkdownEditorConfig() const;
|
||||
|
||||
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
|
||||
|
||||
QJsonObject toJson() const Q_DECL_OVERRIDE;
|
||||
|
||||
int getToolBarIconSize() const;
|
||||
|
||||
EditorConfig::AutoSavePolicy getAutoSavePolicy() const;
|
||||
void setAutoSavePolicy(EditorConfig::AutoSavePolicy p_policy);
|
||||
|
||||
const QString &getBackupFileDirectory() const;
|
||||
|
||||
const QString &getBackupFileExtension() const;
|
||||
|
||||
const QString &getShortcut(Shortcut p_shortcut) const;
|
||||
|
||||
private:
|
||||
void loadCore(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
|
||||
QJsonObject saveCore() const;
|
||||
|
||||
void loadShortcuts(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
|
||||
QJsonObject saveShortcuts() const;
|
||||
|
||||
QString autoSavePolicyToString(AutoSavePolicy p_policy) const;
|
||||
AutoSavePolicy stringToAutoSavePolicy(const QString &p_str) const;
|
||||
|
||||
// Icon size of editor tool bar.
|
||||
int m_toolBarIconSize = 14;
|
||||
|
||||
QString m_shortcuts[Shortcut::MaxShortcut];
|
||||
|
||||
AutoSavePolicy m_autoSavePolicy = AutoSavePolicy::AutoSave;
|
||||
|
||||
// Where to put backup file, relative to the content file itself.
|
||||
QString m_backupFileDirectory;
|
||||
|
||||
// Backup file extension.
|
||||
QString m_backupFileExtension;
|
||||
|
||||
// Will be shared with MarkdownEditorConfig.
|
||||
QSharedPointer<TextEditorConfig> m_textEditorConfig;
|
||||
|
||||
QScopedPointer<MarkdownEditorConfig> m_markdownEditorConfig;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // EDITORCONFIG_H
|
26
src/core/events.h
Normal file
26
src/core/events.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef EVENTS_H
|
||||
#define EVENTS_H
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
void reset()
|
||||
{
|
||||
m_handled = false;
|
||||
m_response.clear();
|
||||
}
|
||||
|
||||
// Whether this event is handled.
|
||||
// If it is handled, later handler should just ignore this event.
|
||||
bool m_handled = false;
|
||||
|
||||
// Handler could use this field to return state to the event sender.
|
||||
QVariant m_response = true;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // EVENTS_H
|
89
src/core/exception.h
Normal file
89
src/core/exception.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef EXCEPTION_H
|
||||
#define EXCEPTION_H
|
||||
|
||||
#include <stdexcept>
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Exception : virtual public std::runtime_error
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
InvalidPath,
|
||||
FailToCreateDir,
|
||||
FailToWriteFile,
|
||||
FailToReadFile,
|
||||
FailToRenameFile,
|
||||
FailToCopyFile,
|
||||
FailToCopyDir,
|
||||
FailToRemoveFile,
|
||||
FailToRemoveDir,
|
||||
FileMissingOnDisk,
|
||||
EssentialFileMissing,
|
||||
InvalidArgument
|
||||
};
|
||||
|
||||
Exception(Type p_type, const QString &p_what)
|
||||
: std::runtime_error(p_what.toStdString()),
|
||||
m_type(p_type)
|
||||
{
|
||||
}
|
||||
|
||||
Type m_type;
|
||||
|
||||
[[noreturn]] static void throwOne(Exception::Type p_type, const QString &p_what)
|
||||
{
|
||||
qCritical() << typeToString(p_type) << p_what;
|
||||
throw Exception(p_type, p_what);
|
||||
}
|
||||
|
||||
private:
|
||||
static QString typeToString(Exception::Type p_type)
|
||||
{
|
||||
switch (p_type) {
|
||||
case Type::InvalidPath:
|
||||
return QString("InvalidPath");
|
||||
|
||||
case Type::FailToCreateDir:
|
||||
return QString("FailToCreateDir");
|
||||
|
||||
case Type::FailToWriteFile:
|
||||
return QString("FailToWriteFile");
|
||||
|
||||
case Type::FailToReadFile:
|
||||
return QString("FailToReadFile");
|
||||
|
||||
case Type::FailToRenameFile:
|
||||
return QString("FailToRenameFile");
|
||||
|
||||
case Type::FailToCopyFile:
|
||||
return QString("FailToCopyFile");
|
||||
|
||||
case Type::FailToCopyDir:
|
||||
return QString("FailToCopyDir");
|
||||
|
||||
case Type::FailToRemoveFile:
|
||||
return QString("FailToRemoveFile");
|
||||
|
||||
case Type::FailToRemoveDir:
|
||||
return QString("FailToRemoveDir");
|
||||
|
||||
case Type::FileMissingOnDisk:
|
||||
return QString("FileMissingOnDisk");
|
||||
|
||||
case Type::EssentialFileMissing:
|
||||
return QString("EssentialFileMissing");
|
||||
|
||||
case Type::InvalidArgument:
|
||||
return QString("InvalidArgument");
|
||||
}
|
||||
|
||||
return QString::number(static_cast<int>(p_type));
|
||||
}
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // EXCEPTION_H
|
48
src/core/filelocator.h
Normal file
48
src/core/filelocator.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef FILELOCATOR_H
|
||||
#define FILELOCATOR_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Node;
|
||||
|
||||
// A unique locator for both internal Node and external file.
|
||||
class FileLocator
|
||||
{
|
||||
public:
|
||||
FileLocator(Node *p_node)
|
||||
: m_node(p_node)
|
||||
{
|
||||
}
|
||||
|
||||
FileLocator(const QString &p_filePath)
|
||||
: m_filePath(p_filePath)
|
||||
{
|
||||
}
|
||||
|
||||
bool isNode() const
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
Node *node() const
|
||||
{
|
||||
Q_ASSERT(isNode());
|
||||
return m_node;
|
||||
}
|
||||
|
||||
const QString &filePath() const
|
||||
{
|
||||
Q_ASSERT(!isNode());
|
||||
return m_filePath;
|
||||
}
|
||||
|
||||
private:
|
||||
Node *m_node = nullptr;
|
||||
|
||||
QString m_filePath;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FILELOCATOR_H
|
35
src/core/fileopenparameters.h
Normal file
35
src/core/fileopenparameters.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef FILEOPENPARAMETERS_H
|
||||
#define FILEOPENPARAMETERS_H
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Node;
|
||||
|
||||
struct FileOpenParameters
|
||||
{
|
||||
// Some modes may be not supported by some editors.
|
||||
enum Mode
|
||||
{
|
||||
Read,
|
||||
Edit,
|
||||
FullPreview,
|
||||
FocusPreview
|
||||
};
|
||||
|
||||
Mode m_mode = Mode::Read;
|
||||
|
||||
// Whether focus to the opened window.
|
||||
bool m_focus = true;
|
||||
|
||||
// Whether it is a new file.
|
||||
bool m_newFile = false;
|
||||
|
||||
// If this file is an attachment of a node, this field indicates it.
|
||||
Node *m_nodeAttachedTo = nullptr;
|
||||
|
||||
// Open as read-only.
|
||||
bool m_readOnly = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FILEOPENPARAMETERS_H
|
78
src/core/global.h
Normal file
78
src/core/global.h
Normal file
@ -0,0 +1,78 @@
|
||||
#ifndef VNOTEX_GLOBAL_H
|
||||
#define VNOTEX_GLOBAL_H
|
||||
|
||||
#include <QString>
|
||||
#include <QPair>
|
||||
#include <QDebug>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
typedef quint64 ID;
|
||||
|
||||
static QPair<bool, ID> stringToID(const QString &p_str)
|
||||
{
|
||||
bool ok;
|
||||
ID id = p_str.toULongLong(&ok);
|
||||
return qMakePair(ok, id);
|
||||
}
|
||||
|
||||
static QString IDToString(ID p_id)
|
||||
{
|
||||
return QString::number(p_id);
|
||||
}
|
||||
|
||||
typedef quint64 TimeStamp;
|
||||
|
||||
struct Info
|
||||
{
|
||||
Info(const QString &p_name, const QString &p_displayName, const QString &p_description)
|
||||
: m_name(p_name), m_displayName(p_displayName), m_description(p_description)
|
||||
{
|
||||
}
|
||||
|
||||
// Name for identification.
|
||||
QString m_name;
|
||||
|
||||
// User-visible name.
|
||||
QString m_displayName;
|
||||
|
||||
QString m_description;
|
||||
};
|
||||
|
||||
enum { CONTENTS_MARGIN = 2 };
|
||||
|
||||
static QString QJsonObjectToString(const QJsonObject &p_obj) {
|
||||
QString str = "{";
|
||||
|
||||
auto keys = p_obj.keys();
|
||||
for (auto &key : keys) {
|
||||
str += "\"" + key + "\": \"" + p_obj.value(key).toString() + "\";";
|
||||
}
|
||||
|
||||
str += "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
static QDebug operator<<(QDebug p_debug, const QJsonObject &p_obj)
|
||||
{
|
||||
QDebugStateSaver saver(p_debug);
|
||||
p_debug << QJsonObjectToString(p_obj);
|
||||
return p_debug;
|
||||
}
|
||||
|
||||
enum FindOption
|
||||
{
|
||||
None = 0,
|
||||
CaseSensitive = 0x1U,
|
||||
WholeWordOnly = 0x2U,
|
||||
RegularExpression = 0x4U,
|
||||
IncrementalSearch = 0x8U
|
||||
};
|
||||
Q_DECLARE_FLAGS(FindOptions, FindOption);
|
||||
|
||||
} // ns vnotex
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions);
|
||||
|
||||
#endif // GLOBAL_H
|
161
src/core/htmltemplatehelper.cpp
Normal file
161
src/core/htmltemplatehelper.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
#include "htmltemplatehelper.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <core/markdowneditorconfig.h>
|
||||
#include <core/configmgr.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <core/thememgr.h>
|
||||
#include <core/vnotex.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
HtmlTemplateHelper::Template HtmlTemplateHelper::s_markdownViewerTemplate;
|
||||
|
||||
QString WebGlobalOptions::toJavascriptObject() const
|
||||
{
|
||||
return QStringLiteral("window.vxOptions = {\n")
|
||||
+ QString("webPlantUml: %1,\n").arg(boolToString(m_webPlantUml))
|
||||
+ QString("webGraphviz: %1,\n").arg(boolToString(m_webGraphviz))
|
||||
+ QString("constrainImageWidthEnabled: %1,\n").arg(boolToString(m_constrainImageWidthEnabled))
|
||||
+ QString("protectFromXss: %1,\n").arg(boolToString(m_protectFromXss))
|
||||
+ QString("sectionNumberEnabled: %1\n").arg(boolToString(m_sectionNumberEnabled))
|
||||
+ QStringLiteral("}");
|
||||
}
|
||||
|
||||
static bool isGlobalStyles(const ViewerResource::Resource &p_resource)
|
||||
{
|
||||
return p_resource.m_name == QStringLiteral("global_styles");
|
||||
}
|
||||
|
||||
// Read "global_styles" from resource and fill the holder with the content.
|
||||
static void fillGlobalStyles(QString &p_template, const ViewerResource &p_resource)
|
||||
{
|
||||
QString styles;
|
||||
for (const auto &ele : p_resource.m_resources) {
|
||||
if (isGlobalStyles(ele)) {
|
||||
if (ele.m_enabled) {
|
||||
for (const auto &style : ele.m_styles) {
|
||||
// Read the style file content.
|
||||
auto styleFile = ConfigMgr::getInst().getUserOrAppFile(style);
|
||||
styles += FileUtils::readTextFile(styleFile);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!styles.isEmpty()) {
|
||||
p_template.replace(QStringLiteral("/* VX_GLOBAL_STYLES_PLACEHOLDER */"),
|
||||
styles);
|
||||
}
|
||||
}
|
||||
|
||||
static QString fillStyleTag(const QString &p_styleFile)
|
||||
{
|
||||
if (p_styleFile.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
auto url = PathUtils::pathToUrl(p_styleFile);
|
||||
return QString("<link rel=\"stylesheet\" type=\"text/css\" href=\"%1\">\n").arg(url.toString());
|
||||
}
|
||||
|
||||
static QString fillScriptTag(const QString &p_scriptFile)
|
||||
{
|
||||
if (p_scriptFile.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
auto url = PathUtils::pathToUrl(p_scriptFile);
|
||||
return QString("<script type=\"text/javascript\" src=\"%1\"></script>\n").arg(url.toString());
|
||||
}
|
||||
|
||||
static void fillThemeStyles(QString &p_template)
|
||||
{
|
||||
QString styles;
|
||||
const auto &themeMgr = VNoteX::getInst().getThemeMgr();
|
||||
|
||||
styles += fillStyleTag(themeMgr.getFile(Theme::File::WebStyleSheet));
|
||||
styles += fillStyleTag(themeMgr.getFile(Theme::File::HighlightStyleSheet));
|
||||
|
||||
if (!styles.isEmpty()) {
|
||||
p_template.replace(QStringLiteral("<!-- VX_THEME_STYLES_PLACEHOLDER -->"),
|
||||
styles);
|
||||
}
|
||||
}
|
||||
|
||||
static void fillGlobalOptions(QString &p_template, const WebGlobalOptions &p_opts)
|
||||
{
|
||||
p_template.replace(QStringLiteral("/* VX_GLOBAL_OPTIONS_PLACEHOLDER */"),
|
||||
p_opts.toJavascriptObject());
|
||||
}
|
||||
|
||||
// Read all other resources in @p_resource and fill the holder with proper resource path.
|
||||
static void fillResources(QString &p_template, const ViewerResource &p_resource)
|
||||
{
|
||||
QString styles;
|
||||
QString scripts;
|
||||
|
||||
for (const auto &ele : p_resource.m_resources) {
|
||||
if (ele.m_enabled && !isGlobalStyles(ele)) {
|
||||
// Styles.
|
||||
for (const auto &style : ele.m_styles) {
|
||||
auto styleFile = ConfigMgr::getInst().getUserOrAppFile(style);
|
||||
styles += fillStyleTag(styleFile);
|
||||
}
|
||||
|
||||
// Scripts.
|
||||
for (const auto &script : ele.m_scripts) {
|
||||
auto scriptFile = ConfigMgr::getInst().getUserOrAppFile(script);
|
||||
scripts += fillScriptTag(scriptFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!styles.isEmpty()) {
|
||||
p_template.replace(QStringLiteral("<!-- VX_STYLES_PLACEHOLDER -->"),
|
||||
styles);
|
||||
}
|
||||
|
||||
if (!scripts.isEmpty()) {
|
||||
p_template.replace(QStringLiteral("<!-- VX_SCRIPTS_PLACEHOLDER -->"),
|
||||
scripts);
|
||||
}
|
||||
}
|
||||
|
||||
const QString &HtmlTemplateHelper::getMarkdownViewerTemplate()
|
||||
{
|
||||
return s_markdownViewerTemplate.m_template;
|
||||
}
|
||||
|
||||
void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config)
|
||||
{
|
||||
if (p_config.revision() == s_markdownViewerTemplate.m_revision) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_markdownViewerTemplate.m_revision = p_config.revision();
|
||||
|
||||
const auto &viewerResource = p_config.getViewerResource();
|
||||
|
||||
{
|
||||
auto templateFile = ConfigMgr::getInst().getUserOrAppFile(viewerResource.m_template);
|
||||
s_markdownViewerTemplate.m_template = FileUtils::readTextFile(templateFile);
|
||||
}
|
||||
|
||||
fillGlobalStyles(s_markdownViewerTemplate.m_template, viewerResource);
|
||||
|
||||
fillThemeStyles(s_markdownViewerTemplate.m_template);
|
||||
|
||||
{
|
||||
WebGlobalOptions opts;
|
||||
opts.m_webPlantUml = p_config.getWebPlantUml();
|
||||
opts.m_webGraphviz = p_config.getWebGraphviz();
|
||||
opts.m_sectionNumberEnabled = p_config.getSectionNumberEnabled();
|
||||
opts.m_constrainImageWidthEnabled = p_config.getConstrainImageWidthEnabled();
|
||||
opts.m_protectFromXss = p_config.getProtectFromXss();
|
||||
fillGlobalOptions(s_markdownViewerTemplate.m_template, opts);
|
||||
}
|
||||
|
||||
fillResources(s_markdownViewerTemplate.m_template, viewerResource);
|
||||
}
|
52
src/core/htmltemplatehelper.h
Normal file
52
src/core/htmltemplatehelper.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef HTMLTEMPLATEHELPER_H
|
||||
#define HTMLTEMPLATEHELPER_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class MarkdownEditorConfig;
|
||||
|
||||
// Global options to be passed to Web side at the very beginning.
|
||||
struct WebGlobalOptions
|
||||
{
|
||||
bool m_webPlantUml = true;
|
||||
|
||||
bool m_webGraphviz = true;
|
||||
|
||||
bool m_sectionNumberEnabled = true;
|
||||
|
||||
bool m_constrainImageWidthEnabled = true;
|
||||
|
||||
bool m_protectFromXss = false;
|
||||
|
||||
QString boolToString(bool p_val) const
|
||||
{
|
||||
return p_val ? QStringLiteral("true") : QStringLiteral("false");
|
||||
}
|
||||
|
||||
QString toJavascriptObject() const;
|
||||
};
|
||||
|
||||
// Help to generate and update HTML templates.
|
||||
class HtmlTemplateHelper
|
||||
{
|
||||
public:
|
||||
HtmlTemplateHelper() = delete;
|
||||
|
||||
static const QString &getMarkdownViewerTemplate();
|
||||
static void updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config);
|
||||
|
||||
private:
|
||||
struct Template
|
||||
{
|
||||
int m_revision = -1;
|
||||
QString m_template;
|
||||
};
|
||||
|
||||
// Template for MarkdownViewer.
|
||||
static Template s_markdownViewerTemplate;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HTMLTEMPLATEHELPER_H
|
155
src/core/iconfig.h
Normal file
155
src/core/iconfig.h
Normal file
@ -0,0 +1,155 @@
|
||||
#ifndef ICONFIG_H
|
||||
#define ICONFIG_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class ConfigMgr;
|
||||
|
||||
// Interface for Config.
|
||||
class IConfig
|
||||
{
|
||||
public:
|
||||
IConfig(ConfigMgr *p_mgr, IConfig *p_topConfig = nullptr)
|
||||
: m_topConfig(p_topConfig),
|
||||
m_mgr(p_mgr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~IConfig()
|
||||
{
|
||||
}
|
||||
|
||||
// Called to init top level config.
|
||||
virtual void init()
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
// Init from QJsonObject.
|
||||
virtual void init(const QJsonObject &p_default, const QJsonObject &p_user)
|
||||
{
|
||||
Q_UNUSED(p_default);
|
||||
Q_UNUSED(p_user);
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
virtual void writeToSettings() const
|
||||
{
|
||||
Q_ASSERT(m_topConfig);
|
||||
m_topConfig->writeToSettings();
|
||||
}
|
||||
|
||||
virtual QJsonObject toJson() const = 0;
|
||||
|
||||
const QString &getSessionName() const
|
||||
{
|
||||
return m_sessionName;
|
||||
}
|
||||
|
||||
virtual int revision() const
|
||||
{
|
||||
return m_revision;
|
||||
}
|
||||
|
||||
protected:
|
||||
ConfigMgr *getMgr() const
|
||||
{
|
||||
return m_mgr;
|
||||
}
|
||||
|
||||
// First read user config, then the default config.
|
||||
static QJsonValue read(const QJsonObject &p_default,
|
||||
const QJsonObject &p_user,
|
||||
const QString &p_key)
|
||||
{
|
||||
auto it = p_user.find(p_key);
|
||||
if (it != p_user.end()) {
|
||||
return it.value();
|
||||
} else {
|
||||
return p_default.value(p_key);
|
||||
}
|
||||
}
|
||||
|
||||
static QString readString(const QJsonObject &p_default,
|
||||
const QJsonObject &p_user,
|
||||
const QString &p_key)
|
||||
{
|
||||
return read(p_default, p_user, p_key).toString();
|
||||
}
|
||||
|
||||
static QString readString(const QJsonObject &p_obj,
|
||||
const QString &p_key)
|
||||
{
|
||||
return p_obj.value(p_key).toString();
|
||||
}
|
||||
|
||||
static QByteArray readByteArray(const QJsonObject &p_obj,
|
||||
const QString &p_key)
|
||||
{
|
||||
return QByteArray::fromBase64(readString(p_obj, p_key).toLatin1());
|
||||
}
|
||||
|
||||
static void writeByteArray(QJsonObject &p_obj,
|
||||
const QString &p_key,
|
||||
const QByteArray &p_bytes)
|
||||
{
|
||||
p_obj.insert(p_key, QLatin1String(p_bytes.toBase64()));
|
||||
}
|
||||
|
||||
static bool readBool(const QJsonObject &p_default,
|
||||
const QJsonObject &p_user,
|
||||
const QString &p_key)
|
||||
{
|
||||
return read(p_default, p_user, p_key).toBool();
|
||||
}
|
||||
|
||||
static bool readBool(const QJsonObject &p_obj,
|
||||
const QString &p_key)
|
||||
{
|
||||
return p_obj.value(p_key).toBool();
|
||||
}
|
||||
|
||||
static int readInt(const QJsonObject &p_default,
|
||||
const QJsonObject &p_user,
|
||||
const QString &p_key)
|
||||
{
|
||||
return read(p_default, p_user, p_key).toInt();
|
||||
}
|
||||
|
||||
static qreal readReal(const QJsonObject &p_default,
|
||||
const QJsonObject &p_user,
|
||||
const QString &p_key)
|
||||
{
|
||||
return read(p_default, p_user, p_key).toDouble();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void updateConfig(T &p_cur,
|
||||
const T &p_new,
|
||||
IConfig *p_config)
|
||||
{
|
||||
if (p_cur == p_new) {
|
||||
return;
|
||||
}
|
||||
|
||||
++p_config->m_revision;
|
||||
p_cur = p_new;
|
||||
p_config->writeToSettings();
|
||||
}
|
||||
|
||||
IConfig *m_topConfig = nullptr;
|
||||
|
||||
QString m_sessionName;
|
||||
|
||||
// Used to indicate whether there is change after last read.
|
||||
int m_revision = 0;
|
||||
|
||||
private:
|
||||
ConfigMgr *m_mgr = nullptr;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // ICONFIG_H
|
116
src/core/logger.cpp
Normal file
116
src/core/logger.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
#include "logger.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include "configmgr.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
QFile Logger::s_file;
|
||||
|
||||
bool Logger::s_debugLog = false;
|
||||
|
||||
void Logger::init(bool p_debugLog)
|
||||
{
|
||||
s_debugLog = p_debugLog;
|
||||
|
||||
#if defined(QT_NO_DEBUG)
|
||||
s_file.setFileName(ConfigMgr::getInst().getLogFile());
|
||||
if (s_file.size() >= 5 * 1024 * 1024) {
|
||||
s_file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
} else {
|
||||
s_file.open(QIODevice::Append | QIODevice::Text);
|
||||
}
|
||||
#endif
|
||||
|
||||
qInstallMessageHandler(Logger::log);
|
||||
}
|
||||
|
||||
static QString getFileName(const char *p_file)
|
||||
{
|
||||
QString file(p_file);
|
||||
int idx = file.lastIndexOf(QChar('/'));
|
||||
if (idx == -1) {
|
||||
idx = file.lastIndexOf(QChar('\\'));
|
||||
}
|
||||
|
||||
if (idx == -1) {
|
||||
return file;
|
||||
} else {
|
||||
return file.mid(idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::log(QtMsgType p_type, const QMessageLogContext &p_context, const QString &p_msg)
|
||||
{
|
||||
#if defined(QT_NO_DEBUG)
|
||||
if (!s_debugLog && p_type == QtDebugMsg) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
QByteArray localMsg = p_msg.toUtf8();
|
||||
QString header;
|
||||
|
||||
switch (p_type) {
|
||||
case QtDebugMsg:
|
||||
header = QStringLiteral("Debug:");
|
||||
break;
|
||||
|
||||
case QtInfoMsg:
|
||||
header = QStringLiteral("Info:");
|
||||
break;
|
||||
|
||||
case QtWarningMsg:
|
||||
header = QStringLiteral("Warning:");
|
||||
break;
|
||||
|
||||
case QtCriticalMsg:
|
||||
header = QStringLiteral("Critical:");
|
||||
break;
|
||||
|
||||
case QtFatalMsg:
|
||||
header = QStringLiteral("Fatal:");
|
||||
}
|
||||
|
||||
QString fileName = getFileName(p_context.file);
|
||||
|
||||
#if defined(QT_NO_DEBUG)
|
||||
QTextStream stream(&s_file);
|
||||
stream << header << (QString("(%1:%2) ").arg(fileName).arg(p_context.line))
|
||||
<< localMsg << "\n";
|
||||
|
||||
if (p_type == QtFatalMsg) {
|
||||
s_file.close();
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
std::string fileStr = fileName.toStdString();
|
||||
const char *file = fileStr.c_str();
|
||||
|
||||
switch (p_type) {
|
||||
case QtDebugMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
fprintf(stderr, "%s(%s:%u) %s\n",
|
||||
header.toStdString().c_str(), file, p_context.line, localMsg.constData());
|
||||
abort();
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
27
src/core/logger.h
Normal file
27
src/core/logger.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <QString>
|
||||
#include <QMessageLogContext>
|
||||
|
||||
class QFile;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
Logger() = delete;
|
||||
|
||||
static void init(bool p_debugLog);
|
||||
|
||||
private:
|
||||
static void log(QtMsgType p_type, const QMessageLogContext &p_context, const QString &p_msg);
|
||||
|
||||
static QFile s_file;
|
||||
|
||||
static bool s_debugLog;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LOGGER_H
|
113
src/core/mainconfig.cpp
Normal file
113
src/core/mainconfig.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
#include "mainconfig.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include "configmgr.h"
|
||||
#include "coreconfig.h"
|
||||
#include "editorconfig.h"
|
||||
#include "widgetconfig.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
bool MainConfig::s_versionChanged = false;
|
||||
|
||||
MainConfig::MainConfig(ConfigMgr *p_mgr)
|
||||
: IConfig(p_mgr, nullptr),
|
||||
m_coreConfig(new CoreConfig(p_mgr, this)),
|
||||
m_editorConfig(new EditorConfig(p_mgr, this)),
|
||||
m_widgetConfig(new WidgetConfig(p_mgr, this))
|
||||
{
|
||||
}
|
||||
|
||||
MainConfig::~MainConfig()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MainConfig::init()
|
||||
{
|
||||
auto mgr = getMgr();
|
||||
auto appSettings = mgr->getSettings(ConfigMgr::Source::App);
|
||||
auto userSettings = mgr->getSettings(ConfigMgr::Source::User);
|
||||
const auto &appJobj = appSettings->getJson();
|
||||
const auto &userJobj = userSettings->getJson();
|
||||
|
||||
loadMetadata(appJobj, userJobj);
|
||||
|
||||
m_coreConfig->init(appJobj, userJobj);
|
||||
|
||||
m_editorConfig->init(appJobj, userJobj);
|
||||
|
||||
m_widgetConfig->init(appJobj, userJobj);
|
||||
|
||||
if (isVersionChanged()) {
|
||||
// Update user config.
|
||||
writeToSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void MainConfig::loadMetadata(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(QStringLiteral("metadata")).toObject();
|
||||
const auto userObj = p_user.value(QStringLiteral("metadata")).toObject();
|
||||
|
||||
m_version = appObj.value(QStringLiteral("version")).toString();
|
||||
m_userVersion = userObj.value(QStringLiteral("version")).toString();
|
||||
s_versionChanged = m_version != m_userVersion;
|
||||
qDebug() << "version" << m_version << "user version" << m_userVersion;
|
||||
}
|
||||
|
||||
QJsonObject MainConfig::saveMetaData() const
|
||||
{
|
||||
QJsonObject metaObj;
|
||||
metaObj[QStringLiteral("version")] = m_version;
|
||||
|
||||
return metaObj;
|
||||
}
|
||||
|
||||
bool MainConfig::isVersionChanged()
|
||||
{
|
||||
return s_versionChanged;
|
||||
}
|
||||
|
||||
CoreConfig &MainConfig::getCoreConfig()
|
||||
{
|
||||
return *m_coreConfig;
|
||||
}
|
||||
|
||||
EditorConfig &MainConfig::getEditorConfig()
|
||||
{
|
||||
return *m_editorConfig;
|
||||
}
|
||||
|
||||
WidgetConfig &MainConfig::getWidgetConfig()
|
||||
{
|
||||
return *m_widgetConfig;
|
||||
}
|
||||
|
||||
void MainConfig::writeToSettings() const
|
||||
{
|
||||
getMgr()->writeUserSettings(toJson());
|
||||
}
|
||||
|
||||
QJsonObject MainConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("metadata")] = saveMetaData();
|
||||
obj[m_coreConfig->getSessionName()] = m_coreConfig->toJson();
|
||||
obj[m_editorConfig->getSessionName()] = m_editorConfig->toJson();
|
||||
obj[m_widgetConfig->getSessionName()] = m_widgetConfig->toJson();
|
||||
return obj;
|
||||
}
|
||||
|
||||
const QString &MainConfig::getVersion() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
QString MainConfig::getVersion(const QJsonObject &p_jobj)
|
||||
{
|
||||
const auto metadataObj = p_jobj.value(QStringLiteral("metadata")).toObject();
|
||||
return metadataObj.value(QStringLiteral("version")).toString();
|
||||
}
|
63
src/core/mainconfig.h
Normal file
63
src/core/mainconfig.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef MAINCONFIG_H
|
||||
#define MAINCONFIG_H
|
||||
|
||||
#include "iconfig.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QString>
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class CoreConfig;
|
||||
class EditorConfig;
|
||||
class WidgetConfig;
|
||||
|
||||
class MainConfig : public IConfig
|
||||
{
|
||||
public:
|
||||
explicit MainConfig(ConfigMgr *p_mgr);
|
||||
|
||||
~MainConfig();
|
||||
|
||||
void init() Q_DECL_OVERRIDE;
|
||||
|
||||
const QString &getVersion() const;
|
||||
|
||||
CoreConfig &getCoreConfig();
|
||||
|
||||
EditorConfig &getEditorConfig();
|
||||
|
||||
WidgetConfig &getWidgetConfig();
|
||||
|
||||
void writeToSettings() const Q_DECL_OVERRIDE;
|
||||
|
||||
QJsonObject toJson() const Q_DECL_OVERRIDE;
|
||||
|
||||
static QString getVersion(const QJsonObject &p_jobj);
|
||||
|
||||
static bool isVersionChanged();
|
||||
|
||||
private:
|
||||
void loadMetadata(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
|
||||
QJsonObject saveMetaData() const;
|
||||
|
||||
// Version of VNoteX.
|
||||
QString m_version;
|
||||
|
||||
// Version of user's configuration.
|
||||
QString m_userVersion;
|
||||
|
||||
QScopedPointer<CoreConfig> m_coreConfig;
|
||||
|
||||
QScopedPointer<EditorConfig> m_editorConfig;
|
||||
|
||||
QScopedPointer<WidgetConfig> m_widgetConfig;
|
||||
|
||||
static bool s_versionChanged;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // MAINCONFIG_H
|
197
src/core/markdowneditorconfig.cpp
Normal file
197
src/core/markdowneditorconfig.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
#include "markdowneditorconfig.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "texteditorconfig.h"
|
||||
#include "mainconfig.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
#define READSTR(key) readString(appObj, userObj, (key))
|
||||
#define READBOOL(key) readBool(appObj, userObj, (key))
|
||||
#define READREAL(key) readReal(appObj, userObj, (key))
|
||||
|
||||
MarkdownEditorConfig::MarkdownEditorConfig(ConfigMgr *p_mgr,
|
||||
IConfig *p_topConfig,
|
||||
const QSharedPointer<TextEditorConfig> &p_textEditorConfig)
|
||||
: IConfig(p_mgr, p_topConfig),
|
||||
m_textEditorConfig(p_textEditorConfig)
|
||||
{
|
||||
m_sessionName = QStringLiteral("markdown_editor");
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
const auto appObj = p_app.value(m_sessionName).toObject();
|
||||
const auto userObj = p_user.value(m_sessionName).toObject();
|
||||
|
||||
loadViewerResource(appObj, userObj);
|
||||
|
||||
m_webPlantUml = READBOOL(QStringLiteral("web_plantuml"));
|
||||
m_webGraphviz = READBOOL(QStringLiteral("web_graphviz"));
|
||||
|
||||
m_prependDotInRelativeLink = READBOOL(QStringLiteral("prepend_dot_in_relative_link"));
|
||||
m_confirmBeforeClearObsoleteImages = READBOOL(QStringLiteral("confirm_before_clear_obsolete_images"));
|
||||
m_insertFileNameAsTitle = READBOOL(QStringLiteral("insert_file_name_as_title"));
|
||||
m_sectionNumberEnabled = READBOOL(QStringLiteral("section_number"));
|
||||
m_constrainImageWidthEnabled = READBOOL(QStringLiteral("constrain_image_width"));
|
||||
m_constrainInPlacePreviewWidthEnabled = READBOOL(QStringLiteral("constrain_inplace_preview_width"));
|
||||
m_zoomFactorInReadMode = READREAL(QStringLiteral("zoom_factor_in_read_mode"));
|
||||
m_fetchImagesInParseAndPaste = READBOOL(QStringLiteral("fetch_images_in_parse_and_paste"));
|
||||
m_protectFromXss = READBOOL(QStringLiteral("protect_from_xss"));
|
||||
}
|
||||
|
||||
QJsonObject MarkdownEditorConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("viewer_resource")] = saveViewerResource();
|
||||
obj[QStringLiteral("web_plantuml")] = m_webPlantUml;
|
||||
obj[QStringLiteral("web_graphviz")] = m_webGraphviz;
|
||||
obj[QStringLiteral("prepend_dot_in_relative_link")] = m_prependDotInRelativeLink;
|
||||
obj[QStringLiteral("confirm_before_clear_obsolete_images")] = m_confirmBeforeClearObsoleteImages;
|
||||
obj[QStringLiteral("insert_file_name_as_title")] = m_insertFileNameAsTitle;
|
||||
obj[QStringLiteral("section_number")] = m_sectionNumberEnabled;
|
||||
obj[QStringLiteral("constrain_image_width")] = m_constrainImageWidthEnabled;
|
||||
obj[QStringLiteral("constrain_inplace_preview_width")] = m_constrainInPlacePreviewWidthEnabled;
|
||||
obj[QStringLiteral("zoom_factor_in_read_mode")] = m_zoomFactorInReadMode;
|
||||
obj[QStringLiteral("fetch_images_in_parse_and_paste")] = m_fetchImagesInParseAndPaste;
|
||||
obj[QStringLiteral("protect_from_xss")] = m_protectFromXss;
|
||||
return obj;
|
||||
}
|
||||
|
||||
TextEditorConfig &MarkdownEditorConfig::getTextEditorConfig()
|
||||
{
|
||||
return *m_textEditorConfig;
|
||||
}
|
||||
|
||||
const TextEditorConfig &MarkdownEditorConfig::getTextEditorConfig() const
|
||||
{
|
||||
return *m_textEditorConfig;
|
||||
}
|
||||
|
||||
int MarkdownEditorConfig::revision() const
|
||||
{
|
||||
return m_revision + m_textEditorConfig->revision();
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::loadViewerResource(const QJsonObject &p_app, const QJsonObject &p_user)
|
||||
{
|
||||
const QString name(QStringLiteral("viewer_resource"));
|
||||
|
||||
if (MainConfig::isVersionChanged()) {
|
||||
bool needOverride = p_app[QStringLiteral("override_viewer_resource")].toBool();
|
||||
if (needOverride) {
|
||||
qInfo() << "override \"viewer_resource\" in user configuration due to version change";
|
||||
m_viewerResource.init(p_app[name].toObject());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_user.contains(name)) {
|
||||
m_viewerResource.init(p_user[name].toObject());
|
||||
} else {
|
||||
m_viewerResource.init(p_app[name].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject MarkdownEditorConfig::saveViewerResource() const
|
||||
{
|
||||
return m_viewerResource.toJson();
|
||||
}
|
||||
|
||||
const ViewerResource &MarkdownEditorConfig::getViewerResource() const
|
||||
{
|
||||
return m_viewerResource;
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getWebPlantUml() const
|
||||
{
|
||||
return m_webPlantUml;
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getWebGraphviz() const
|
||||
{
|
||||
return m_webGraphviz;
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getPrependDotInRelativeLink() const
|
||||
{
|
||||
return m_prependDotInRelativeLink;
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getConfirmBeforeClearObsoleteImages() const
|
||||
{
|
||||
return m_confirmBeforeClearObsoleteImages;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setConfirmBeforeClearObsoleteImages(bool p_confirm)
|
||||
{
|
||||
updateConfig(m_confirmBeforeClearObsoleteImages,
|
||||
p_confirm,
|
||||
this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getInsertFileNameAsTitle() const
|
||||
{
|
||||
return m_insertFileNameAsTitle;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setInsertFileNameAsTitle(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_insertFileNameAsTitle, p_enabled, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getSectionNumberEnabled() const
|
||||
{
|
||||
return m_sectionNumberEnabled;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setSectionNumberEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_sectionNumberEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getConstrainImageWidthEnabled() const
|
||||
{
|
||||
return m_constrainImageWidthEnabled;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setConstrainImageWidthEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_constrainImageWidthEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getConstrainInPlacePreviewWidthEnabled() const
|
||||
{
|
||||
return m_constrainInPlacePreviewWidthEnabled;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setConstrainInPlacePreviewWidthEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_constrainInPlacePreviewWidthEnabled, p_enabled, this);
|
||||
}
|
||||
|
||||
qreal MarkdownEditorConfig::getZoomFactorInReadMode() const
|
||||
{
|
||||
return m_zoomFactorInReadMode;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setZoomFactorInReadMode(qreal p_factor)
|
||||
{
|
||||
updateConfig(m_zoomFactorInReadMode, p_factor, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getFetchImagesInParseAndPaste() const
|
||||
{
|
||||
return m_fetchImagesInParseAndPaste;
|
||||
}
|
||||
|
||||
void MarkdownEditorConfig::setFetchImagesInParseAndPaste(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_fetchImagesInParseAndPaste, p_enabled, this);
|
||||
}
|
||||
|
||||
bool MarkdownEditorConfig::getProtectFromXss() const
|
||||
{
|
||||
return m_protectFromXss;
|
||||
}
|
103
src/core/markdowneditorconfig.h
Normal file
103
src/core/markdowneditorconfig.h
Normal file
@ -0,0 +1,103 @@
|
||||
#ifndef MARKDOWNEDITORCONFIG_H
|
||||
#define MARKDOWNEDITORCONFIG_H
|
||||
|
||||
#include "iconfig.h"
|
||||
|
||||
#include "viewerresource.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QVector>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class TextEditorConfig;
|
||||
|
||||
class MarkdownEditorConfig : public IConfig
|
||||
{
|
||||
public:
|
||||
MarkdownEditorConfig(ConfigMgr *p_mgr,
|
||||
IConfig *p_topConfig,
|
||||
const QSharedPointer<TextEditorConfig> &p_textEditorConfig);
|
||||
|
||||
void init(const QJsonObject &p_app, const QJsonObject &p_user) Q_DECL_OVERRIDE;
|
||||
|
||||
QJsonObject toJson() const Q_DECL_OVERRIDE;
|
||||
|
||||
void loadViewerResource(const QJsonObject &p_app, const QJsonObject &p_user);
|
||||
QJsonObject saveViewerResource() const;
|
||||
|
||||
int revision() const Q_DECL_OVERRIDE;
|
||||
|
||||
TextEditorConfig &getTextEditorConfig();
|
||||
const TextEditorConfig &getTextEditorConfig() const;
|
||||
|
||||
const ViewerResource &getViewerResource() const;
|
||||
|
||||
bool getWebPlantUml() const;
|
||||
|
||||
bool getWebGraphviz() const;
|
||||
|
||||
bool getPrependDotInRelativeLink() const;
|
||||
|
||||
bool getConfirmBeforeClearObsoleteImages() const;
|
||||
void setConfirmBeforeClearObsoleteImages(bool p_confirm);
|
||||
|
||||
bool getInsertFileNameAsTitle() const;
|
||||
void setInsertFileNameAsTitle(bool p_enabled);
|
||||
|
||||
bool getSectionNumberEnabled() const;
|
||||
void setSectionNumberEnabled(bool p_enabled);
|
||||
|
||||
bool getConstrainImageWidthEnabled() const;
|
||||
void setConstrainImageWidthEnabled(bool p_enabled);
|
||||
|
||||
bool getConstrainInPlacePreviewWidthEnabled() const;
|
||||
void setConstrainInPlacePreviewWidthEnabled(bool p_enabled);
|
||||
|
||||
qreal getZoomFactorInReadMode() const;
|
||||
void setZoomFactorInReadMode(qreal p_factor);
|
||||
|
||||
bool getFetchImagesInParseAndPaste() const;
|
||||
void setFetchImagesInParseAndPaste(bool p_enabled);
|
||||
|
||||
bool getProtectFromXss() const;
|
||||
|
||||
private:
|
||||
QSharedPointer<TextEditorConfig> m_textEditorConfig;
|
||||
|
||||
ViewerResource m_viewerResource;
|
||||
|
||||
// Whether use javascript or external program to render PlantUML.
|
||||
bool m_webPlantUml = true;
|
||||
|
||||
bool m_webGraphviz = true;
|
||||
|
||||
// Whether prepend a dot in front of the relative link, like images.
|
||||
bool m_prependDotInRelativeLink = false;
|
||||
|
||||
// Whether ask for user confirmation before clearing obsolete images.
|
||||
bool m_confirmBeforeClearObsoleteImages = true;
|
||||
|
||||
// Whether insert the name of the new file as title.
|
||||
bool m_insertFileNameAsTitle = true;
|
||||
|
||||
// Whether enable section numbering.
|
||||
bool m_sectionNumberEnabled = true;
|
||||
|
||||
// Whether enable image width constraint.
|
||||
bool m_constrainImageWidthEnabled = true;
|
||||
|
||||
// Whether enable in-place preview width constraint.
|
||||
bool m_constrainInPlacePreviewWidthEnabled = false;
|
||||
|
||||
qreal m_zoomFactorInReadMode = 1.0;
|
||||
|
||||
// Whether fetch images to local in Parse To Markdown And Paste.
|
||||
bool m_fetchImagesInParseAndPaste = true;
|
||||
|
||||
// Whether protect from Cross-Site Scripting.
|
||||
bool m_protectFromXss = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MARKDOWNEDITORCONFIG_H
|
50
src/core/namebasedserver.h
Normal file
50
src/core/namebasedserver.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef NAMEBASEDSERVER_H
|
||||
#define NAMEBASEDSERVER_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QSharedPointer>
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
template <typename T>
|
||||
class NameBasedServer
|
||||
{
|
||||
public:
|
||||
// Register an item.
|
||||
bool registerItem(const QString &p_name, const QSharedPointer<T> &p_item)
|
||||
{
|
||||
if (m_data.contains(p_name)) {
|
||||
qWarning() << "item to register already exists with name" << p_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_data.insert(p_name, p_item);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get an item.
|
||||
QSharedPointer<T> getItem(const QString &p_name)
|
||||
{
|
||||
auto it = m_data.find(p_name);
|
||||
if (it != m_data.end()) {
|
||||
return it.value();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QList<QSharedPointer<T>> getAllItems() const
|
||||
{
|
||||
return m_data.values();
|
||||
}
|
||||
|
||||
private:
|
||||
// Name to item mapping.
|
||||
QHash<QString, QSharedPointer<T>> m_data;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
|
||||
#endif // NAMEBASEDSERVER_H
|
60
src/core/notebook/bundlenotebook.cpp
Normal file
60
src/core/notebook/bundlenotebook.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "bundlenotebook.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <notebookconfigmgr/bundlenotebookconfigmgr.h>
|
||||
#include <notebookconfigmgr/notebookconfig.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
BundleNotebook::BundleNotebook(const NotebookParameters &p_paras,
|
||||
QObject *p_parent)
|
||||
: Notebook(p_paras, p_parent)
|
||||
{
|
||||
auto configMgr = getBundleNotebookConfigMgr();
|
||||
auto config = configMgr->readNotebookConfig();
|
||||
m_nextNodeId = config->m_nextNodeId;
|
||||
}
|
||||
|
||||
BundleNotebookConfigMgr *BundleNotebook::getBundleNotebookConfigMgr() const
|
||||
{
|
||||
return dynamic_cast<BundleNotebookConfigMgr *>(getConfigMgr().data());
|
||||
}
|
||||
|
||||
ID BundleNotebook::getNextNodeId() const
|
||||
{
|
||||
return m_nextNodeId;
|
||||
}
|
||||
|
||||
ID BundleNotebook::getAndUpdateNextNodeId()
|
||||
{
|
||||
auto id = m_nextNodeId++;
|
||||
getBundleNotebookConfigMgr()->writeNotebookConfig();
|
||||
return id;
|
||||
}
|
||||
|
||||
void BundleNotebook::updateNotebookConfig()
|
||||
{
|
||||
getBundleNotebookConfigMgr()->writeNotebookConfig();
|
||||
}
|
||||
|
||||
void BundleNotebook::removeNotebookConfig()
|
||||
{
|
||||
getBundleNotebookConfigMgr()->removeNotebookConfig();
|
||||
}
|
||||
|
||||
void BundleNotebook::remove()
|
||||
{
|
||||
// Remove all nodes.
|
||||
removeNode(getRootNode());
|
||||
|
||||
// Remove notebook config.
|
||||
removeNotebookConfig();
|
||||
|
||||
// Remove notebook root folder if it is empty.
|
||||
if (!FileUtils::removeDirIfEmpty(getRootFolderAbsolutePath())) {
|
||||
qInfo() << QString("root folder of notebook (%1) is not empty and needs manual clean up")
|
||||
.arg(getRootFolderAbsolutePath());
|
||||
}
|
||||
}
|
35
src/core/notebook/bundlenotebook.h
Normal file
35
src/core/notebook/bundlenotebook.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef BUNDLENOTEBOOK_H
|
||||
#define BUNDLENOTEBOOK_H
|
||||
|
||||
#include "notebook.h"
|
||||
#include "global.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class BundleNotebookConfigMgr;
|
||||
|
||||
class BundleNotebook : public Notebook
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BundleNotebook(const NotebookParameters &p_paras,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
ID getNextNodeId() const Q_DECL_OVERRIDE;
|
||||
|
||||
ID getAndUpdateNextNodeId() Q_DECL_OVERRIDE;
|
||||
|
||||
void updateNotebookConfig() Q_DECL_OVERRIDE;
|
||||
|
||||
void removeNotebookConfig() Q_DECL_OVERRIDE;
|
||||
|
||||
void remove() Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
BundleNotebookConfigMgr *getBundleNotebookConfigMgr() const;
|
||||
|
||||
ID m_nextNodeId = 1;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // BUNDLENOTEBOOK_H
|
104
src/core/notebook/bundlenotebookfactory.cpp
Normal file
104
src/core/notebook/bundlenotebookfactory.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
#include "bundlenotebookfactory.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include <utils/pathutils.h>
|
||||
#include "../exception.h"
|
||||
#include "notebookconfigmgr/bundlenotebookconfigmgr.h"
|
||||
#include "notebookparameters.h"
|
||||
#include "bundlenotebook.h"
|
||||
#include "notebookmgr.h"
|
||||
#include "notebookconfigmgr/notebookconfig.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
BundleNotebookFactory::BundleNotebookFactory()
|
||||
{
|
||||
}
|
||||
|
||||
QString BundleNotebookFactory::getName() const
|
||||
{
|
||||
return QStringLiteral("bundle.vnotex");
|
||||
}
|
||||
|
||||
QString BundleNotebookFactory::getDisplayName() const
|
||||
{
|
||||
return QObject::tr("Bundled Notebook");
|
||||
}
|
||||
|
||||
QString BundleNotebookFactory::getDescription() const
|
||||
{
|
||||
return QObject::tr("A notebook with configuration files to track its content");
|
||||
}
|
||||
|
||||
// Check if root folder is valid for a new notebook.
|
||||
static void checkRootFolderForNewNotebook(const NotebookParameters &p_paras)
|
||||
{
|
||||
if (p_paras.m_rootFolderPath.isEmpty()) {
|
||||
QString msg("no local root folder is specified");
|
||||
qCritical() << msg;
|
||||
throw Exception(Exception::Type::InvalidPath, msg);
|
||||
} else if (p_paras.m_ensureEmptyRootFolder && !PathUtils::isEmptyDir(p_paras.m_rootFolderPath)) {
|
||||
QString msg = QString("local root folder must be empty: %1 (%2)")
|
||||
.arg(p_paras.m_rootFolderPath, PathUtils::absolutePath(p_paras.m_rootFolderPath));
|
||||
qCritical() << msg;
|
||||
throw Exception(Exception::Type::InvalidPath, msg);
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<Notebook> BundleNotebookFactory::newNotebook(const NotebookParameters &p_paras)
|
||||
{
|
||||
checkParameters(p_paras);
|
||||
|
||||
checkRootFolderForNewNotebook(p_paras);
|
||||
|
||||
p_paras.m_notebookConfigMgr->createEmptySkeleton(p_paras);
|
||||
|
||||
auto notebook = QSharedPointer<BundleNotebook>::create(p_paras);
|
||||
return notebook;
|
||||
}
|
||||
|
||||
QSharedPointer<Notebook> BundleNotebookFactory::createNotebook(const NotebookMgr &p_mgr,
|
||||
const QString &p_rootFolderPath,
|
||||
const QSharedPointer<INotebookBackend> &p_backend)
|
||||
{
|
||||
// Read basic info about this notebook.
|
||||
auto nbConfig = BundleNotebookConfigMgr::readNotebookConfig(p_backend);
|
||||
auto paras = NotebookParameters::createNotebookParameters(p_mgr,
|
||||
p_backend,
|
||||
getName(),
|
||||
nbConfig->m_name,
|
||||
nbConfig->m_description,
|
||||
p_rootFolderPath,
|
||||
QIcon(),
|
||||
nbConfig->m_imageFolder,
|
||||
nbConfig->m_attachmentFolder,
|
||||
nbConfig->m_createdTimeUtc,
|
||||
nbConfig->m_versionController,
|
||||
nbConfig->m_notebookConfigMgr);
|
||||
checkParameters(*paras);
|
||||
auto notebook = QSharedPointer<BundleNotebook>::create(*paras);
|
||||
return notebook;
|
||||
}
|
||||
|
||||
void BundleNotebookFactory::checkParameters(const NotebookParameters &p_paras) const
|
||||
{
|
||||
auto configMgr = dynamic_cast<BundleNotebookConfigMgr *>(p_paras.m_notebookConfigMgr.data());
|
||||
if (!configMgr) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("Invalid notebook configuration manager"));
|
||||
}
|
||||
}
|
||||
|
||||
bool BundleNotebookFactory::checkRootFolder(const QSharedPointer<INotebookBackend> &p_backend)
|
||||
{
|
||||
try {
|
||||
BundleNotebookConfigMgr::readNotebookConfig(p_backend);
|
||||
} catch (Exception &p_e) {
|
||||
Q_UNUSED(p_e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
38
src/core/notebook/bundlenotebookfactory.h
Normal file
38
src/core/notebook/bundlenotebookfactory.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef BUNDLENOTEBOOKFACTORY_H
|
||||
#define BUNDLENOTEBOOKFACTORY_H
|
||||
|
||||
#include "inotebookfactory.h"
|
||||
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class BundleNotebookFactory : public INotebookFactory
|
||||
{
|
||||
public:
|
||||
BundleNotebookFactory();
|
||||
|
||||
// Get the name of this factory.
|
||||
QString getName() const Q_DECL_OVERRIDE;
|
||||
|
||||
// Get the display name of this factory.
|
||||
QString getDisplayName() const Q_DECL_OVERRIDE;
|
||||
|
||||
// Get the description of this factory.
|
||||
QString getDescription() const Q_DECL_OVERRIDE;
|
||||
|
||||
// New a notebook with given information and return an instance of that notebook.
|
||||
QSharedPointer<Notebook> newNotebook(const NotebookParameters &p_paras) Q_DECL_OVERRIDE;
|
||||
|
||||
// Create a Notebook instance from existing root folder.
|
||||
QSharedPointer<Notebook> createNotebook(const NotebookMgr &p_mgr,
|
||||
const QString &p_rootFolderPath,
|
||||
const QSharedPointer<INotebookBackend> &p_backend) Q_DECL_OVERRIDE;
|
||||
|
||||
bool checkRootFolder(const QSharedPointer<INotebookBackend> &p_backend) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void checkParameters(const NotebookParameters &p_paras) const;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // BUNDLENOTEBOOKFACTORY_H
|
130
src/core/notebook/filenode.cpp
Normal file
130
src/core/notebook/filenode.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
#include "filenode.h"
|
||||
|
||||
#include <notebookconfigmgr/inotebookconfigmgr.h>
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include "notebook.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
FileNode::FileNode(ID p_id,
|
||||
const QString &p_name,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QDateTime &p_modifiedTimeUtc,
|
||||
const QString &p_attachmentFolder,
|
||||
const QStringList &p_tags,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent)
|
||||
: Node(Node::Type::File,
|
||||
p_id,
|
||||
p_name,
|
||||
p_createdTimeUtc,
|
||||
p_notebook,
|
||||
p_parent),
|
||||
m_modifiedTimeUtc(p_modifiedTimeUtc),
|
||||
m_attachmentFolder(p_attachmentFolder),
|
||||
m_tags(p_tags)
|
||||
{
|
||||
}
|
||||
|
||||
QVector<QSharedPointer<Node>> FileNode::getChildren() const
|
||||
{
|
||||
return QVector<QSharedPointer<Node>>();
|
||||
}
|
||||
|
||||
int FileNode::getChildrenCount() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FileNode::addChild(const QSharedPointer<Node> &p_node)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
Q_UNUSED(p_node);
|
||||
}
|
||||
|
||||
void FileNode::insertChild(int p_idx, const QSharedPointer<Node> &p_node)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
Q_UNUSED(p_idx);
|
||||
Q_UNUSED(p_node);
|
||||
}
|
||||
|
||||
void FileNode::removeChild(const QSharedPointer<Node> &p_child)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
Q_UNUSED(p_child);
|
||||
}
|
||||
|
||||
QDateTime FileNode::getModifiedTimeUtc() const
|
||||
{
|
||||
return m_modifiedTimeUtc;
|
||||
}
|
||||
|
||||
void FileNode::setModifiedTimeUtc()
|
||||
{
|
||||
m_modifiedTimeUtc = QDateTime::currentDateTimeUtc();
|
||||
}
|
||||
|
||||
QString FileNode::getAttachmentFolder() const
|
||||
{
|
||||
return m_attachmentFolder;
|
||||
}
|
||||
|
||||
void FileNode::setAttachmentFolder(const QString &p_folder)
|
||||
{
|
||||
m_attachmentFolder = p_folder;
|
||||
}
|
||||
|
||||
QStringList FileNode::addAttachment(const QString &p_destFolderPath, const QStringList &p_files)
|
||||
{
|
||||
QStringList addedFiles;
|
||||
for (const auto &file : p_files) {
|
||||
auto destFilePath = m_backend->renameIfExistsCaseInsensitive(
|
||||
PathUtils::concatenateFilePath(p_destFolderPath, PathUtils::fileName(file)));
|
||||
m_backend->copyFile(file, destFilePath);
|
||||
addedFiles << destFilePath;
|
||||
}
|
||||
|
||||
return addedFiles;
|
||||
}
|
||||
|
||||
QString FileNode::newAttachmentFile(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
auto destFilePath = m_backend->renameIfExistsCaseInsensitive(
|
||||
PathUtils::concatenateFilePath(p_destFolderPath, p_name));
|
||||
m_backend->writeFile(destFilePath, QByteArray());
|
||||
return destFilePath;
|
||||
}
|
||||
|
||||
QString FileNode::newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
auto destFilePath = m_backend->renameIfExistsCaseInsensitive(
|
||||
PathUtils::concatenateFilePath(p_destFolderPath, p_name));
|
||||
m_backend->makePath(destFilePath);
|
||||
return destFilePath;
|
||||
}
|
||||
|
||||
QString FileNode::renameAttachment(const QString &p_path, const QString &p_name)
|
||||
{
|
||||
m_backend->renameFile(p_path, p_name);
|
||||
return p_name;
|
||||
}
|
||||
|
||||
void FileNode::removeAttachment(const QStringList &p_paths)
|
||||
{
|
||||
// Just move it to recycle bin but not added as a child node of recycle bin.
|
||||
for (const auto &pa : p_paths) {
|
||||
if (QFileInfo(pa).isDir()) {
|
||||
m_notebook->moveDirToRecycleBin(pa);
|
||||
} else {
|
||||
m_notebook->moveFileToRecycleBin(pa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList FileNode::getTags() const
|
||||
{
|
||||
return m_tags;
|
||||
}
|
60
src/core/notebook/filenode.h
Normal file
60
src/core/notebook/filenode.h
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef FILENODE_H
|
||||
#define FILENODE_H
|
||||
|
||||
#include "node.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// File node of notebook.
|
||||
class FileNode : public Node
|
||||
{
|
||||
public:
|
||||
FileNode(ID p_id,
|
||||
const QString &p_name,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QDateTime &p_modifiedTimeUtc,
|
||||
const QString &p_attachmentFolder,
|
||||
const QStringList &p_tags,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent = nullptr);
|
||||
|
||||
QVector<QSharedPointer<Node>> getChildren() const Q_DECL_OVERRIDE;
|
||||
|
||||
int getChildrenCount() const Q_DECL_OVERRIDE;
|
||||
|
||||
void addChild(const QSharedPointer<Node> &p_node) Q_DECL_OVERRIDE;
|
||||
|
||||
void insertChild(int p_idx, const QSharedPointer<Node> &p_node) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeChild(const QSharedPointer<Node> &p_child) Q_DECL_OVERRIDE;
|
||||
|
||||
QDateTime getModifiedTimeUtc() const Q_DECL_OVERRIDE;
|
||||
|
||||
void setModifiedTimeUtc() Q_DECL_OVERRIDE;
|
||||
|
||||
QString getAttachmentFolder() const Q_DECL_OVERRIDE;
|
||||
|
||||
void setAttachmentFolder(const QString &p_folder) Q_DECL_OVERRIDE;
|
||||
|
||||
QStringList addAttachment(const QString &p_destFolderPath, const QStringList &p_files) Q_DECL_OVERRIDE;
|
||||
|
||||
QString newAttachmentFile(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
QString newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
QString renameAttachment(const QString &p_path, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeAttachment(const QStringList &p_paths) Q_DECL_OVERRIDE;
|
||||
|
||||
QStringList getTags() const Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
QDateTime m_modifiedTimeUtc;
|
||||
|
||||
QString m_attachmentFolder;
|
||||
|
||||
QStringList m_tags;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // FILENODE_H
|
65
src/core/notebook/foldernode.cpp
Normal file
65
src/core/notebook/foldernode.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include "foldernode.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
FolderNode::FolderNode(const QString &p_name,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent)
|
||||
: Node(Node::Type::Folder,
|
||||
p_name,
|
||||
p_notebook,
|
||||
p_parent)
|
||||
{
|
||||
}
|
||||
|
||||
void FolderNode::loadFolder(ID p_id,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QVector<QSharedPointer<Node>> &p_children)
|
||||
{
|
||||
Node::loadInfo(p_id, p_createdTimeUtc);
|
||||
m_children = p_children;
|
||||
}
|
||||
|
||||
QVector<QSharedPointer<Node>> FolderNode::getChildren() const
|
||||
{
|
||||
return m_children;
|
||||
}
|
||||
|
||||
int FolderNode::getChildrenCount() const
|
||||
{
|
||||
return m_children.size();
|
||||
}
|
||||
|
||||
void FolderNode::addChild(const QSharedPointer<Node> &p_node)
|
||||
{
|
||||
insertChild(m_children.size(), p_node);
|
||||
}
|
||||
|
||||
void FolderNode::insertChild(int p_idx, const QSharedPointer<Node> &p_node)
|
||||
{
|
||||
p_node->setParent(this);
|
||||
|
||||
m_children.insert(p_idx, p_node);
|
||||
}
|
||||
|
||||
void FolderNode::removeChild(const QSharedPointer<Node> &p_child)
|
||||
{
|
||||
if (m_children.removeOne(p_child)) {
|
||||
p_child->setParent(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
QDateTime FolderNode::getModifiedTimeUtc() const
|
||||
{
|
||||
return getCreatedTimeUtc();
|
||||
}
|
||||
|
||||
void FolderNode::setModifiedTimeUtc()
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
QDir FolderNode::toDir() const
|
||||
{
|
||||
return QDir(fetchAbsolutePath());
|
||||
}
|
40
src/core/notebook/foldernode.h
Normal file
40
src/core/notebook/foldernode.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef FOLDERNODE_H
|
||||
#define FOLDERNODE_H
|
||||
|
||||
#include "node.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class FolderNode : public Node
|
||||
{
|
||||
public:
|
||||
FolderNode(const QString &p_name,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent = nullptr);
|
||||
|
||||
void loadFolder(ID p_id,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QVector<QSharedPointer<Node>> &p_children);
|
||||
|
||||
QVector<QSharedPointer<Node>> getChildren() const Q_DECL_OVERRIDE;
|
||||
|
||||
int getChildrenCount() const Q_DECL_OVERRIDE;
|
||||
|
||||
void addChild(const QSharedPointer<Node> &p_node) Q_DECL_OVERRIDE;
|
||||
|
||||
void insertChild(int p_idx, const QSharedPointer<Node> &p_node) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeChild(const QSharedPointer<Node> &p_child) Q_DECL_OVERRIDE;
|
||||
|
||||
QDateTime getModifiedTimeUtc() const Q_DECL_OVERRIDE;
|
||||
|
||||
void setModifiedTimeUtc() Q_DECL_OVERRIDE;
|
||||
|
||||
QDir toDir() const Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
QVector<QSharedPointer<Node>> m_children;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // FOLDERNODE_H
|
46
src/core/notebook/inotebookfactory.h
Normal file
46
src/core/notebook/inotebookfactory.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef INOTEBOOKFACTORY_H
|
||||
#define INOTEBOOKFACTORY_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QIcon>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Notebook;
|
||||
class NotebookParameters;
|
||||
class INotebookBackend;
|
||||
class NotebookMgr;
|
||||
|
||||
// Abstract factory to create notebook.
|
||||
class INotebookFactory
|
||||
{
|
||||
public:
|
||||
virtual ~INotebookFactory()
|
||||
{
|
||||
}
|
||||
|
||||
// Get the name of this factory.
|
||||
virtual QString getName() const = 0;
|
||||
|
||||
// Get the display name of this factory.
|
||||
virtual QString getDisplayName() const = 0;
|
||||
|
||||
// Get the description of this factory.
|
||||
virtual QString getDescription() const = 0;
|
||||
|
||||
// New a notebook with given information and return an instance of that notebook.
|
||||
// The root folder should be empty.
|
||||
virtual QSharedPointer<Notebook> newNotebook(const NotebookParameters &p_paras) = 0;
|
||||
|
||||
// Create a Notebook instance from existing root folder.
|
||||
virtual QSharedPointer<Notebook> createNotebook(const NotebookMgr &p_mgr,
|
||||
const QString &p_rootFolderPath,
|
||||
const QSharedPointer<INotebookBackend> &p_backend) = 0;
|
||||
|
||||
// Check if @p_rootFolderPath is a valid root folder to use by this factory
|
||||
// to create a notebook.
|
||||
virtual bool checkRootFolder(const QSharedPointer<INotebookBackend> &p_backend) = 0;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // INOTEBOOKFACTORY_H
|
335
src/core/notebook/node.cpp
Normal file
335
src/core/notebook/node.cpp
Normal file
@ -0,0 +1,335 @@
|
||||
#include "node.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <notebookconfigmgr/inotebookconfigmgr.h>
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <core/exception.h>
|
||||
#include "notebook.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
Node::Node(Type p_type,
|
||||
ID p_id,
|
||||
const QString &p_name,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent)
|
||||
: m_type(p_type),
|
||||
m_id(p_id),
|
||||
m_name(p_name),
|
||||
m_createdTimeUtc(p_createdTimeUtc),
|
||||
m_loaded(true),
|
||||
m_notebook(p_notebook),
|
||||
m_parent(p_parent)
|
||||
{
|
||||
if (m_notebook) {
|
||||
m_configMgr = m_notebook->getConfigMgr();
|
||||
m_backend = m_notebook->getBackend();
|
||||
}
|
||||
}
|
||||
|
||||
Node::Node(Type p_type,
|
||||
const QString &p_name,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent)
|
||||
: m_type(p_type),
|
||||
m_name(p_name),
|
||||
m_notebook(p_notebook),
|
||||
m_parent(p_parent)
|
||||
{
|
||||
if (m_notebook) {
|
||||
m_configMgr = m_notebook->getConfigMgr();
|
||||
m_backend = m_notebook->getBackend();
|
||||
}
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
}
|
||||
|
||||
bool Node::isLoaded() const
|
||||
{
|
||||
return m_loaded;
|
||||
}
|
||||
|
||||
void Node::setLoaded(bool p_loaded)
|
||||
{
|
||||
m_loaded = p_loaded;
|
||||
}
|
||||
|
||||
void Node::loadInfo(ID p_id, const QDateTime &p_createdTimeUtc)
|
||||
{
|
||||
Q_ASSERT(!m_loaded);
|
||||
|
||||
m_id = p_id;
|
||||
m_createdTimeUtc = p_createdTimeUtc;
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
bool Node::isRoot() const
|
||||
{
|
||||
return !m_parent;
|
||||
}
|
||||
|
||||
const QString &Node::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
bool Node::hasChild(const QString &p_name, bool p_caseSensitive) const
|
||||
{
|
||||
return findChild(p_name, p_caseSensitive) != nullptr;
|
||||
}
|
||||
|
||||
bool Node::hasChild(const QSharedPointer<Node> &p_node) const
|
||||
{
|
||||
return getChildren().indexOf(p_node) != -1;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Node::findChild(const QString &p_name, bool p_caseSensitive) const
|
||||
{
|
||||
auto targetName = p_caseSensitive ? p_name : p_name.toLower();
|
||||
for (auto &child : getChildren()) {
|
||||
if (p_caseSensitive ? child->getName() == targetName
|
||||
: child->getName().toLower() == targetName) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Node::setParent(Node *p_parent)
|
||||
{
|
||||
m_parent = p_parent;
|
||||
}
|
||||
|
||||
Node *Node::getParent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
Node::Type Node::getType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
Node::Flags Node::getFlags() const
|
||||
{
|
||||
return m_flags;
|
||||
}
|
||||
|
||||
void Node::setFlags(Node::Flags p_flags)
|
||||
{
|
||||
m_flags = p_flags;
|
||||
}
|
||||
|
||||
Node::Use Node::getUse() const
|
||||
{
|
||||
return m_use;
|
||||
}
|
||||
|
||||
void Node::setUse(Node::Use p_use)
|
||||
{
|
||||
m_use = p_use;
|
||||
}
|
||||
|
||||
ID Node::getId() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const QDateTime &Node::getCreatedTimeUtc() const
|
||||
{
|
||||
return m_createdTimeUtc;
|
||||
}
|
||||
|
||||
Notebook *Node::getNotebook() const
|
||||
{
|
||||
return m_notebook;
|
||||
}
|
||||
|
||||
QString Node::fetchRelativePath() const
|
||||
{
|
||||
if (!m_parent) {
|
||||
return QString();
|
||||
} else {
|
||||
return PathUtils::concatenateFilePath(m_parent->fetchRelativePath(), m_name);
|
||||
}
|
||||
}
|
||||
|
||||
QString Node::fetchAbsolutePath() const
|
||||
{
|
||||
return PathUtils::concatenateFilePath(m_notebook->getRootFolderAbsolutePath(),
|
||||
fetchRelativePath());
|
||||
}
|
||||
|
||||
QString Node::fetchContentPath() const
|
||||
{
|
||||
return fetchAbsolutePath();
|
||||
}
|
||||
|
||||
void Node::load()
|
||||
{
|
||||
Q_ASSERT(m_notebook);
|
||||
m_notebook->load(this);
|
||||
}
|
||||
|
||||
void Node::save()
|
||||
{
|
||||
Q_ASSERT(m_notebook);
|
||||
m_notebook->save(this);
|
||||
}
|
||||
|
||||
void Node::setName(const QString &p_name)
|
||||
{
|
||||
m_name = p_name;
|
||||
}
|
||||
|
||||
void Node::updateName(const QString &p_name)
|
||||
{
|
||||
if (m_name == p_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_notebook->rename(this, p_name);
|
||||
Q_ASSERT(m_name == p_name);
|
||||
}
|
||||
|
||||
bool Node::isAncestor(const Node *p_ancestor, const Node *p_child)
|
||||
{
|
||||
if (!p_ancestor || !p_child) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (p_child) {
|
||||
p_child = p_child->getParent();
|
||||
if (p_child == p_ancestor) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Node::existsOnDisk() const
|
||||
{
|
||||
return m_configMgr->nodeExistsOnDisk(this);
|
||||
}
|
||||
|
||||
QString Node::read() const
|
||||
{
|
||||
return m_configMgr->readNode(this);
|
||||
}
|
||||
|
||||
void Node::write(const QString &p_content)
|
||||
{
|
||||
m_configMgr->writeNode(this, p_content);
|
||||
}
|
||||
|
||||
QString Node::fetchImageFolderPath()
|
||||
{
|
||||
return m_configMgr->fetchNodeImageFolderPath(this);
|
||||
}
|
||||
|
||||
QString Node::insertImage(const QString &p_srcImagePath, const QString &p_imageFileName)
|
||||
{
|
||||
const auto imageFolderPath = fetchImageFolderPath();
|
||||
auto destFilePath = m_backend->renameIfExistsCaseInsensitive(PathUtils::concatenateFilePath(imageFolderPath, p_imageFileName));
|
||||
m_backend->copyFile(p_srcImagePath, destFilePath);
|
||||
return destFilePath;
|
||||
}
|
||||
|
||||
QString Node::insertImage(const QImage &p_image, const QString &p_imageFileName)
|
||||
{
|
||||
const auto imageFolderPath = fetchImageFolderPath();
|
||||
auto destFilePath = m_backend->renameIfExistsCaseInsensitive(PathUtils::concatenateFilePath(imageFolderPath, p_imageFileName));
|
||||
p_image.save(destFilePath);
|
||||
m_backend->addFile(destFilePath);
|
||||
return destFilePath;
|
||||
}
|
||||
|
||||
void Node::removeImage(const QString &p_imagePath)
|
||||
{
|
||||
// Just move it to recycle bin but not added as a child node of recycle bin.
|
||||
m_notebook->moveFileToRecycleBin(p_imagePath);
|
||||
}
|
||||
|
||||
QString Node::getAttachmentFolder() const
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
void Node::setAttachmentFolder(const QString &p_folder)
|
||||
{
|
||||
Q_UNUSED(p_folder);
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
QString Node::fetchAttachmentFolderPath()
|
||||
{
|
||||
return m_configMgr->fetchNodeAttachmentFolderPath(this);
|
||||
}
|
||||
|
||||
QStringList Node::addAttachment(const QString &p_destFolderPath, const QStringList &p_files)
|
||||
{
|
||||
Q_UNUSED(p_destFolderPath);
|
||||
Q_UNUSED(p_files);
|
||||
Q_ASSERT(false);
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QString Node::newAttachmentFile(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
Q_UNUSED(p_destFolderPath);
|
||||
Q_UNUSED(p_name);
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString Node::newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name)
|
||||
{
|
||||
Q_UNUSED(p_destFolderPath);
|
||||
Q_UNUSED(p_name);
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString Node::renameAttachment(const QString &p_path, const QString &p_name)
|
||||
{
|
||||
Q_UNUSED(p_path);
|
||||
Q_UNUSED(p_name);
|
||||
Q_ASSERT(false);
|
||||
return QString();
|
||||
}
|
||||
|
||||
void Node::removeAttachment(const QStringList &p_paths)
|
||||
{
|
||||
Q_UNUSED(p_paths);
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
QStringList Node::getTags() const
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QDir Node::toDir() const
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return QDir();
|
||||
}
|
||||
|
||||
INotebookBackend *Node::getBackend() const
|
||||
{
|
||||
return m_backend.data();
|
||||
}
|
||||
|
||||
bool Node::isReadOnly() const
|
||||
{
|
||||
return m_flags & Flag::ReadOnly;
|
||||
}
|
196
src/core/notebook/node.h
Normal file
196
src/core/notebook/node.h
Normal file
@ -0,0 +1,196 @@
|
||||
#ifndef NODE_H
|
||||
#define NODE_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
#include <QDir>
|
||||
|
||||
#include <global.h>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class Notebook;
|
||||
class INotebookConfigMgr;
|
||||
class INotebookBackend;
|
||||
|
||||
// Used when add/new a node.
|
||||
struct NodeParameters
|
||||
{
|
||||
QDateTime m_createdTimeUtc = QDateTime::currentDateTimeUtc();
|
||||
QDateTime m_modifiedTimeUtc = QDateTime::currentDateTimeUtc();
|
||||
QString m_attachmentFolder;
|
||||
QStringList m_tags;
|
||||
};
|
||||
|
||||
// Node of notebook.
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
Folder,
|
||||
File
|
||||
};
|
||||
|
||||
enum Flag {
|
||||
None = 0,
|
||||
ReadOnly = 0x1
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
|
||||
enum Use {
|
||||
Normal,
|
||||
RecycleBin
|
||||
};
|
||||
|
||||
// Constructor with all information loaded.
|
||||
Node(Type p_type,
|
||||
ID p_id,
|
||||
const QString &p_name,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent = nullptr);
|
||||
|
||||
// Constructor not loaded.
|
||||
Node(Type p_type,
|
||||
const QString &p_name,
|
||||
Notebook *p_notebook,
|
||||
Node *p_parent = nullptr);
|
||||
|
||||
virtual ~Node();
|
||||
|
||||
enum { InvalidId = 0 };
|
||||
|
||||
bool isLoaded() const;
|
||||
|
||||
bool isRoot() const;
|
||||
|
||||
const QString &getName() const;
|
||||
void setName(const QString &p_name);
|
||||
|
||||
// Change the config and backend file as well.
|
||||
void updateName(const QString &p_name);
|
||||
|
||||
Node::Type getType() const;
|
||||
|
||||
Node::Flags getFlags() const;
|
||||
void setFlags(Node::Flags p_flags);
|
||||
|
||||
Node::Use getUse() const;
|
||||
void setUse(Node::Use p_use);
|
||||
|
||||
ID getId() const;
|
||||
|
||||
const QDateTime &getCreatedTimeUtc() const;
|
||||
|
||||
virtual QDateTime getModifiedTimeUtc() const = 0;
|
||||
virtual void setModifiedTimeUtc() = 0;
|
||||
|
||||
virtual QString getAttachmentFolder() const;
|
||||
virtual void setAttachmentFolder(const QString &p_folder);
|
||||
|
||||
virtual QVector<QSharedPointer<Node>> getChildren() const = 0;
|
||||
|
||||
virtual int getChildrenCount() const = 0;
|
||||
|
||||
QSharedPointer<Node> findChild(const QString &p_name, bool p_caseSensitive = true) const;
|
||||
|
||||
bool hasChild(const QString &p_name, bool p_caseSensitive = true) const;
|
||||
|
||||
bool hasChild(const QSharedPointer<Node> &p_node) const;
|
||||
|
||||
virtual void addChild(const QSharedPointer<Node> &p_node) = 0;
|
||||
|
||||
virtual void insertChild(int p_idx, const QSharedPointer<Node> &p_node) = 0;
|
||||
|
||||
virtual void removeChild(const QSharedPointer<Node> &p_node) = 0;
|
||||
|
||||
void setParent(Node *p_parent);
|
||||
Node *getParent() const;
|
||||
|
||||
Notebook *getNotebook() const;
|
||||
|
||||
// Path to the node.
|
||||
QString fetchRelativePath() const;
|
||||
|
||||
QString fetchAbsolutePath() const;
|
||||
|
||||
// A node may be a container of all the stuffs, so the node's path may not be identical with
|
||||
// the content file path, like TextBundle.
|
||||
virtual QString fetchContentPath() const;
|
||||
|
||||
// Get image folder path.
|
||||
virtual QString fetchImageFolderPath();
|
||||
|
||||
virtual void load();
|
||||
virtual void save();
|
||||
|
||||
static bool isAncestor(const Node *p_ancestor, const Node *p_child);
|
||||
|
||||
bool existsOnDisk() const;
|
||||
|
||||
QString read() const;
|
||||
void write(const QString &p_content);
|
||||
|
||||
// Insert image from @p_srcImagePath.
|
||||
// Return inserted image file path.
|
||||
virtual QString insertImage(const QString &p_srcImagePath, const QString &p_imageFileName);
|
||||
|
||||
virtual QString insertImage(const QImage &p_image, const QString &p_imageFileName);
|
||||
|
||||
virtual void removeImage(const QString &p_imagePath);
|
||||
|
||||
// Get attachment folder path.
|
||||
virtual QString fetchAttachmentFolderPath();
|
||||
|
||||
virtual QStringList addAttachment(const QString &p_destFolderPath, const QStringList &p_files);
|
||||
|
||||
virtual QString newAttachmentFile(const QString &p_destFolderPath, const QString &p_name);
|
||||
|
||||
virtual QString newAttachmentFolder(const QString &p_destFolderPath, const QString &p_name);
|
||||
|
||||
virtual QString renameAttachment(const QString &p_path, const QString &p_name);
|
||||
|
||||
virtual void removeAttachment(const QStringList &p_paths);
|
||||
|
||||
virtual QStringList getTags() const;
|
||||
|
||||
virtual QDir toDir() const;
|
||||
|
||||
INotebookBackend *getBackend() const;
|
||||
|
||||
bool isReadOnly() const;
|
||||
|
||||
protected:
|
||||
void loadInfo(ID p_id, const QDateTime &p_createdTimeUtc);
|
||||
|
||||
void setLoaded(bool p_loaded);
|
||||
|
||||
Notebook *m_notebook = nullptr;
|
||||
|
||||
QSharedPointer<INotebookConfigMgr> m_configMgr;
|
||||
|
||||
QSharedPointer<INotebookBackend> m_backend;
|
||||
|
||||
private:
|
||||
Type m_type = Type::Folder;
|
||||
|
||||
Flags m_flags = Flag::None;
|
||||
|
||||
Use m_use = Use::Normal;
|
||||
|
||||
ID m_id = InvalidId;
|
||||
|
||||
QString m_name;
|
||||
|
||||
QDateTime m_createdTimeUtc;
|
||||
|
||||
bool m_loaded = false;
|
||||
|
||||
Node *m_parent = nullptr;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Node::Flags)
|
||||
} // ns vnotex
|
||||
|
||||
#endif // NODE_H
|
380
src/core/notebook/notebook.cpp
Normal file
380
src/core/notebook/notebook.cpp
Normal file
@ -0,0 +1,380 @@
|
||||
#include "notebook.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <versioncontroller/iversioncontroller.h>
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
#include <notebookconfigmgr/inotebookconfigmgr.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include "exception.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const QString Notebook::c_defaultAttachmentFolder = QStringLiteral("vx_attachments");
|
||||
|
||||
const QString Notebook::c_defaultImageFolder = QStringLiteral("vx_images");
|
||||
|
||||
static vnotex::ID generateNotebookID()
|
||||
{
|
||||
static vnotex::ID id = Notebook::InvalidId;
|
||||
return ++id;
|
||||
}
|
||||
|
||||
Notebook::Notebook(const NotebookParameters &p_paras,
|
||||
QObject *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_id(generateNotebookID()),
|
||||
m_type(p_paras.m_type),
|
||||
m_name(p_paras.m_name),
|
||||
m_description(p_paras.m_description),
|
||||
m_rootFolderPath(p_paras.m_rootFolderPath),
|
||||
m_icon(p_paras.m_icon),
|
||||
m_imageFolder(p_paras.m_imageFolder),
|
||||
m_attachmentFolder(p_paras.m_attachmentFolder),
|
||||
m_createdTimeUtc(p_paras.m_createdTimeUtc),
|
||||
m_backend(p_paras.m_notebookBackend),
|
||||
m_versionController(p_paras.m_versionController),
|
||||
m_configMgr(p_paras.m_notebookConfigMgr)
|
||||
{
|
||||
if (m_imageFolder.isEmpty()) {
|
||||
m_imageFolder = c_defaultImageFolder;
|
||||
}
|
||||
if (m_attachmentFolder.isEmpty()) {
|
||||
m_attachmentFolder = c_defaultAttachmentFolder;
|
||||
}
|
||||
m_configMgr->setNotebook(this);
|
||||
}
|
||||
|
||||
Notebook::~Notebook()
|
||||
{
|
||||
}
|
||||
|
||||
vnotex::ID Notebook::getId() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
const QString &Notebook::getType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
const QString &Notebook::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void Notebook::setName(const QString &p_name)
|
||||
{
|
||||
m_name = p_name;
|
||||
}
|
||||
|
||||
void Notebook::updateName(const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(!p_name.isEmpty());
|
||||
if (p_name == m_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_name = p_name;
|
||||
updateNotebookConfig();
|
||||
emit updated();
|
||||
}
|
||||
|
||||
const QString &Notebook::getDescription() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
void Notebook::setDescription(const QString &p_description)
|
||||
{
|
||||
m_description = p_description;
|
||||
}
|
||||
|
||||
void Notebook::updateDescription(const QString &p_description)
|
||||
{
|
||||
if (p_description == m_description) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_description = p_description;
|
||||
updateNotebookConfig();
|
||||
emit updated();
|
||||
}
|
||||
|
||||
const QString &Notebook::getRootFolderPath() const
|
||||
{
|
||||
return m_rootFolderPath;
|
||||
}
|
||||
|
||||
QString Notebook::getRootFolderAbsolutePath() const
|
||||
{
|
||||
return PathUtils::absolutePath(m_rootFolderPath);
|
||||
}
|
||||
|
||||
const QIcon &Notebook::getIcon() const
|
||||
{
|
||||
return m_icon;
|
||||
}
|
||||
|
||||
void Notebook::setIcon(const QIcon &p_icon)
|
||||
{
|
||||
m_icon = p_icon;
|
||||
}
|
||||
|
||||
const QString &Notebook::getImageFolder() const
|
||||
{
|
||||
return m_imageFolder;
|
||||
}
|
||||
|
||||
const QString &Notebook::getAttachmentFolder() const
|
||||
{
|
||||
return m_attachmentFolder;
|
||||
}
|
||||
|
||||
const QSharedPointer<INotebookBackend> &Notebook::getBackend() const
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
const QSharedPointer<IVersionController> &Notebook::getVersionController() const
|
||||
{
|
||||
return m_versionController;
|
||||
}
|
||||
|
||||
const QSharedPointer<INotebookConfigMgr> &Notebook::getConfigMgr() const
|
||||
{
|
||||
return m_configMgr;
|
||||
}
|
||||
|
||||
const QSharedPointer<Node> &Notebook::getRootNode() const
|
||||
{
|
||||
if (!m_root) {
|
||||
const_cast<Notebook *>(this)->m_root = m_configMgr->loadRootNode();
|
||||
}
|
||||
|
||||
return m_root;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::getRecycleBinNode() const
|
||||
{
|
||||
auto root = getRootNode();
|
||||
auto children = root->getChildren();
|
||||
auto it = std::find_if(children.begin(),
|
||||
children.end(),
|
||||
[this](const QSharedPointer<Node> &p_node) {
|
||||
return isRecycleBinNode(p_node.data());
|
||||
});
|
||||
|
||||
if (it != children.end()) {
|
||||
return *it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::newNode(Node *p_parent, Node::Type p_type, const QString &p_name)
|
||||
{
|
||||
return m_configMgr->newNode(p_parent, p_type, p_name);
|
||||
}
|
||||
|
||||
const QDateTime &Notebook::getCreatedTimeUtc() const
|
||||
{
|
||||
return m_createdTimeUtc;
|
||||
}
|
||||
|
||||
void Notebook::load(Node *p_node)
|
||||
{
|
||||
Q_ASSERT(p_node->getNotebook() == this);
|
||||
if (p_node->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_configMgr->loadNode(p_node);
|
||||
}
|
||||
|
||||
void Notebook::save(const Node *p_node)
|
||||
{
|
||||
Q_ASSERT(p_node->getNotebook() == this);
|
||||
m_configMgr->saveNode(p_node);
|
||||
}
|
||||
|
||||
void Notebook::rename(Node *p_node, const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(p_node->getNotebook() == this);
|
||||
m_configMgr->renameNode(p_node, p_name);
|
||||
|
||||
emit nodeUpdated(p_node);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::loadNodeByPath(const QString &p_path)
|
||||
{
|
||||
if (!PathUtils::pathContains(m_rootFolderPath, p_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString relativePath;
|
||||
QFileInfo fi(p_path);
|
||||
if (fi.isAbsolute()) {
|
||||
if (!fi.exists()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
relativePath = PathUtils::relativePath(m_rootFolderPath, p_path);
|
||||
} else {
|
||||
relativePath = p_path;
|
||||
}
|
||||
|
||||
return m_configMgr->loadNodeByPath(m_root, relativePath);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::copyNodeAsChildOf(const QSharedPointer<Node> &p_src, Node *p_dest, bool p_move)
|
||||
{
|
||||
Q_ASSERT(p_src != p_dest);
|
||||
Q_ASSERT(p_dest->getNotebook() == this);
|
||||
|
||||
if (Node::isAncestor(p_src.data(), p_dest)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("source (%1) is the ancestor of destination (%2)")
|
||||
.arg(p_src->fetchRelativePath(), p_dest->fetchRelativePath()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (p_src->getParent() == p_dest && p_move) {
|
||||
return p_src;
|
||||
}
|
||||
|
||||
return m_configMgr->copyNodeAsChildOf(p_src, p_dest, p_move);
|
||||
}
|
||||
|
||||
void Notebook::removeNode(const QSharedPointer<Node> &p_node, bool p_force, bool p_configOnly)
|
||||
{
|
||||
Q_ASSERT(p_node->getNotebook() == this);
|
||||
m_configMgr->removeNode(p_node, p_force, p_configOnly);
|
||||
}
|
||||
|
||||
void Notebook::removeNode(const Node *p_node, bool p_force, bool p_configOnly)
|
||||
{
|
||||
Q_ASSERT(p_node && !p_node->isRoot());
|
||||
auto children = p_node->getParent()->getChildren();
|
||||
auto it = std::find(children.begin(), children.end(), p_node);
|
||||
Q_ASSERT(it != children.end());
|
||||
removeNode(*it, p_force, p_configOnly);
|
||||
}
|
||||
|
||||
bool Notebook::isRecycleBinNode(const Node *p_node) const
|
||||
{
|
||||
return p_node && p_node->getUse() == Node::Use::RecycleBin;
|
||||
}
|
||||
|
||||
bool Notebook::isNodeInRecycleBin(const Node *p_node) const
|
||||
{
|
||||
if (p_node) {
|
||||
p_node = p_node->getParent();
|
||||
while (p_node) {
|
||||
if (isRecycleBinNode(p_node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
p_node = p_node->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Notebook::moveNodeToRecycleBin(const Node *p_node)
|
||||
{
|
||||
Q_ASSERT(p_node && !p_node->isRoot());
|
||||
auto children = p_node->getParent()->getChildren();
|
||||
for (auto &child : children) {
|
||||
if (p_node == child) {
|
||||
moveNodeToRecycleBin(child);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
void Notebook::moveNodeToRecycleBin(const QSharedPointer<Node> &p_node)
|
||||
{
|
||||
auto destNode = getOrCreateRecycleBinDateNode();
|
||||
copyNodeAsChildOf(p_node, destNode.data(), true);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::getOrCreateRecycleBinDateNode()
|
||||
{
|
||||
// Name after date.
|
||||
auto dateNodeName = QDate::currentDate().toString(QStringLiteral("yyyyMMdd"));
|
||||
|
||||
auto recycleBinNode = getRecycleBinNode();
|
||||
auto dateNode = recycleBinNode->findChild(dateNodeName,
|
||||
FileUtils::isPlatformNameCaseSensitive());
|
||||
if (!dateNode) {
|
||||
// Create a date node.
|
||||
dateNode = newNode(recycleBinNode.data(), Node::Type::Folder, dateNodeName);
|
||||
}
|
||||
|
||||
return dateNode;
|
||||
}
|
||||
|
||||
void Notebook::emptyNode(const Node *p_node, bool p_force)
|
||||
{
|
||||
auto children = p_node->getChildren();
|
||||
for (auto &child : children) {
|
||||
removeNode(child, p_force);
|
||||
}
|
||||
}
|
||||
|
||||
void Notebook::moveFileToRecycleBin(const QString &p_filePath)
|
||||
{
|
||||
auto node = getOrCreateRecycleBinDateNode();
|
||||
auto destFilePath = PathUtils::concatenateFilePath(node->fetchRelativePath(),
|
||||
PathUtils::fileName(p_filePath));
|
||||
destFilePath = getBackend()->renameIfExistsCaseInsensitive(destFilePath);
|
||||
m_backend->copyFile(p_filePath, destFilePath);
|
||||
|
||||
getBackend()->removeFile(p_filePath);
|
||||
|
||||
emit nodeUpdated(node.data());
|
||||
}
|
||||
|
||||
void Notebook::moveDirToRecycleBin(const QString &p_dirPath)
|
||||
{
|
||||
auto node = getOrCreateRecycleBinDateNode();
|
||||
auto destDirPath = PathUtils::concatenateFilePath(node->fetchRelativePath(),
|
||||
PathUtils::fileName(p_dirPath));
|
||||
destDirPath = getBackend()->renameIfExistsCaseInsensitive(destDirPath);
|
||||
m_backend->copyDir(p_dirPath, destDirPath);
|
||||
|
||||
getBackend()->removeDir(p_dirPath);
|
||||
|
||||
emit nodeUpdated(node.data());
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::addAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_name,
|
||||
const NodeParameters &p_paras)
|
||||
{
|
||||
return m_configMgr->addAsNode(p_parent, p_type, p_name, p_paras);
|
||||
}
|
||||
|
||||
bool Notebook::isBuiltInFile(const Node *p_node, const QString &p_name) const
|
||||
{
|
||||
return m_configMgr->isBuiltInFile(p_node, p_name);
|
||||
}
|
||||
|
||||
bool Notebook::isBuiltInFolder(const Node *p_node, const QString &p_name) const
|
||||
{
|
||||
return m_configMgr->isBuiltInFolder(p_node, p_name);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> Notebook::copyAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_path)
|
||||
{
|
||||
return m_configMgr->copyAsNode(p_parent, p_type, p_path);
|
||||
}
|
184
src/core/notebook/notebook.h
Normal file
184
src/core/notebook/notebook.h
Normal file
@ -0,0 +1,184 @@
|
||||
#ifndef NOTEBOOK_H
|
||||
#define NOTEBOOK_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QIcon>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "notebookparameters.h"
|
||||
#include "../global.h"
|
||||
#include "node.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class INotebookBackend;
|
||||
class IVersionController;
|
||||
class INotebookConfigMgr;
|
||||
struct NodeParameters;
|
||||
|
||||
// Base class of notebook.
|
||||
class Notebook : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Notebook(const NotebookParameters &p_paras,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
virtual ~Notebook();
|
||||
|
||||
enum { InvalidId = 0 };
|
||||
|
||||
ID getId() const;
|
||||
|
||||
const QString &getType() const;
|
||||
|
||||
const QString &getName() const;
|
||||
void setName(const QString &p_name);
|
||||
// Change the config and backend file as well.
|
||||
void updateName(const QString &p_name);
|
||||
|
||||
const QString &getDescription() const;
|
||||
void setDescription(const QString &p_description);
|
||||
void updateDescription(const QString &p_description);
|
||||
|
||||
// Use getRootFolderAbsolutePath() instead for access.
|
||||
const QString &getRootFolderPath() const;
|
||||
|
||||
QString getRootFolderAbsolutePath() const;
|
||||
|
||||
const QIcon &getIcon() const;
|
||||
void setIcon(const QIcon &p_icon);
|
||||
|
||||
const QString &getImageFolder() const;
|
||||
|
||||
const QString &getAttachmentFolder() const;
|
||||
|
||||
const QDateTime &getCreatedTimeUtc() const;
|
||||
|
||||
const QSharedPointer<INotebookBackend> &getBackend() const;
|
||||
|
||||
const QSharedPointer<IVersionController> &getVersionController() const;
|
||||
|
||||
const QSharedPointer<INotebookConfigMgr> &getConfigMgr() const;
|
||||
|
||||
const QSharedPointer<Node> &getRootNode() const;
|
||||
|
||||
QSharedPointer<Node> getRecycleBinNode() const;
|
||||
|
||||
QSharedPointer<Node> newNode(Node *p_parent, Node::Type p_type, const QString &p_name);
|
||||
|
||||
// Add @p_name under @p_parent to add as a new node @p_type.
|
||||
QSharedPointer<Node> addAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_name,
|
||||
const NodeParameters &p_paras);
|
||||
|
||||
// Copy @p_path to @p_parent and add as a new node @p_type.
|
||||
QSharedPointer<Node> copyAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_path);
|
||||
|
||||
virtual ID getNextNodeId() const = 0;
|
||||
|
||||
virtual ID getAndUpdateNextNodeId() = 0;
|
||||
|
||||
virtual void load(Node *p_node);
|
||||
virtual void save(const Node *p_node);
|
||||
|
||||
virtual void rename(Node *p_node, const QString &p_name);
|
||||
|
||||
virtual void updateNotebookConfig() = 0;
|
||||
|
||||
virtual void removeNotebookConfig() = 0;
|
||||
|
||||
// @p_path could be absolute or relative.
|
||||
virtual QSharedPointer<Node> loadNodeByPath(const QString &p_path);
|
||||
|
||||
// Copy @p_src as a child of @p_dest. They may belong to different notebooks.
|
||||
virtual QSharedPointer<Node> copyNodeAsChildOf(const QSharedPointer<Node> &p_src, Node *p_dest, bool p_move);
|
||||
|
||||
// Remove @p_node and delete all related files from disk.
|
||||
// @p_force: if true, will delete all files including files not tracked by configmgr.
|
||||
// @p_configOnly: if true, will just remove node from config.
|
||||
void removeNode(const QSharedPointer<Node> &p_node, bool p_force = false, bool p_configOnly = false);
|
||||
|
||||
void removeNode(const Node *p_node, bool p_force = false, bool p_configOnly = false);
|
||||
|
||||
void moveNodeToRecycleBin(const QSharedPointer<Node> &p_node);
|
||||
|
||||
void moveNodeToRecycleBin(const Node *p_node);
|
||||
|
||||
// Move @p_filePath to the recycle bin, without adding it as a child node.
|
||||
void moveFileToRecycleBin(const QString &p_filePath);
|
||||
|
||||
// Move @p_dirPath to the recycle bin, without adding it as a child node.
|
||||
void moveDirToRecycleBin(const QString &p_dirPath);
|
||||
|
||||
// Remove all files of this notebook from disk.
|
||||
virtual void remove() = 0;
|
||||
|
||||
bool isRecycleBinNode(const Node *p_node) const;
|
||||
|
||||
bool isNodeInRecycleBin(const Node *p_node) const;
|
||||
|
||||
// Remove all children node of @p_node.
|
||||
// @p_force: if true, just delete all folders and files under @p_node.
|
||||
void emptyNode(const Node *p_node, bool p_force = false);
|
||||
|
||||
// Whether @p_name is a built-in file under @p_node.
|
||||
bool isBuiltInFile(const Node *p_node, const QString &p_name) const;
|
||||
|
||||
bool isBuiltInFolder(const Node *p_node, const QString &p_name) const;
|
||||
|
||||
static const QString c_defaultAttachmentFolder;
|
||||
|
||||
static const QString c_defaultImageFolder;
|
||||
|
||||
signals:
|
||||
void updated();
|
||||
|
||||
void nodeUpdated(const Node *p_node);
|
||||
|
||||
private:
|
||||
QSharedPointer<Node> getOrCreateRecycleBinDateNode();
|
||||
|
||||
// ID of this notebook.
|
||||
// Will be assigned uniquely once loaded.
|
||||
ID m_id;
|
||||
|
||||
// Type of this notebook.
|
||||
QString m_type;
|
||||
|
||||
// Name of this notebook.
|
||||
QString m_name;
|
||||
|
||||
// Description of this notebook.
|
||||
QString m_description;
|
||||
|
||||
// Path of the notebook root folder.
|
||||
QString m_rootFolderPath;
|
||||
|
||||
QIcon m_icon;
|
||||
|
||||
// Name of the folder to hold images.
|
||||
QString m_imageFolder;
|
||||
|
||||
// Name of the folder to hold attachments.
|
||||
QString m_attachmentFolder;
|
||||
|
||||
QDateTime m_createdTimeUtc;
|
||||
|
||||
// Backend for file access and synchronization.
|
||||
QSharedPointer<INotebookBackend> m_backend;
|
||||
|
||||
// Version controller.
|
||||
QSharedPointer<IVersionController> m_versionController;
|
||||
|
||||
// Config manager to read/wirte config files.
|
||||
QSharedPointer<INotebookConfigMgr> m_configMgr;
|
||||
|
||||
QSharedPointer<Node> m_root;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // NOTEBOOK_H
|
18
src/core/notebook/notebook.pri
Normal file
18
src/core/notebook/notebook.pri
Normal file
@ -0,0 +1,18 @@
|
||||
SOURCES += \
|
||||
$$PWD/notebook.cpp \
|
||||
$$PWD/bundlenotebookfactory.cpp \
|
||||
$$PWD/notebookparameters.cpp \
|
||||
$$PWD/bundlenotebook.cpp \
|
||||
$$PWD/node.cpp \
|
||||
$$PWD/filenode.cpp \
|
||||
$$PWD/foldernode.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/notebook.h \
|
||||
$$PWD/inotebookfactory.h \
|
||||
$$PWD/bundlenotebookfactory.h \
|
||||
$$PWD/notebookparameters.h \
|
||||
$$PWD/bundlenotebook.h \
|
||||
$$PWD/node.h \
|
||||
$$PWD/filenode.h \
|
||||
$$PWD/foldernode.h
|
64
src/core/notebook/notebookparameters.cpp
Normal file
64
src/core/notebook/notebookparameters.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "notebookparameters.h"
|
||||
|
||||
#include "notebookmgr.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
QSharedPointer<NotebookParameters> NotebookParameters::createNotebookParameters(
|
||||
const NotebookMgr &p_mgr,
|
||||
const QString &p_type,
|
||||
const QString &p_name,
|
||||
const QString &p_description,
|
||||
const QString &p_rootFolderPath,
|
||||
const QIcon &p_icon,
|
||||
const QString &p_imageFolder,
|
||||
const QString &p_attachmentFolder,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QString &p_backend,
|
||||
const QString &p_versionController,
|
||||
const QString &p_configMgr)
|
||||
{
|
||||
auto backend = p_mgr.createNotebookBackend(p_backend, p_rootFolderPath);
|
||||
return createNotebookParameters(p_mgr,
|
||||
backend,
|
||||
p_type,
|
||||
p_name,
|
||||
p_description,
|
||||
p_rootFolderPath,
|
||||
p_icon,
|
||||
p_imageFolder,
|
||||
p_attachmentFolder,
|
||||
p_createdTimeUtc,
|
||||
p_versionController,
|
||||
p_configMgr);
|
||||
}
|
||||
|
||||
QSharedPointer<NotebookParameters> NotebookParameters::createNotebookParameters(
|
||||
const NotebookMgr &p_mgr,
|
||||
const QSharedPointer<INotebookBackend> &p_backend,
|
||||
const QString &p_type,
|
||||
const QString &p_name,
|
||||
const QString &p_description,
|
||||
const QString &p_rootFolderPath,
|
||||
const QIcon &p_icon,
|
||||
const QString &p_imageFolder,
|
||||
const QString &p_attachmentFolder,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QString &p_versionController,
|
||||
const QString &p_configMgr)
|
||||
{
|
||||
auto paras = QSharedPointer<NotebookParameters>::create();
|
||||
paras->m_type = p_type;
|
||||
paras->m_name = p_name;
|
||||
paras->m_description = p_description;
|
||||
paras->m_rootFolderPath = p_rootFolderPath;
|
||||
paras->m_icon = p_icon;
|
||||
paras->m_imageFolder = p_imageFolder;
|
||||
paras->m_attachmentFolder = p_attachmentFolder;
|
||||
paras->m_createdTimeUtc = p_createdTimeUtc;
|
||||
paras->m_notebookBackend = p_backend;
|
||||
paras->m_versionController = p_mgr.createVersionController(p_versionController);
|
||||
paras->m_notebookConfigMgr = p_mgr.createNotebookConfigMgr(p_configMgr,
|
||||
paras->m_notebookBackend);
|
||||
return paras;
|
||||
}
|
70
src/core/notebook/notebookparameters.h
Normal file
70
src/core/notebook/notebookparameters.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef NOTEBOOKPARAMETERS_H
|
||||
#define NOTEBOOKPARAMETERS_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QIcon>
|
||||
#include <QDateTime>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class NotebookMgr;
|
||||
class INotebookBackend;
|
||||
class IVersionController;
|
||||
class INotebookConfigMgr;
|
||||
|
||||
// Used to new a notebook.
|
||||
class NotebookParameters
|
||||
{
|
||||
public:
|
||||
virtual ~NotebookParameters() {}
|
||||
|
||||
static QSharedPointer<NotebookParameters> createNotebookParameters(
|
||||
const NotebookMgr &p_mgr,
|
||||
const QString &p_type,
|
||||
const QString &p_name,
|
||||
const QString &p_description,
|
||||
const QString &p_rootFolderPath,
|
||||
const QIcon &p_icon,
|
||||
const QString &p_imageFolder,
|
||||
const QString &p_attachmentFolder,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QString &p_backend,
|
||||
const QString &p_versionController,
|
||||
const QString &p_configMgr);
|
||||
|
||||
static QSharedPointer<NotebookParameters> createNotebookParameters(
|
||||
const NotebookMgr &p_mgr,
|
||||
const QSharedPointer<INotebookBackend> &p_backend,
|
||||
const QString &p_type,
|
||||
const QString &p_name,
|
||||
const QString &p_description,
|
||||
const QString &p_rootFolderPath,
|
||||
const QIcon &p_icon,
|
||||
const QString &p_imageFolder,
|
||||
const QString &p_attachmentFolder,
|
||||
const QDateTime &p_createdTimeUtc,
|
||||
const QString &p_versionController,
|
||||
const QString &p_configMgr);
|
||||
|
||||
QString m_type;
|
||||
QString m_name;
|
||||
QString m_description;
|
||||
QString m_rootFolderPath;
|
||||
QIcon m_icon;
|
||||
|
||||
// Name of image folder.
|
||||
QString m_imageFolder;
|
||||
|
||||
// Name of attachment folder.
|
||||
QString m_attachmentFolder;
|
||||
|
||||
QDateTime m_createdTimeUtc;
|
||||
QSharedPointer<INotebookBackend> m_notebookBackend;
|
||||
QSharedPointer<IVersionController> m_versionController;
|
||||
QSharedPointer<INotebookConfigMgr> m_notebookConfigMgr;
|
||||
|
||||
bool m_ensureEmptyRootFolder = true;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // NOTEBOOKPARAMETERS_H
|
23
src/core/notebookbackend/inotebookbackend.cpp
Normal file
23
src/core/notebookbackend/inotebookbackend.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "inotebookbackend.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#include <exception.h>
|
||||
#include <utils/pathutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
void INotebookBackend::constrainPath(const QString &p_path) const
|
||||
{
|
||||
if (!PathUtils::pathContains(m_rootPath, p_path)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("path (%1) does not locate in root folder (%2)")
|
||||
.arg(p_path, m_rootPath));
|
||||
}
|
||||
}
|
||||
|
||||
QString INotebookBackend::getFullPath(const QString &p_path) const
|
||||
{
|
||||
constrainPath(p_path);
|
||||
return QDir(m_rootPath).filePath(p_path);
|
||||
}
|
111
src/core/notebookbackend/inotebookbackend.h
Normal file
111
src/core/notebookbackend/inotebookbackend.h
Normal file
@ -0,0 +1,111 @@
|
||||
#ifndef INOTEBOOKBACKEND_H
|
||||
#define INOTEBOOKBACKEND_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <utils/pathutils.h>
|
||||
|
||||
class QByteArray;
|
||||
class QJsonObject;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Abstract class for notebook backend, which is responsible for file access
|
||||
// and synchronization.
|
||||
class INotebookBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
INotebookBackend(const QString &p_rootPath, QObject *p_parent = nullptr)
|
||||
: QObject(p_parent),
|
||||
m_rootPath(PathUtils::absolutePath(p_rootPath))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~INotebookBackend()
|
||||
{
|
||||
}
|
||||
|
||||
virtual QString getName() const = 0;
|
||||
|
||||
virtual QString getDisplayName() const = 0;
|
||||
|
||||
virtual QString getDescription() const = 0;
|
||||
|
||||
const QString &getRootPath() const
|
||||
{
|
||||
return m_rootPath;
|
||||
}
|
||||
|
||||
void setRootPath(const QString &p_rootPath)
|
||||
{
|
||||
m_rootPath = p_rootPath;
|
||||
}
|
||||
|
||||
// Whether @p_dirPath is an empty directory.
|
||||
virtual bool isEmptyDir(const QString &p_dirPath) const = 0;
|
||||
|
||||
// Create the directory path @p_dirPath. Create all parent directories if necessary.
|
||||
virtual void makePath(const QString &p_dirPath) = 0;
|
||||
|
||||
// Write @p_data to @p_filePath.
|
||||
virtual void writeFile(const QString &p_filePath, const QByteArray &p_data) = 0;
|
||||
|
||||
// Write @p_text to @p_filePath.
|
||||
virtual void writeFile(const QString &p_filePath, const QString &p_text) = 0;
|
||||
|
||||
// Write @p_jobj to @p_filePath.
|
||||
virtual void writeFile(const QString &p_filePath, const QJsonObject &p_jobj) = 0;
|
||||
|
||||
// Read content from @p_filePath.
|
||||
virtual QString readTextFile(const QString &p_filePath) = 0;
|
||||
|
||||
// Read file @p_filePath.
|
||||
virtual QByteArray readFile(const QString &p_filePath) = 0;
|
||||
|
||||
QString getFullPath(const QString &p_path) const;
|
||||
|
||||
virtual bool exists(const QString &p_path) const = 0;
|
||||
|
||||
virtual bool childExistsCaseInsensitive(const QString &p_dirPath, const QString &p_name) const = 0;
|
||||
|
||||
virtual bool isFile(const QString &p_path) const = 0;
|
||||
|
||||
virtual void renameFile(const QString &p_filePath, const QString &p_name) = 0;
|
||||
|
||||
virtual void renameDir(const QString &p_dirPath, const QString &p_name) = 0;
|
||||
|
||||
// Copy @p_filePath to @p_destPath.
|
||||
// @p_filePath could be outside notebook.
|
||||
virtual void copyFile(const QString &p_filePath, const QString &p_destPath) = 0;
|
||||
|
||||
// Delete @p_filePath from disk.
|
||||
virtual void removeFile(const QString &p_filePath) = 0;
|
||||
|
||||
// Copy @p_dirPath to as @p_destPath.
|
||||
virtual void copyDir(const QString &p_dirPath, const QString &p_destPath) = 0;
|
||||
|
||||
// Delete @p_dirPath from disk if it is empty.
|
||||
// Return false if it is not deleted due to non-empty.
|
||||
virtual bool removeDirIfEmpty(const QString &p_dirPath) = 0;
|
||||
|
||||
virtual void removeDir(const QString &p_dirPath) = 0;
|
||||
|
||||
virtual QString renameIfExistsCaseInsensitive(const QString &p_path) const = 0;
|
||||
|
||||
// Add one file to backend.
|
||||
virtual void addFile(const QString &p_path) = 0;
|
||||
|
||||
virtual void removeEmptyDir(const QString &p_dirPath) = 0;
|
||||
|
||||
protected:
|
||||
// Constrain @p_path within root path of the notebook.
|
||||
void constrainPath(const QString &p_path) const;
|
||||
|
||||
private:
|
||||
// Root path of the notebook.
|
||||
QString m_rootPath;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // INOTEBOOKBACKEND_H
|
31
src/core/notebookbackend/inotebookbackendfactory.h
Normal file
31
src/core/notebookbackend/inotebookbackendfactory.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef INOTEBOOKBACKENDFACTORY_H
|
||||
#define INOTEBOOKBACKENDFACTORY_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class INotebookBackend;
|
||||
|
||||
class INotebookBackendFactory
|
||||
{
|
||||
public:
|
||||
INotebookBackendFactory()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~INotebookBackendFactory()
|
||||
{
|
||||
}
|
||||
|
||||
virtual QString getName() const = 0;
|
||||
|
||||
virtual QString getDisplayName() const = 0;
|
||||
|
||||
virtual QString getDescription() const = 0;
|
||||
|
||||
virtual QSharedPointer<INotebookBackend> createNotebookBackend(const QString &p_rootPath) = 0;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // INOTEBOOKBACKENDFACTORY_H
|
170
src/core/notebookbackend/localnotebookbackend.cpp
Normal file
170
src/core/notebookbackend/localnotebookbackend.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
#include "localnotebookbackend.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <utils/pathutils.h>
|
||||
#include "exception.h"
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
LocalNotebookBackend::LocalNotebookBackend(const QString &p_name,
|
||||
const QString &p_displayName,
|
||||
const QString &p_description,
|
||||
const QString &p_rootPath,
|
||||
QObject *p_parent)
|
||||
: INotebookBackend(p_rootPath, p_parent),
|
||||
m_info(p_name, p_displayName, p_description)
|
||||
{
|
||||
}
|
||||
|
||||
QString LocalNotebookBackend::getName() const
|
||||
{
|
||||
return m_info.m_name;
|
||||
}
|
||||
|
||||
QString LocalNotebookBackend::getDisplayName() const
|
||||
{
|
||||
return m_info.m_displayName;
|
||||
}
|
||||
|
||||
QString LocalNotebookBackend::getDescription() const
|
||||
{
|
||||
return m_info.m_description;
|
||||
}
|
||||
|
||||
bool LocalNotebookBackend::isEmptyDir(const QString &p_dirPath) const
|
||||
{
|
||||
return PathUtils::isEmptyDir(getFullPath(p_dirPath));
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::makePath(const QString &p_dirPath)
|
||||
{
|
||||
constrainPath(p_dirPath);
|
||||
QDir dir(getRootPath());
|
||||
if (!dir.mkpath(p_dirPath)) {
|
||||
Exception::throwOne(Exception::Type::FailToCreateDir,
|
||||
QString("fail to create directory: %1").arg(p_dirPath));
|
||||
}
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::writeFile(const QString &p_filePath, const QByteArray &p_data)
|
||||
{
|
||||
const auto filePath = getFullPath(p_filePath);
|
||||
FileUtils::writeFile(filePath, p_data);
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::writeFile(const QString &p_filePath, const QString &p_text)
|
||||
{
|
||||
const auto filePath = getFullPath(p_filePath);
|
||||
FileUtils::writeFile(filePath, p_text);
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::writeFile(const QString &p_filePath, const QJsonObject &p_jobj)
|
||||
{
|
||||
writeFile(p_filePath, QJsonDocument(p_jobj).toJson());
|
||||
}
|
||||
|
||||
QString LocalNotebookBackend::readTextFile(const QString &p_filePath)
|
||||
{
|
||||
const auto filePath = getFullPath(p_filePath);
|
||||
return FileUtils::readTextFile(filePath);
|
||||
}
|
||||
|
||||
QByteArray LocalNotebookBackend::readFile(const QString &p_filePath)
|
||||
{
|
||||
const auto filePath = getFullPath(p_filePath);
|
||||
return FileUtils::readFile(filePath);
|
||||
}
|
||||
|
||||
bool LocalNotebookBackend::exists(const QString &p_path) const
|
||||
{
|
||||
return QFileInfo::exists(getFullPath(p_path));
|
||||
}
|
||||
|
||||
bool LocalNotebookBackend::childExistsCaseInsensitive(const QString &p_dirPath, const QString &p_name) const
|
||||
{
|
||||
return FileUtils::childExistsCaseInsensitive(getFullPath(p_dirPath), p_name);
|
||||
}
|
||||
|
||||
bool LocalNotebookBackend::isFile(const QString &p_path) const
|
||||
{
|
||||
QFileInfo fi(getFullPath(p_path));
|
||||
return fi.isFile();
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::renameFile(const QString &p_filePath, const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(isFile(p_filePath));
|
||||
const auto filePath = getFullPath(p_filePath);
|
||||
FileUtils::renameFile(filePath, p_name);
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::renameDir(const QString &p_dirPath, const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(!isFile(p_dirPath));
|
||||
const auto dirPath = getFullPath(p_dirPath);
|
||||
FileUtils::renameFile(dirPath, p_name);
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::copyFile(const QString &p_filePath, const QString &p_destPath)
|
||||
{
|
||||
auto filePath = p_filePath;
|
||||
if (QFileInfo(filePath).isRelative()) {
|
||||
filePath = getFullPath(filePath);
|
||||
}
|
||||
|
||||
Q_ASSERT(QFileInfo(filePath).isFile());
|
||||
|
||||
FileUtils::copyFile(filePath, getFullPath(p_destPath));
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::copyDir(const QString &p_dirPath, const QString &p_destPath)
|
||||
{
|
||||
auto dirPath = p_dirPath;
|
||||
if (QFileInfo(dirPath).isRelative()) {
|
||||
dirPath = getFullPath(dirPath);
|
||||
}
|
||||
|
||||
Q_ASSERT(QFileInfo(dirPath).isDir());
|
||||
|
||||
FileUtils::copyDir(dirPath, getFullPath(p_destPath));
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::removeFile(const QString &p_filePath)
|
||||
{
|
||||
Q_ASSERT(isFile(p_filePath));
|
||||
FileUtils::removeFile(getFullPath(p_filePath));
|
||||
}
|
||||
|
||||
bool LocalNotebookBackend::removeDirIfEmpty(const QString &p_dirPath)
|
||||
{
|
||||
Q_ASSERT(!isFile(p_dirPath));
|
||||
return FileUtils::removeDirIfEmpty(getFullPath(p_dirPath));
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::removeDir(const QString &p_dirPath)
|
||||
{
|
||||
Q_ASSERT(!isFile(p_dirPath));
|
||||
return FileUtils::removeDir(getFullPath(p_dirPath));
|
||||
}
|
||||
|
||||
QString LocalNotebookBackend::renameIfExistsCaseInsensitive(const QString &p_path) const
|
||||
{
|
||||
return FileUtils::renameIfExistsCaseInsensitive(getFullPath(p_path));
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::addFile(const QString &p_path)
|
||||
{
|
||||
Q_UNUSED(p_path);
|
||||
// Do nothing for now.
|
||||
}
|
||||
|
||||
void LocalNotebookBackend::removeEmptyDir(const QString &p_dirPath)
|
||||
{
|
||||
FileUtils::removeEmptyDir(getFullPath(p_dirPath));
|
||||
}
|
84
src/core/notebookbackend/localnotebookbackend.h
Normal file
84
src/core/notebookbackend/localnotebookbackend.h
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef LOCALNOTEBOOKBACKEND_H
|
||||
#define LOCALNOTEBOOKBACKEND_H
|
||||
|
||||
#include "inotebookbackend.h"
|
||||
|
||||
#include "../global.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
// Backend to access local file system.
|
||||
class LocalNotebookBackend : public INotebookBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LocalNotebookBackend(const QString &p_name,
|
||||
const QString &p_displayName,
|
||||
const QString &p_description,
|
||||
const QString &p_rootPath,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
QString getName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getDisplayName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getDescription() const Q_DECL_OVERRIDE;
|
||||
|
||||
// Whether @p_dirPath is an empty directory.
|
||||
bool isEmptyDir(const QString &p_dirPath) const Q_DECL_OVERRIDE;
|
||||
|
||||
// Create the directory path @p_dirPath. Create all parent directories if necessary.
|
||||
void makePath(const QString &p_dirPath) Q_DECL_OVERRIDE;
|
||||
|
||||
// Write @p_data to @p_filePath.
|
||||
void writeFile(const QString &p_filePath, const QByteArray &p_data) Q_DECL_OVERRIDE;
|
||||
|
||||
// Write @p_text to @p_filePath.
|
||||
void writeFile(const QString &p_filePath, const QString &p_text) Q_DECL_OVERRIDE;
|
||||
|
||||
// Write @p_jobj to @p_filePath.
|
||||
void writeFile(const QString &p_filePath, const QJsonObject &p_jobj) Q_DECL_OVERRIDE;
|
||||
|
||||
// Read content from @p_filePath.
|
||||
QString readTextFile(const QString &p_filePath) Q_DECL_OVERRIDE;
|
||||
|
||||
// Read file @p_filePath.
|
||||
QByteArray readFile(const QString &p_filePath) Q_DECL_OVERRIDE;
|
||||
|
||||
bool exists(const QString &p_path) const Q_DECL_OVERRIDE;
|
||||
|
||||
bool childExistsCaseInsensitive(const QString &p_dirPath, const QString &p_name) const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isFile(const QString &p_path) const Q_DECL_OVERRIDE;
|
||||
|
||||
void renameFile(const QString &p_filePath, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
void renameDir(const QString &p_dirPath, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
// Delete @p_filePath from disk.
|
||||
void removeFile(const QString &p_filePath) Q_DECL_OVERRIDE;
|
||||
|
||||
// Delete @p_dirPath from disk if it is empty.
|
||||
bool removeDirIfEmpty(const QString &p_dirPath) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeDir(const QString &p_dirPath) Q_DECL_OVERRIDE;
|
||||
|
||||
// Copy @p_filePath to @p_destPath.
|
||||
// @p_filePath may beyond this notebook backend.
|
||||
void copyFile(const QString &p_filePath, const QString &p_destPath) Q_DECL_OVERRIDE;
|
||||
|
||||
// Copy @p_dirPath to as @p_destPath.
|
||||
void copyDir(const QString &p_dirPath, const QString &p_destPath) Q_DECL_OVERRIDE;
|
||||
|
||||
QString renameIfExistsCaseInsensitive(const QString &p_path) const Q_DECL_OVERRIDE;
|
||||
|
||||
void addFile(const QString &p_path) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeEmptyDir(const QString &p_dirPath) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
Info m_info;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // LOCALNOTEBOOKBACKEND_H
|
34
src/core/notebookbackend/localnotebookbackendfactory.cpp
Normal file
34
src/core/notebookbackend/localnotebookbackendfactory.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "localnotebookbackendfactory.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "localnotebookbackend.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
LocalNotebookBackendFactory::LocalNotebookBackendFactory()
|
||||
{
|
||||
}
|
||||
|
||||
QString LocalNotebookBackendFactory::getName() const
|
||||
{
|
||||
return QStringLiteral("local.vnotex");
|
||||
}
|
||||
|
||||
QString LocalNotebookBackendFactory::getDisplayName() const
|
||||
{
|
||||
return QObject::tr("Local Notebook Backend");
|
||||
}
|
||||
|
||||
QString LocalNotebookBackendFactory::getDescription() const
|
||||
{
|
||||
return QObject::tr("Local file system");
|
||||
}
|
||||
|
||||
QSharedPointer<INotebookBackend> LocalNotebookBackendFactory::createNotebookBackend(const QString &p_rootPath)
|
||||
{
|
||||
return QSharedPointer<LocalNotebookBackend>::create(getName(),
|
||||
getDisplayName(),
|
||||
getDescription(),
|
||||
p_rootPath);
|
||||
}
|
24
src/core/notebookbackend/localnotebookbackendfactory.h
Normal file
24
src/core/notebookbackend/localnotebookbackendfactory.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef LOCALNOTEBOOKBACKENDFACTORY_H
|
||||
#define LOCALNOTEBOOKBACKENDFACTORY_H
|
||||
|
||||
#include "inotebookbackendfactory.h"
|
||||
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class LocalNotebookBackendFactory : public INotebookBackendFactory
|
||||
{
|
||||
public:
|
||||
LocalNotebookBackendFactory();
|
||||
|
||||
QString getName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getDisplayName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getDescription()const Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<INotebookBackend> createNotebookBackend(const QString &p_rootPath) Q_DECL_OVERRIDE;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // LOCALNOTEBOOKBACKENDFACTORY_H
|
10
src/core/notebookbackend/notebookbackend.pri
Normal file
10
src/core/notebookbackend/notebookbackend.pri
Normal file
@ -0,0 +1,10 @@
|
||||
SOURCES += \
|
||||
$$PWD/localnotebookbackend.cpp \
|
||||
$$PWD/localnotebookbackendfactory.cpp \
|
||||
$$PWD/inotebookbackend.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/inotebookbackend.h \
|
||||
$$PWD/localnotebookbackend.h \
|
||||
$$PWD/inotebookbackendfactory.h \
|
||||
$$PWD/localnotebookbackendfactory.h
|
96
src/core/notebookconfigmgr/bundlenotebookconfigmgr.cpp
Normal file
96
src/core/notebookconfigmgr/bundlenotebookconfigmgr.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "bundlenotebookconfigmgr.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
#include <notebook/notebookparameters.h>
|
||||
#include <notebook/bundlenotebook.h>
|
||||
#include "notebookconfig.h"
|
||||
#include <utils/pathutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const QString BundleNotebookConfigMgr::c_configFolderName = "vx_notebook";
|
||||
|
||||
const QString BundleNotebookConfigMgr::c_configName = "vx_notebook.json";
|
||||
|
||||
BundleNotebookConfigMgr::BundleNotebookConfigMgr(const QSharedPointer<INotebookBackend> &p_backend,
|
||||
QObject *p_parent)
|
||||
: INotebookConfigMgr(p_backend, p_parent)
|
||||
{
|
||||
}
|
||||
|
||||
void BundleNotebookConfigMgr::createEmptySkeleton(const NotebookParameters &p_paras)
|
||||
{
|
||||
getBackend()->makePath(BundleNotebookConfigMgr::c_configFolderName);
|
||||
|
||||
auto config = NotebookConfig::fromNotebookParameters(getCodeVersion(), p_paras);
|
||||
writeNotebookConfig(*config);
|
||||
}
|
||||
|
||||
QSharedPointer<NotebookConfig> BundleNotebookConfigMgr::readNotebookConfig() const
|
||||
{
|
||||
return readNotebookConfig(getBackend());
|
||||
}
|
||||
|
||||
void BundleNotebookConfigMgr::writeNotebookConfig()
|
||||
{
|
||||
auto config = NotebookConfig::fromNotebook(getCodeVersion(), getNotebook());
|
||||
writeNotebookConfig(*config);
|
||||
}
|
||||
|
||||
void BundleNotebookConfigMgr::writeNotebookConfig(const NotebookConfig &p_config)
|
||||
{
|
||||
getBackend()->writeFile(getConfigFilePath(), p_config.toJson());
|
||||
}
|
||||
|
||||
void BundleNotebookConfigMgr::removeNotebookConfig()
|
||||
{
|
||||
getBackend()->removeDir(getConfigFolderName());
|
||||
}
|
||||
|
||||
QSharedPointer<NotebookConfig> BundleNotebookConfigMgr::readNotebookConfig(
|
||||
const QSharedPointer<INotebookBackend> &p_backend)
|
||||
{
|
||||
auto data = p_backend->readFile(getConfigFilePath());
|
||||
|
||||
auto config = QSharedPointer<NotebookConfig>::create();
|
||||
config->fromJson(QJsonDocument::fromJson(data).object());
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
const QString &BundleNotebookConfigMgr::getConfigFolderName()
|
||||
{
|
||||
return c_configFolderName;
|
||||
}
|
||||
|
||||
const QString &BundleNotebookConfigMgr::getConfigName()
|
||||
{
|
||||
return c_configName;
|
||||
}
|
||||
|
||||
QString BundleNotebookConfigMgr::getConfigFilePath()
|
||||
{
|
||||
return PathUtils::concatenateFilePath(c_configFolderName, c_configName);
|
||||
}
|
||||
|
||||
BundleNotebook *BundleNotebookConfigMgr::getBundleNotebook() const
|
||||
{
|
||||
return dynamic_cast<BundleNotebook *>(getNotebook());
|
||||
}
|
||||
|
||||
bool BundleNotebookConfigMgr::isBuiltInFile(const Node *p_node, const QString &p_name) const
|
||||
{
|
||||
Q_UNUSED(p_node);
|
||||
Q_UNUSED(p_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BundleNotebookConfigMgr::isBuiltInFolder(const Node *p_node, const QString &p_name) const
|
||||
{
|
||||
if (p_node->isRoot()) {
|
||||
return p_name.toLower() == c_configFolderName;
|
||||
}
|
||||
return false;
|
||||
}
|
54
src/core/notebookconfigmgr/bundlenotebookconfigmgr.h
Normal file
54
src/core/notebookconfigmgr/bundlenotebookconfigmgr.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef BUNDLENOTEBOOKCONFIGMGR_H
|
||||
#define BUNDLENOTEBOOKCONFIGMGR_H
|
||||
|
||||
#include "inotebookconfigmgr.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class BundleNotebook;
|
||||
|
||||
class BundleNotebookConfigMgr : public INotebookConfigMgr
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BundleNotebookConfigMgr(const QSharedPointer<INotebookBackend> &p_backend,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
// Create an empty skeleton for an empty notebook.
|
||||
virtual void createEmptySkeleton(const NotebookParameters &p_paras) Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<NotebookConfig> readNotebookConfig() const;
|
||||
void writeNotebookConfig();
|
||||
|
||||
void removeNotebookConfig();
|
||||
|
||||
bool isBuiltInFile(const Node *p_node, const QString &p_name) const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isBuiltInFolder(const Node *p_node, const QString &p_name) const Q_DECL_OVERRIDE;
|
||||
|
||||
static const QString &getConfigFolderName();
|
||||
|
||||
static const QString &getConfigName();
|
||||
|
||||
static QString getConfigFilePath();
|
||||
|
||||
static QSharedPointer<NotebookConfig> readNotebookConfig(const QSharedPointer<INotebookBackend> &p_backend);
|
||||
|
||||
enum { RootNodeId = 1 };
|
||||
|
||||
protected:
|
||||
BundleNotebook *getBundleNotebook() const;
|
||||
|
||||
private:
|
||||
void writeNotebookConfig(const NotebookConfig &p_config);
|
||||
|
||||
// Folder name to store the notebook's config.
|
||||
// This folder locates in the root folder of the notebook.
|
||||
static const QString c_configFolderName;
|
||||
|
||||
// Name of the notebook's config file.
|
||||
static const QString c_configName;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // BUNDLENOTEBOOKCONFIGMGR_H
|
37
src/core/notebookconfigmgr/inotebookconfigmgr.cpp
Normal file
37
src/core/notebookconfigmgr/inotebookconfigmgr.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "inotebookconfigmgr.h"
|
||||
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
INotebookConfigMgr::INotebookConfigMgr(const QSharedPointer<INotebookBackend> &p_backend,
|
||||
QObject *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_backend(p_backend)
|
||||
{
|
||||
}
|
||||
|
||||
INotebookConfigMgr::~INotebookConfigMgr()
|
||||
{
|
||||
}
|
||||
|
||||
const QSharedPointer<INotebookBackend> &INotebookConfigMgr::getBackend() const
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
QString INotebookConfigMgr::getCodeVersion() const
|
||||
{
|
||||
const QString version("1");
|
||||
return version;
|
||||
}
|
||||
|
||||
Notebook *INotebookConfigMgr::getNotebook() const
|
||||
{
|
||||
return m_notebook;
|
||||
}
|
||||
|
||||
void INotebookConfigMgr::setNotebook(Notebook *p_notebook)
|
||||
{
|
||||
m_notebook = p_notebook;
|
||||
}
|
96
src/core/notebookconfigmgr/inotebookconfigmgr.h
Normal file
96
src/core/notebookconfigmgr/inotebookconfigmgr.h
Normal file
@ -0,0 +1,96 @@
|
||||
#ifndef INOTEBOOKCONFIGMGR_H
|
||||
#define INOTEBOOKCONFIGMGR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "notebook/node.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class NotebookConfig;
|
||||
class INotebookBackend;
|
||||
class NotebookParameters;
|
||||
class Notebook;
|
||||
struct NodeParameters;
|
||||
|
||||
// Abstract class for notebook config manager, which is responsible for config
|
||||
// files access and note nodes access.
|
||||
class INotebookConfigMgr : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
INotebookConfigMgr(const QSharedPointer<INotebookBackend> &p_backend,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
virtual ~INotebookConfigMgr();
|
||||
|
||||
virtual QString getName() const = 0;
|
||||
|
||||
virtual QString getDisplayName() const = 0;
|
||||
|
||||
virtual QString getDescription() const = 0;
|
||||
|
||||
// Create an empty skeleton for an empty notebook.
|
||||
virtual void createEmptySkeleton(const NotebookParameters &p_paras) = 0;
|
||||
|
||||
const QSharedPointer<INotebookBackend> &getBackend() const;
|
||||
|
||||
virtual QSharedPointer<Node> loadRootNode() const = 0;
|
||||
|
||||
virtual void loadNode(Node *p_node) const = 0;
|
||||
virtual void saveNode(const Node *p_node) = 0;
|
||||
|
||||
virtual void renameNode(Node *p_node, const QString &p_name) = 0;
|
||||
|
||||
virtual QSharedPointer<Node> newNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_name) = 0;
|
||||
|
||||
virtual QSharedPointer<Node> addAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_name,
|
||||
const NodeParameters &p_paras) = 0;
|
||||
|
||||
virtual QSharedPointer<Node> copyAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_path) = 0;
|
||||
|
||||
Notebook *getNotebook() const;
|
||||
void setNotebook(Notebook *p_notebook);
|
||||
|
||||
virtual QSharedPointer<Node> loadNodeByPath(const QSharedPointer<Node> &p_root,
|
||||
const QString &p_relativePath) = 0;
|
||||
|
||||
virtual QSharedPointer<Node> copyNodeAsChildOf(const QSharedPointer<Node> &p_src,
|
||||
Node *p_dest,
|
||||
bool p_move) = 0;
|
||||
|
||||
virtual void removeNode(const QSharedPointer<Node> &p_node, bool p_force, bool p_configOnly) = 0;
|
||||
|
||||
virtual bool nodeExistsOnDisk(const Node *p_node) const = 0;
|
||||
|
||||
virtual QString readNode(const Node *p_node) const = 0;
|
||||
virtual void writeNode(Node *p_node, const QString &p_content) = 0;
|
||||
|
||||
virtual QString fetchNodeImageFolderPath(Node *p_node) = 0;
|
||||
|
||||
virtual QString fetchNodeAttachmentFolderPath(Node *p_node) = 0;
|
||||
|
||||
// Whether @p_name is a built-in file under @p_node.
|
||||
virtual bool isBuiltInFile(const Node *p_node, const QString &p_name) const = 0;
|
||||
|
||||
virtual bool isBuiltInFolder(const Node *p_node, const QString &p_name) const = 0;
|
||||
|
||||
protected:
|
||||
// Version of the config processing code.
|
||||
virtual QString getCodeVersion() const;
|
||||
|
||||
private:
|
||||
QSharedPointer<INotebookBackend> m_backend;
|
||||
|
||||
Notebook *m_notebook = nullptr;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // INOTEBOOKCONFIGMGR_H
|
33
src/core/notebookconfigmgr/inotebookconfigmgrfactory.h
Normal file
33
src/core/notebookconfigmgr/inotebookconfigmgrfactory.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef INOTEBOOKCONFIGMGRFACTORY_H
|
||||
#define INOTEBOOKCONFIGMGRFACTORY_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class INotebookConfigMgr;
|
||||
class INotebookBackend;
|
||||
|
||||
class INotebookConfigMgrFactory
|
||||
{
|
||||
public:
|
||||
INotebookConfigMgrFactory()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~INotebookConfigMgrFactory()
|
||||
{
|
||||
}
|
||||
|
||||
virtual QString getName() const = 0;
|
||||
|
||||
virtual QString getDisplayName() const = 0;
|
||||
|
||||
virtual QString getDescription() const = 0;
|
||||
|
||||
virtual QSharedPointer<INotebookConfigMgr> createNotebookConfigMgr(
|
||||
const QSharedPointer<INotebookBackend> &p_backend) = 0;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // INOTEBOOKCONFIGMGRFACTORY_H
|
177
src/core/notebookconfigmgr/nodecontentmediautils.cpp
Normal file
177
src/core/notebookconfigmgr/nodecontentmediautils.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
#include "nodecontentmediautils.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSet>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
#include <notebook/node.h>
|
||||
|
||||
#include <buffer/filetypehelper.h>
|
||||
|
||||
#include <vtextedit/markdownutils.h>
|
||||
|
||||
#include <utils/pathutils.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
void NodeContentMediaUtils::copyMediaFiles(const Node *p_node,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath)
|
||||
{
|
||||
Q_ASSERT(p_node->getType() == Node::Type::File);
|
||||
auto fileType = FileTypeHelper::fileType(p_node->fetchAbsolutePath());
|
||||
if (fileType == QStringLiteral("markdown")) {
|
||||
copyMarkdownMediaFiles(p_node->read(),
|
||||
PathUtils::parentDirPath(p_node->fetchContentPath()),
|
||||
p_backend,
|
||||
p_destFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeContentMediaUtils::copyMediaFiles(const QString &p_filePath,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath)
|
||||
{
|
||||
auto fileType = FileTypeHelper::fileType(p_filePath);
|
||||
if (fileType == QStringLiteral("markdown")) {
|
||||
copyMarkdownMediaFiles(FileUtils::readTextFile(p_filePath),
|
||||
PathUtils::parentDirPath(p_filePath),
|
||||
p_backend,
|
||||
p_destFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeContentMediaUtils::copyMarkdownMediaFiles(const QString &p_content,
|
||||
const QString &p_basePath,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath)
|
||||
{
|
||||
auto content = p_content;
|
||||
|
||||
// Images.
|
||||
const auto images =
|
||||
vte::MarkdownUtils::fetchImagesFromMarkdownText(content,
|
||||
p_basePath,
|
||||
vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||
|
||||
QDir destDir(PathUtils::parentDirPath(p_destFilePath));
|
||||
QSet<QString> handledImages;
|
||||
QHash<QString, QString> renamedImages;
|
||||
int lastPos = content.size();
|
||||
for (const auto &link : images) {
|
||||
Q_ASSERT(link.m_urlInLinkPos < lastPos);
|
||||
lastPos = link.m_urlInLinkPos;
|
||||
|
||||
if (handledImages.contains(link.m_path)) {
|
||||
auto it = renamedImages.find(link.m_path);
|
||||
if (it != renamedImages.end()) {
|
||||
content.replace(link.m_urlInLinkPos, link.m_urlInLink.size(), it.value());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
handledImages.insert(link.m_path);
|
||||
|
||||
if (!QFileInfo::exists(link.m_path)) {
|
||||
qWarning() << "Image of Markdown file does not exist" << link.m_path << link.m_urlInLink;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the relative path of the image and apply it to the dest file path.
|
||||
const auto oldDestFilePath = destDir.filePath(link.m_urlInLink);
|
||||
destDir.mkpath(PathUtils::parentDirPath(oldDestFilePath));
|
||||
auto destFilePath = p_backend->renameIfExistsCaseInsensitive(oldDestFilePath);
|
||||
if (oldDestFilePath != destFilePath) {
|
||||
// Rename happens.
|
||||
const auto oldFileName = PathUtils::fileName(oldDestFilePath);
|
||||
const auto newFileName = PathUtils::fileName(destFilePath);
|
||||
qWarning() << QString("Image name conflicts when copy. Renamed from (%1) to (%2)").arg(oldFileName, newFileName);
|
||||
|
||||
// Update the text content.
|
||||
auto newUrlInLink(link.m_urlInLink);
|
||||
newUrlInLink.replace(newUrlInLink.size() - oldFileName.size(),
|
||||
oldFileName.size(),
|
||||
newFileName);
|
||||
|
||||
content.replace(link.m_urlInLinkPos, link.m_urlInLink.size(), newUrlInLink);
|
||||
renamedImages.insert(link.m_path, newUrlInLink);
|
||||
}
|
||||
|
||||
p_backend->copyFile(link.m_path, destFilePath);
|
||||
}
|
||||
|
||||
if (!renamedImages.isEmpty()) {
|
||||
p_backend->writeFile(p_destFilePath, content);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeContentMediaUtils::removeMediaFiles(const Node *p_node)
|
||||
{
|
||||
Q_ASSERT(p_node->getType() == Node::Type::File);
|
||||
auto fileType = FileTypeHelper::fileType(p_node->fetchAbsolutePath());
|
||||
if (fileType == QStringLiteral("markdown")) {
|
||||
removeMarkdownMediaFiles(p_node);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeContentMediaUtils::removeMarkdownMediaFiles(const Node *p_node)
|
||||
{
|
||||
auto content = p_node->read();
|
||||
|
||||
// Images.
|
||||
const auto images =
|
||||
vte::MarkdownUtils::fetchImagesFromMarkdownText(content,
|
||||
PathUtils::parentDirPath(p_node->fetchContentPath()),
|
||||
vte::MarkdownLink::TypeFlag::LocalRelativeInternal);
|
||||
|
||||
auto backend = p_node->getBackend();
|
||||
QSet<QString> handledImages;
|
||||
for (const auto &link : images) {
|
||||
if (handledImages.contains(link.m_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handledImages.insert(link.m_path);
|
||||
|
||||
if (!QFileInfo::exists(link.m_path)) {
|
||||
qWarning() << "Image of Markdown file does not exist" << link.m_path << link.m_urlInLink;
|
||||
continue;
|
||||
}
|
||||
backend->removeFile(link.m_path);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeContentMediaUtils::copyAttachment(Node *p_node,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath,
|
||||
const QString &p_destAttachmentFolderPath)
|
||||
{
|
||||
Q_ASSERT(p_node->getType() == Node::Type::File);
|
||||
Q_ASSERT(!p_node->getAttachmentFolder().isEmpty());
|
||||
|
||||
// Copy the whole folder.
|
||||
const auto srcAttachmentFolderPath = p_node->fetchAttachmentFolderPath();
|
||||
p_backend->copyDir(srcAttachmentFolderPath, p_destAttachmentFolderPath);
|
||||
|
||||
// Check if we need to modify links in content.
|
||||
if (p_node->getAttachmentFolder() == PathUtils::dirName(p_destAttachmentFolderPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto fileType = FileTypeHelper::fileType(p_node->fetchAbsolutePath());
|
||||
if (fileType == QStringLiteral("markdown")) {
|
||||
fixMarkdownLinks(srcAttachmentFolderPath, p_backend, p_destFilePath, p_destAttachmentFolderPath);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeContentMediaUtils::fixMarkdownLinks(const QString &p_srcFolderPath,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath,
|
||||
const QString &p_destFolderPath)
|
||||
{
|
||||
// TODO.
|
||||
}
|
52
src/core/notebookconfigmgr/nodecontentmediautils.h
Normal file
52
src/core/notebookconfigmgr/nodecontentmediautils.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef NODECONTENTMEDIAUTILS_H
|
||||
#define NODECONTENTMEDIAUTILS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class INotebookBackend;
|
||||
class Node;
|
||||
|
||||
// Utils to operate on the media files from node's content.
|
||||
class NodeContentMediaUtils
|
||||
{
|
||||
public:
|
||||
NodeContentMediaUtils() = delete;
|
||||
|
||||
// Fetch media files from @p_node and copy them to dest folder.
|
||||
// @p_destFilePath: @p_node has been copied to @p_destFilePath.
|
||||
static void copyMediaFiles(const Node *p_node,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath);
|
||||
|
||||
// @p_filePath: the file path to read the content for parse.
|
||||
static void copyMediaFiles(const QString &p_filePath,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath);
|
||||
|
||||
static void removeMediaFiles(const Node *p_node);
|
||||
|
||||
// Copy attachment folder.
|
||||
static void copyAttachment(Node *p_node,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath,
|
||||
const QString &p_destAttachmentFolderPath);
|
||||
|
||||
private:
|
||||
static void copyMarkdownMediaFiles(const QString &p_content,
|
||||
const QString &p_basePath,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath);
|
||||
|
||||
static void removeMarkdownMediaFiles(const Node *p_node);
|
||||
|
||||
// Fix local relative internal links locating in @p_srcFolderPath.
|
||||
static void fixMarkdownLinks(const QString &p_srcFolderPath,
|
||||
INotebookBackend *p_backend,
|
||||
const QString &p_destFilePath,
|
||||
const QString &p_destFolderPath);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NODECONTENTMEDIAUTILS_H
|
111
src/core/notebookconfigmgr/notebookconfig.cpp
Normal file
111
src/core/notebookconfigmgr/notebookconfig.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#include "notebookconfig.h"
|
||||
|
||||
#include <notebook/notebookparameters.h>
|
||||
#include <notebook/notebook.h>
|
||||
#include <versioncontroller/iversioncontroller.h>
|
||||
#include <utils/utils.h>
|
||||
#include "exception.h"
|
||||
#include "global.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const QString NotebookConfig::c_version = "version";
|
||||
|
||||
const QString NotebookConfig::c_name = "name";
|
||||
|
||||
const QString NotebookConfig::c_description = "description";
|
||||
|
||||
const QString NotebookConfig::c_imageFolder = "image_folder";
|
||||
|
||||
const QString NotebookConfig::c_attachmentFolder = "attachment_folder";
|
||||
|
||||
const QString NotebookConfig::c_createdTimeUtc = "created_time";
|
||||
|
||||
const QString NotebookConfig::c_versionController = "version_controller";
|
||||
|
||||
const QString NotebookConfig::c_configMgr = "config_mgr";
|
||||
|
||||
const QString NotebookConfig::c_nextNodeId = "next_node_id";
|
||||
|
||||
QSharedPointer<NotebookConfig> NotebookConfig::fromNotebookParameters(const QString &p_version,
|
||||
const NotebookParameters &p_paras)
|
||||
{
|
||||
auto config = QSharedPointer<NotebookConfig>::create();
|
||||
|
||||
config->m_version = p_version;
|
||||
config->m_name = p_paras.m_name;
|
||||
config->m_description = p_paras.m_description;
|
||||
config->m_imageFolder = p_paras.m_imageFolder;
|
||||
config->m_attachmentFolder = p_paras.m_attachmentFolder;
|
||||
config->m_createdTimeUtc = p_paras.m_createdTimeUtc;
|
||||
config->m_versionController = p_paras.m_versionController->getName();
|
||||
config->m_notebookConfigMgr = p_paras.m_notebookConfigMgr->getName();
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
QJsonObject NotebookConfig::toJson() const
|
||||
{
|
||||
QJsonObject jobj;
|
||||
|
||||
jobj[NotebookConfig::c_version] = m_version;
|
||||
jobj[NotebookConfig::c_name] = m_name;
|
||||
jobj[NotebookConfig::c_description] = m_description;
|
||||
jobj[NotebookConfig::c_imageFolder] = m_imageFolder;
|
||||
jobj[NotebookConfig::c_attachmentFolder] = m_attachmentFolder;
|
||||
jobj[NotebookConfig::c_createdTimeUtc] = Utils::dateTimeStringUniform(m_createdTimeUtc);
|
||||
jobj[NotebookConfig::c_versionController] = m_versionController;
|
||||
jobj[NotebookConfig::c_configMgr] = m_notebookConfigMgr;
|
||||
jobj[NotebookConfig::c_nextNodeId] = QString::number(m_nextNodeId);
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
void NotebookConfig::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
if (!p_jobj.contains(NotebookConfig::c_version)
|
||||
|| !p_jobj.contains(NotebookConfig::c_name)
|
||||
|| !p_jobj.contains(NotebookConfig::c_createdTimeUtc)
|
||||
|| !p_jobj.contains(NotebookConfig::c_versionController)
|
||||
|| !p_jobj.contains(NotebookConfig::c_configMgr)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to read notebook configuration from JSON (%1)").arg(QJsonObjectToString(p_jobj)));
|
||||
return;
|
||||
}
|
||||
|
||||
m_version = p_jobj[NotebookConfig::c_version].toString();
|
||||
m_name = p_jobj[NotebookConfig::c_name].toString();
|
||||
m_description = p_jobj[NotebookConfig::c_description].toString();
|
||||
m_imageFolder = p_jobj[NotebookConfig::c_imageFolder].toString();
|
||||
m_attachmentFolder = p_jobj[NotebookConfig::c_attachmentFolder].toString();
|
||||
m_createdTimeUtc = Utils::dateTimeFromStringUniform(p_jobj[NotebookConfig::c_createdTimeUtc].toString());
|
||||
m_versionController = p_jobj[NotebookConfig::c_versionController].toString();
|
||||
m_notebookConfigMgr = p_jobj[NotebookConfig::c_configMgr].toString();
|
||||
|
||||
{
|
||||
auto nextNodeIdStr = p_jobj[NotebookConfig::c_nextNodeId].toString();
|
||||
bool ok;
|
||||
m_nextNodeId = nextNodeIdStr.toULongLong(&ok);
|
||||
if (!ok) {
|
||||
m_nextNodeId = BundleNotebookConfigMgr::RootNodeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<NotebookConfig> NotebookConfig::fromNotebook(const QString &p_version,
|
||||
const Notebook *p_notebook)
|
||||
{
|
||||
auto config = QSharedPointer<NotebookConfig>::create();
|
||||
|
||||
config->m_version = p_version;
|
||||
config->m_name = p_notebook->getName();
|
||||
config->m_description = p_notebook->getDescription();
|
||||
config->m_imageFolder = p_notebook->getImageFolder();
|
||||
config->m_attachmentFolder = p_notebook->getAttachmentFolder();
|
||||
config->m_createdTimeUtc = p_notebook->getCreatedTimeUtc();
|
||||
config->m_versionController = p_notebook->getVersionController()->getName();
|
||||
config->m_notebookConfigMgr = p_notebook->getConfigMgr()->getName();
|
||||
config->m_nextNodeId = p_notebook->getNextNodeId();
|
||||
|
||||
return config;
|
||||
}
|
68
src/core/notebookconfigmgr/notebookconfig.h
Normal file
68
src/core/notebookconfigmgr/notebookconfig.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef NOTEBOOKCONFIG_H
|
||||
#define NOTEBOOKCONFIG_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "bundlenotebookconfigmgr.h"
|
||||
#include "global.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class NotebookParameters;
|
||||
|
||||
class NotebookConfig
|
||||
{
|
||||
public:
|
||||
virtual ~NotebookConfig() {}
|
||||
|
||||
static QSharedPointer<NotebookConfig> fromNotebookParameters(const QString &p_version,
|
||||
const NotebookParameters &p_paras);
|
||||
|
||||
static QSharedPointer<NotebookConfig> fromNotebook(const QString &p_version,
|
||||
const Notebook *p_notebook);
|
||||
|
||||
virtual QJsonObject toJson() const;
|
||||
|
||||
virtual void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
QString m_version;
|
||||
|
||||
QString m_name;
|
||||
|
||||
QString m_description;
|
||||
|
||||
QString m_imageFolder;
|
||||
|
||||
QString m_attachmentFolder;
|
||||
|
||||
QDateTime m_createdTimeUtc;
|
||||
|
||||
QString m_versionController;
|
||||
|
||||
QString m_notebookConfigMgr;
|
||||
|
||||
ID m_nextNodeId = BundleNotebookConfigMgr::RootNodeId + 1;
|
||||
|
||||
static const QString c_version;
|
||||
|
||||
static const QString c_name;
|
||||
|
||||
static const QString c_description;
|
||||
|
||||
static const QString c_imageFolder;
|
||||
|
||||
static const QString c_attachmentFolder;
|
||||
|
||||
static const QString c_createdTimeUtc;
|
||||
|
||||
static const QString c_versionController;
|
||||
|
||||
static const QString c_configMgr;
|
||||
|
||||
static const QString c_nextNodeId;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // NOTEBOOKCONFIG_H
|
16
src/core/notebookconfigmgr/notebookconfigmgr.pri
Normal file
16
src/core/notebookconfigmgr/notebookconfigmgr.pri
Normal file
@ -0,0 +1,16 @@
|
||||
SOURCES += \
|
||||
$$PWD/nodecontentmediautils.cpp \
|
||||
$$PWD/vxnotebookconfigmgr.cpp \
|
||||
$$PWD/vxnotebookconfigmgrfactory.cpp \
|
||||
$$PWD/inotebookconfigmgr.cpp \
|
||||
$$PWD/notebookconfig.cpp \
|
||||
$$PWD/bundlenotebookconfigmgr.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/inotebookconfigmgr.h \
|
||||
$$PWD/nodecontentmediautils.h \
|
||||
$$PWD/vxnotebookconfigmgr.h \
|
||||
$$PWD/inotebookconfigmgrfactory.h \
|
||||
$$PWD/vxnotebookconfigmgrfactory.h \
|
||||
$$PWD/notebookconfig.h \
|
||||
$$PWD/bundlenotebookconfigmgr.h
|
886
src/core/notebookconfigmgr/vxnotebookconfigmgr.cpp
Normal file
886
src/core/notebookconfigmgr/vxnotebookconfigmgr.cpp
Normal file
@ -0,0 +1,886 @@
|
||||
#include "vxnotebookconfigmgr.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QDebug>
|
||||
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
#include <notebook/notebookparameters.h>
|
||||
#include <notebook/filenode.h>
|
||||
#include <notebook/foldernode.h>
|
||||
#include <notebook/bundlenotebook.h>
|
||||
#include <utils/utils.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/pathutils.h>
|
||||
#include <exception.h>
|
||||
|
||||
#include "nodecontentmediautils.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_version = "version";
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_id = "id";
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_createdTimeUtc = "created_time";
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_files = "files";
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_folders = "folders";
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_name = "name";
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_modifiedTimeUtc = "modified_time";
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_attachmentFolder = "attachment_folder";
|
||||
|
||||
const QString VXNotebookConfigMgr::NodeConfig::c_tags = "tags";
|
||||
|
||||
QJsonObject VXNotebookConfigMgr::NodeFileConfig::toJson() const
|
||||
{
|
||||
QJsonObject jobj;
|
||||
|
||||
jobj[NodeConfig::c_name] = m_name;
|
||||
jobj[NodeConfig::c_id] = QString::number(m_id);
|
||||
jobj[NodeConfig::c_createdTimeUtc] = Utils::dateTimeStringUniform(m_createdTimeUtc);
|
||||
jobj[NodeConfig::c_modifiedTimeUtc] = Utils::dateTimeStringUniform(m_modifiedTimeUtc);
|
||||
jobj[NodeConfig::c_attachmentFolder] = m_attachmentFolder;
|
||||
jobj[NodeConfig::c_tags] = QJsonArray::fromStringList(m_tags);
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::NodeFileConfig::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
m_name = p_jobj[NodeConfig::c_name].toString();
|
||||
|
||||
{
|
||||
auto idStr = p_jobj[NodeConfig::c_id].toString();
|
||||
bool ok;
|
||||
m_id = idStr.toULongLong(&ok);
|
||||
if (!ok) {
|
||||
m_id = Node::InvalidId;
|
||||
}
|
||||
}
|
||||
|
||||
m_createdTimeUtc = Utils::dateTimeFromStringUniform(p_jobj[NodeConfig::c_createdTimeUtc].toString());
|
||||
m_modifiedTimeUtc = Utils::dateTimeFromStringUniform(p_jobj[NodeConfig::c_modifiedTimeUtc].toString());
|
||||
|
||||
m_attachmentFolder = p_jobj[NodeConfig::c_attachmentFolder].toString();
|
||||
|
||||
{
|
||||
auto arr = p_jobj[NodeConfig::c_tags].toArray();
|
||||
for (const auto &tag : arr) {
|
||||
m_tags << tag.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject VXNotebookConfigMgr::NodeFolderConfig::toJson() const
|
||||
{
|
||||
QJsonObject jobj;
|
||||
|
||||
jobj[NodeConfig::c_name] = m_name;
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::NodeFolderConfig::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
m_name = p_jobj[NodeConfig::c_name].toString();
|
||||
}
|
||||
|
||||
VXNotebookConfigMgr::NodeConfig::NodeConfig()
|
||||
{
|
||||
}
|
||||
|
||||
VXNotebookConfigMgr::NodeConfig::NodeConfig(const QString &p_version,
|
||||
ID p_id,
|
||||
const QDateTime &p_createdTimeUtc)
|
||||
: m_version(p_version),
|
||||
m_id(p_id),
|
||||
m_createdTimeUtc(p_createdTimeUtc)
|
||||
{
|
||||
}
|
||||
|
||||
QJsonObject VXNotebookConfigMgr::NodeConfig::toJson() const
|
||||
{
|
||||
QJsonObject jobj;
|
||||
|
||||
jobj[NodeConfig::c_version] = m_version;
|
||||
jobj[NodeConfig::c_id] = QString::number(m_id);
|
||||
jobj[NodeConfig::c_createdTimeUtc] = Utils::dateTimeStringUniform(m_createdTimeUtc);
|
||||
|
||||
QJsonArray files;
|
||||
for (const auto &file : m_files) {
|
||||
files.append(file.toJson());
|
||||
}
|
||||
jobj[NodeConfig::c_files] = files;
|
||||
|
||||
QJsonArray folders;
|
||||
for (const auto& folder : m_folders) {
|
||||
folders.append(folder.toJson());
|
||||
}
|
||||
jobj[NodeConfig::c_folders] = folders;
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::NodeConfig::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
m_version = p_jobj[NodeConfig::c_version].toString();
|
||||
|
||||
{
|
||||
auto idStr = p_jobj[NodeConfig::c_id].toString();
|
||||
bool ok;
|
||||
m_id = idStr.toULongLong(&ok);
|
||||
if (!ok) {
|
||||
m_id = Node::InvalidId;
|
||||
}
|
||||
}
|
||||
|
||||
m_createdTimeUtc = Utils::dateTimeFromStringUniform(p_jobj[NodeConfig::c_createdTimeUtc].toString());
|
||||
|
||||
auto filesJson = p_jobj[NodeConfig::c_files].toArray();
|
||||
m_files.resize(filesJson.size());
|
||||
for (int i = 0; i < filesJson.size(); ++i) {
|
||||
m_files[i].fromJson(filesJson[i].toObject());
|
||||
}
|
||||
|
||||
auto foldersJson = p_jobj[NodeConfig::c_folders].toArray();
|
||||
m_folders.resize(foldersJson.size());
|
||||
for (int i = 0; i < foldersJson.size(); ++i) {
|
||||
m_folders[i].fromJson(foldersJson[i].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const QString VXNotebookConfigMgr::c_nodeConfigName = "vx.json";
|
||||
|
||||
const QString VXNotebookConfigMgr::c_recycleBinFolderName = "vx_recycle_bin";
|
||||
|
||||
VXNotebookConfigMgr::VXNotebookConfigMgr(const QString &p_name,
|
||||
const QString &p_displayName,
|
||||
const QString &p_description,
|
||||
const QSharedPointer<INotebookBackend> &p_backend,
|
||||
QObject *p_parent)
|
||||
: BundleNotebookConfigMgr(p_backend, p_parent),
|
||||
m_info(p_name, p_displayName, p_description)
|
||||
{
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgr::getName() const
|
||||
{
|
||||
return m_info.m_name;
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgr::getDisplayName() const
|
||||
{
|
||||
return m_info.m_displayName;
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgr::getDescription() const
|
||||
{
|
||||
return m_info.m_description;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::createEmptySkeleton(const NotebookParameters &p_paras)
|
||||
{
|
||||
BundleNotebookConfigMgr::createEmptySkeleton(p_paras);
|
||||
|
||||
createEmptyRootNode();
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::createEmptyRootNode()
|
||||
{
|
||||
NodeConfig node(getCodeVersion(),
|
||||
BundleNotebookConfigMgr::RootNodeId,
|
||||
QDateTime::currentDateTimeUtc());
|
||||
writeNodeConfig(c_nodeConfigName, node);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::loadRootNode() const
|
||||
{
|
||||
auto nodeConfig = readNodeConfig("");
|
||||
QSharedPointer<Node> root = nodeConfigToNode(*nodeConfig, "", nullptr);
|
||||
Q_ASSERT(root->isLoaded());
|
||||
|
||||
if (!markRecycleBinNode(root)) {
|
||||
const_cast<VXNotebookConfigMgr *>(this)->createRecycleBinNode(root);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
bool VXNotebookConfigMgr::markRecycleBinNode(const QSharedPointer<Node> &p_root) const
|
||||
{
|
||||
auto node = p_root->findChild(c_recycleBinFolderName,
|
||||
FileUtils::isPlatformNameCaseSensitive());
|
||||
if (node) {
|
||||
node->setUse(Node::Use::RecycleBin);
|
||||
markNodeReadOnly(node.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::markNodeReadOnly(Node *p_node) const
|
||||
{
|
||||
auto flags = p_node->getFlags();
|
||||
if (flags & Node::Flag::ReadOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
p_node->setFlags(flags | Node::Flag::ReadOnly);
|
||||
for (auto &child : p_node->getChildren()) {
|
||||
markNodeReadOnly(child.data());
|
||||
}
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::createRecycleBinNode(const QSharedPointer<Node> &p_root)
|
||||
{
|
||||
Q_ASSERT(p_root->isRoot());
|
||||
|
||||
auto node = newNode(p_root.data(), Node::Type::Folder, c_recycleBinFolderName);
|
||||
node->setUse(Node::Use::RecycleBin);
|
||||
markNodeReadOnly(node.data());
|
||||
}
|
||||
|
||||
QSharedPointer<VXNotebookConfigMgr::NodeConfig> VXNotebookConfigMgr::readNodeConfig(const QString &p_path) const
|
||||
{
|
||||
auto backend = getBackend();
|
||||
if (!backend->exists(p_path)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("node path (%1) does not exist").arg(p_path));
|
||||
}
|
||||
|
||||
if (backend->isFile(p_path)) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("node (%1) is a file node without config").arg(p_path));
|
||||
} else {
|
||||
auto configPath = PathUtils::concatenateFilePath(p_path, c_nodeConfigName);
|
||||
auto data = backend->readFile(configPath);
|
||||
auto nodeConfig = QSharedPointer<NodeConfig>::create();
|
||||
nodeConfig->fromJson(QJsonDocument::fromJson(data).object());
|
||||
return nodeConfig;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgr::getNodeConfigFilePath(const Node *p_node) const
|
||||
{
|
||||
Q_ASSERT(p_node->getType() == Node::Type::Folder);
|
||||
return PathUtils::concatenateFilePath(p_node->fetchRelativePath(), c_nodeConfigName);
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::writeNodeConfig(const QString &p_path, const NodeConfig &p_config) const
|
||||
{
|
||||
getBackend()->writeFile(p_path, p_config.toJson());
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::writeNodeConfig(const Node *p_node)
|
||||
{
|
||||
auto config = nodeToNodeConfig(p_node);
|
||||
writeNodeConfig(getNodeConfigFilePath(p_node), *config);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::nodeConfigToNode(const NodeConfig &p_config,
|
||||
const QString &p_name,
|
||||
Node *p_parent) const
|
||||
{
|
||||
auto node = QSharedPointer<FolderNode>::create(p_name, getNotebook(), p_parent);
|
||||
loadFolderNode(node.data(), p_config);
|
||||
return node;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::loadFolderNode(FolderNode *p_node, const NodeConfig &p_config) const
|
||||
{
|
||||
QVector<QSharedPointer<Node>> children;
|
||||
children.reserve(p_config.m_files.size() + p_config.m_folders.size());
|
||||
|
||||
for (const auto &folder : p_config.m_folders) {
|
||||
auto folderNode = QSharedPointer<FolderNode>::create(folder.m_name,
|
||||
getNotebook(),
|
||||
p_node);
|
||||
inheritNodeFlags(p_node, folderNode.data());
|
||||
children.push_back(folderNode);
|
||||
}
|
||||
|
||||
for (const auto &file : p_config.m_files) {
|
||||
auto fileNode = QSharedPointer<FileNode>::create(file.m_id,
|
||||
file.m_name,
|
||||
file.m_createdTimeUtc,
|
||||
file.m_modifiedTimeUtc,
|
||||
file.m_attachmentFolder,
|
||||
file.m_tags,
|
||||
getNotebook(),
|
||||
p_node);
|
||||
inheritNodeFlags(p_node, fileNode.data());
|
||||
children.push_back(fileNode);
|
||||
}
|
||||
|
||||
p_node->loadFolder(p_config.m_id, p_config.m_createdTimeUtc, children);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::newNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(p_parent && p_parent->getType() == Node::Type::Folder);
|
||||
|
||||
QSharedPointer<Node> node;
|
||||
|
||||
switch (p_type) {
|
||||
case Node::Type::File:
|
||||
node = newFileNode(p_parent, p_name, true, NodeParameters());
|
||||
break;
|
||||
|
||||
case Node::Type::Folder:
|
||||
node = newFolderNode(p_parent, p_name, true, NodeParameters());
|
||||
break;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::addAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_name,
|
||||
const NodeParameters &p_paras)
|
||||
{
|
||||
Q_ASSERT(p_parent && p_parent->getType() == Node::Type::Folder);
|
||||
|
||||
QSharedPointer<Node> node;
|
||||
switch (p_type) {
|
||||
case Node::Type::File:
|
||||
node = newFileNode(p_parent, p_name, false, p_paras);
|
||||
break;
|
||||
|
||||
case Node::Type::Folder:
|
||||
node = newFolderNode(p_parent, p_name, false, p_paras);
|
||||
break;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::copyAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_path)
|
||||
{
|
||||
Q_ASSERT(p_parent && p_parent->getType() == Node::Type::Folder);
|
||||
|
||||
QSharedPointer<Node> node;
|
||||
switch (p_type) {
|
||||
case Node::Type::File:
|
||||
node = copyFileAsChildOf(p_path, p_parent);
|
||||
break;
|
||||
|
||||
case Node::Type::Folder:
|
||||
node = copyFolderAsChildOf(p_path, p_parent);
|
||||
break;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::newFileNode(Node *p_parent,
|
||||
const QString &p_name,
|
||||
bool p_create,
|
||||
const NodeParameters &p_paras)
|
||||
{
|
||||
auto notebook = getNotebook();
|
||||
|
||||
// Create file node.
|
||||
auto node = QSharedPointer<FileNode>::create(Node::InvalidId,
|
||||
p_name,
|
||||
p_paras.m_createdTimeUtc,
|
||||
p_paras.m_modifiedTimeUtc,
|
||||
p_paras.m_attachmentFolder,
|
||||
p_paras.m_tags,
|
||||
notebook,
|
||||
p_parent);
|
||||
|
||||
// Write empty file.
|
||||
if (p_create) {
|
||||
getBackend()->writeFile(node->fetchRelativePath(), QString());
|
||||
}
|
||||
|
||||
addChildNode(p_parent, node);
|
||||
writeNodeConfig(p_parent);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::newFolderNode(Node *p_parent,
|
||||
const QString &p_name,
|
||||
bool p_create,
|
||||
const NodeParameters &p_paras)
|
||||
{
|
||||
auto notebook = getNotebook();
|
||||
|
||||
// Create folder node.
|
||||
auto node = QSharedPointer<FolderNode>::create(p_name, notebook, p_parent);
|
||||
node->loadFolder(Node::InvalidId,
|
||||
p_paras.m_createdTimeUtc,
|
||||
QVector<QSharedPointer<Node>>());
|
||||
|
||||
// Make folder.
|
||||
if (p_create) {
|
||||
getBackend()->makePath(node->fetchRelativePath());
|
||||
}
|
||||
|
||||
writeNodeConfig(node.data());
|
||||
|
||||
addChildNode(p_parent, node);
|
||||
writeNodeConfig(p_parent);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
QSharedPointer<VXNotebookConfigMgr::NodeConfig> VXNotebookConfigMgr::nodeToNodeConfig(const Node *p_node) const
|
||||
{
|
||||
Q_ASSERT(p_node->getType() == Node::Type::Folder);
|
||||
|
||||
auto config = QSharedPointer<NodeConfig>::create(getCodeVersion(),
|
||||
p_node->getId(),
|
||||
p_node->getCreatedTimeUtc());
|
||||
|
||||
for (const auto &child : p_node->getChildren()) {
|
||||
switch (child->getType()) {
|
||||
case Node::Type::File:
|
||||
{
|
||||
NodeFileConfig fileConfig;
|
||||
fileConfig.m_name = child->getName();
|
||||
fileConfig.m_id = child->getId();
|
||||
fileConfig.m_createdTimeUtc = child->getCreatedTimeUtc();
|
||||
fileConfig.m_modifiedTimeUtc = child->getModifiedTimeUtc();
|
||||
fileConfig.m_attachmentFolder = child->getAttachmentFolder();
|
||||
fileConfig.m_tags = child->getTags();
|
||||
|
||||
config->m_files.push_back(fileConfig);
|
||||
break;
|
||||
}
|
||||
|
||||
case Node::Type::Folder:
|
||||
{
|
||||
NodeFolderConfig folderConfig;
|
||||
folderConfig.m_name = child->getName();
|
||||
|
||||
config->m_folders.push_back(folderConfig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::loadNode(Node *p_node) const
|
||||
{
|
||||
if (p_node->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto config = readNodeConfig(p_node->fetchRelativePath());
|
||||
auto folderNode = dynamic_cast<FolderNode *>(p_node);
|
||||
loadFolderNode(folderNode, *config);
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::saveNode(const Node *p_node)
|
||||
{
|
||||
Q_ASSERT(!p_node->isRoot());
|
||||
|
||||
if (p_node->getType() == Node::Type::Folder) {
|
||||
writeNodeConfig(p_node);
|
||||
} else {
|
||||
writeNodeConfig(p_node->getParent());
|
||||
}
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::renameNode(Node *p_node, const QString &p_name)
|
||||
{
|
||||
Q_ASSERT(!p_node->isRoot());
|
||||
switch (p_node->getType()) {
|
||||
case Node::Type::Folder:
|
||||
getBackend()->renameDir(p_node->fetchRelativePath(), p_name);
|
||||
break;
|
||||
|
||||
case Node::Type::File:
|
||||
getBackend()->renameFile(p_node->fetchRelativePath(), p_name);
|
||||
break;
|
||||
}
|
||||
|
||||
p_node->setName(p_name);
|
||||
writeNodeConfig(p_node->getParent());
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::addChildNode(Node *p_parent, const QSharedPointer<Node> &p_child) const
|
||||
{
|
||||
// Add @p_child after the last node of same type.
|
||||
const auto type = p_child->getType();
|
||||
switch (type) {
|
||||
case Node::Type::Folder:
|
||||
{
|
||||
int idx = 0;
|
||||
auto children = p_parent->getChildren();
|
||||
for (; idx < children.size(); ++idx) {
|
||||
if (children[idx]->getType() != type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p_parent->insertChild(idx, p_child);
|
||||
break;
|
||||
}
|
||||
|
||||
case Node::Type::File:
|
||||
p_parent->addChild(p_child);
|
||||
break;
|
||||
}
|
||||
|
||||
inheritNodeFlags(p_parent, p_child.data());
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::loadNodeByPath(const QSharedPointer<Node> &p_root, const QString &p_relativePath)
|
||||
{
|
||||
auto p = PathUtils::cleanPath(p_relativePath);
|
||||
auto paths = p.split('/', QString::SkipEmptyParts);
|
||||
auto node = p_root;
|
||||
for (auto &pa : paths) {
|
||||
// Find child @pa in @node.
|
||||
if (!node->isLoaded()) {
|
||||
loadNode(node.data());
|
||||
}
|
||||
|
||||
auto child = node->findChild(pa, FileUtils::isPlatformNameCaseSensitive());
|
||||
if (!child) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node = child;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// @p_src may belong to different notebook or different kind of configmgr.
|
||||
// TODO: we could constrain @p_src within the same configrmgr?
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::copyNodeAsChildOf(const QSharedPointer<Node> &p_src,
|
||||
Node *p_dest,
|
||||
bool p_move)
|
||||
{
|
||||
Q_ASSERT(p_dest->getType() == Node::Type::Folder);
|
||||
if (!p_src->existsOnDisk()) {
|
||||
Exception::throwOne(Exception::Type::FileMissingOnDisk,
|
||||
QString("source node missing on disk (%1)").arg(p_src->fetchAbsolutePath()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> node;
|
||||
switch (p_src->getType()) {
|
||||
case Node::Type::File:
|
||||
node = copyFileNodeAsChildOf(p_src, p_dest, p_move);
|
||||
break;
|
||||
|
||||
case Node::Type::Folder:
|
||||
node = copyFolderNodeAsChildOf(p_src, p_dest, p_move);
|
||||
break;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::copyFileNodeAsChildOf(const QSharedPointer<Node> &p_src,
|
||||
Node *p_dest,
|
||||
bool p_move)
|
||||
{
|
||||
// Copy source file itself.
|
||||
auto srcFilePath = p_src->fetchAbsolutePath();
|
||||
auto destFilePath = PathUtils::concatenateFilePath(p_dest->fetchRelativePath(),
|
||||
PathUtils::fileName(srcFilePath));
|
||||
destFilePath = getBackend()->renameIfExistsCaseInsensitive(destFilePath);
|
||||
getBackend()->copyFile(srcFilePath, destFilePath);
|
||||
|
||||
// Copy media files fetched from content.
|
||||
NodeContentMediaUtils::copyMediaFiles(p_src.data(), getBackend().data(), destFilePath);
|
||||
|
||||
// Copy attachment folder. Rename attachment folder if conflicts.
|
||||
QString attachmentFolder = p_src->getAttachmentFolder();
|
||||
if (!attachmentFolder.isEmpty()) {
|
||||
auto destAttachmentFolderPath = fetchNodeAttachmentFolder(destFilePath, attachmentFolder);
|
||||
NodeContentMediaUtils::copyAttachment(p_src.data(), getBackend().data(), destFilePath, destAttachmentFolderPath);
|
||||
}
|
||||
|
||||
// Create a file node.
|
||||
auto notebook = getNotebook();
|
||||
auto id = p_src->getId();
|
||||
if (!p_move || p_src->getNotebook() != notebook) {
|
||||
// Use a new id.
|
||||
id = notebook->getAndUpdateNextNodeId();
|
||||
}
|
||||
|
||||
auto destNode = QSharedPointer<FileNode>::create(id,
|
||||
PathUtils::fileName(destFilePath),
|
||||
p_src->getCreatedTimeUtc(),
|
||||
p_src->getModifiedTimeUtc(),
|
||||
attachmentFolder,
|
||||
p_src->getTags(),
|
||||
notebook,
|
||||
p_dest);
|
||||
addChildNode(p_dest, destNode);
|
||||
writeNodeConfig(p_dest);
|
||||
|
||||
if (p_move) {
|
||||
// Delete src node.
|
||||
p_src->getNotebook()->removeNode(p_src);
|
||||
}
|
||||
|
||||
return destNode;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::copyFolderNodeAsChildOf(const QSharedPointer<Node> &p_src,
|
||||
Node *p_dest,
|
||||
bool p_move)
|
||||
{
|
||||
auto srcFolderPath = p_src->fetchAbsolutePath();
|
||||
auto destFolderPath = PathUtils::concatenateFilePath(p_dest->fetchRelativePath(),
|
||||
PathUtils::fileName(srcFolderPath));
|
||||
destFolderPath = getBackend()->renameIfExistsCaseInsensitive(destFolderPath);
|
||||
|
||||
// Make folder.
|
||||
getBackend()->makePath(destFolderPath);
|
||||
|
||||
// Create a folder node.
|
||||
auto notebook = getNotebook();
|
||||
auto id = p_src->getId();
|
||||
if (!p_move || p_src->getNotebook() != notebook) {
|
||||
// Use a new id.
|
||||
id = notebook->getAndUpdateNextNodeId();
|
||||
}
|
||||
auto destNode = QSharedPointer<FolderNode>::create(PathUtils::fileName(destFolderPath),
|
||||
notebook,
|
||||
p_dest);
|
||||
destNode->loadFolder(id, p_src->getCreatedTimeUtc(), QVector<QSharedPointer<Node>>());
|
||||
|
||||
writeNodeConfig(destNode.data());
|
||||
|
||||
addChildNode(p_dest, destNode);
|
||||
writeNodeConfig(p_dest);
|
||||
|
||||
// Copy children node.
|
||||
for (const auto &childNode : p_src->getChildren()) {
|
||||
copyNodeAsChildOf(childNode, destNode.data(), p_move);
|
||||
}
|
||||
|
||||
if (p_move) {
|
||||
p_src->getNotebook()->removeNode(p_src);
|
||||
}
|
||||
|
||||
return destNode;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::removeNode(const QSharedPointer<Node> &p_node, bool p_force, bool p_configOnly)
|
||||
{
|
||||
auto parentNode = p_node->getParent();
|
||||
if (!p_configOnly) {
|
||||
// Remove all children.
|
||||
for (auto &childNode : p_node->getChildren()) {
|
||||
removeNode(childNode, p_force, p_configOnly);
|
||||
}
|
||||
|
||||
removeFilesOfNode(p_node.data(), p_force);
|
||||
}
|
||||
|
||||
if (parentNode) {
|
||||
parentNode->removeChild(p_node);
|
||||
writeNodeConfig(parentNode);
|
||||
}
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::removeFilesOfNode(Node *p_node, bool p_force)
|
||||
{
|
||||
Q_ASSERT(p_node->getNotebook() == getNotebook());
|
||||
switch (p_node->getType()) {
|
||||
case Node::Type::File:
|
||||
{
|
||||
// Delete attachment.
|
||||
if (!p_node->getAttachmentFolder().isEmpty()) {
|
||||
getBackend()->removeDir(p_node->fetchAttachmentFolderPath());
|
||||
}
|
||||
|
||||
// Delete media files fetched from content.
|
||||
NodeContentMediaUtils::removeMediaFiles(p_node);
|
||||
|
||||
// Delete node file itself.
|
||||
auto filePath = p_node->fetchRelativePath();
|
||||
getBackend()->removeFile(filePath);
|
||||
break;
|
||||
}
|
||||
|
||||
case Node::Type::Folder:
|
||||
{
|
||||
Q_ASSERT(p_node->getChildrenCount() == 0);
|
||||
// Delete node config file and the dir if it is empty.
|
||||
auto configFilePath = getNodeConfigFilePath(p_node);
|
||||
getBackend()->removeFile(configFilePath);
|
||||
auto folderPath = p_node->fetchRelativePath();
|
||||
if (p_force) {
|
||||
getBackend()->removeDir(folderPath);
|
||||
} else {
|
||||
getBackend()->removeEmptyDir(folderPath);
|
||||
bool deleted = getBackend()->removeDirIfEmpty(folderPath);
|
||||
if (!deleted) {
|
||||
qWarning() << "folder is not deleted since it is not empty" << folderPath;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool VXNotebookConfigMgr::nodeExistsOnDisk(const Node *p_node) const
|
||||
{
|
||||
return getBackend()->exists(p_node->fetchRelativePath());
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgr::readNode(const Node *p_node) const
|
||||
{
|
||||
Q_ASSERT(p_node->getType() == Node::Type::File);
|
||||
return getBackend()->readTextFile(p_node->fetchRelativePath());
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::writeNode(Node *p_node, const QString &p_content)
|
||||
{
|
||||
Q_ASSERT(p_node->getType() == Node::Type::File);
|
||||
getBackend()->writeFile(p_node->fetchRelativePath(), p_content);
|
||||
|
||||
p_node->setModifiedTimeUtc();
|
||||
writeNodeConfig(p_node->getParent());
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgr::fetchNodeImageFolderPath(Node *p_node)
|
||||
{
|
||||
auto pa = PathUtils::concatenateFilePath(PathUtils::parentDirPath(p_node->fetchAbsolutePath()),
|
||||
getNotebook()->getImageFolder());
|
||||
// Do not make the folder when it is a folder node request.
|
||||
if (p_node->getType() == Node::Type::File) {
|
||||
getBackend()->makePath(pa);
|
||||
}
|
||||
return pa;
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgr::fetchNodeAttachmentFolderPath(Node *p_node)
|
||||
{
|
||||
auto notebookFolder = PathUtils::concatenateFilePath(PathUtils::parentDirPath(p_node->fetchAbsolutePath()),
|
||||
getNotebook()->getAttachmentFolder());
|
||||
if (p_node->getType() == Node::Type::File) {
|
||||
auto nodeFolder = p_node->getAttachmentFolder();
|
||||
if (nodeFolder.isEmpty()) {
|
||||
auto folderPath = fetchNodeAttachmentFolder(p_node->fetchAbsolutePath(), nodeFolder);
|
||||
p_node->setAttachmentFolder(nodeFolder);
|
||||
saveNode(p_node);
|
||||
|
||||
getBackend()->makePath(folderPath);
|
||||
return folderPath;
|
||||
} else {
|
||||
return PathUtils::concatenateFilePath(notebookFolder, nodeFolder);
|
||||
}
|
||||
} else {
|
||||
// Do not make the folder when it is a folder node request.
|
||||
return notebookFolder;
|
||||
}
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgr::fetchNodeAttachmentFolder(const QString &p_nodePath, QString &p_folderName)
|
||||
{
|
||||
auto notebookFolder = PathUtils::concatenateFilePath(PathUtils::parentDirPath(p_nodePath),
|
||||
getNotebook()->getAttachmentFolder());
|
||||
if (p_folderName.isEmpty()) {
|
||||
p_folderName = FileUtils::generateUniqueFileName(notebookFolder, QString(), QString());
|
||||
} else if (FileUtils::childExistsCaseInsensitive(notebookFolder, p_folderName)) {
|
||||
p_folderName = FileUtils::generateFileNameWithSequence(notebookFolder, p_folderName, QString());
|
||||
}
|
||||
return PathUtils::concatenateFilePath(notebookFolder, p_folderName);
|
||||
}
|
||||
|
||||
bool VXNotebookConfigMgr::isBuiltInFile(const Node *p_node, const QString &p_name) const
|
||||
{
|
||||
const auto name = p_name.toLower();
|
||||
if (name == c_nodeConfigName) {
|
||||
return true;
|
||||
}
|
||||
return BundleNotebookConfigMgr::isBuiltInFile(p_node, p_name);
|
||||
}
|
||||
|
||||
bool VXNotebookConfigMgr::isBuiltInFolder(const Node *p_node, const QString &p_name) const
|
||||
{
|
||||
const auto name = p_name.toLower();
|
||||
if (name == c_recycleBinFolderName
|
||||
|| name == getNotebook()->getImageFolder().toLower()
|
||||
|| name == getNotebook()->getAttachmentFolder().toLower()) {
|
||||
return true;
|
||||
}
|
||||
return BundleNotebookConfigMgr::isBuiltInFolder(p_node, p_name);
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::copyFileAsChildOf(const QString &p_srcPath, Node *p_dest)
|
||||
{
|
||||
// Copy source file itself.
|
||||
auto destFilePath = PathUtils::concatenateFilePath(p_dest->fetchRelativePath(),
|
||||
PathUtils::fileName(p_srcPath));
|
||||
destFilePath = getBackend()->renameIfExistsCaseInsensitive(destFilePath);
|
||||
getBackend()->copyFile(p_srcPath, destFilePath);
|
||||
|
||||
// Copy media files fetched from content.
|
||||
NodeContentMediaUtils::copyMediaFiles(p_srcPath, getBackend().data(), destFilePath);
|
||||
|
||||
// Create a file node.
|
||||
auto currentTime = QDateTime::currentDateTimeUtc();
|
||||
auto destNode = QSharedPointer<FileNode>::create(getNotebook()->getAndUpdateNextNodeId(),
|
||||
PathUtils::fileName(destFilePath),
|
||||
currentTime,
|
||||
currentTime,
|
||||
QString(),
|
||||
QStringList(),
|
||||
getNotebook(),
|
||||
p_dest);
|
||||
addChildNode(p_dest, destNode);
|
||||
writeNodeConfig(p_dest);
|
||||
|
||||
return destNode;
|
||||
}
|
||||
|
||||
QSharedPointer<Node> VXNotebookConfigMgr::copyFolderAsChildOf(const QString &p_srcPath, Node *p_dest)
|
||||
{
|
||||
auto destFolderPath = PathUtils::concatenateFilePath(p_dest->fetchRelativePath(),
|
||||
PathUtils::fileName(p_srcPath));
|
||||
destFolderPath = getBackend()->renameIfExistsCaseInsensitive(destFolderPath);
|
||||
|
||||
// Copy folder.
|
||||
getBackend()->copyDir(p_srcPath, destFolderPath);
|
||||
|
||||
// Create a folder node.
|
||||
auto notebook = getNotebook();
|
||||
auto destNode = QSharedPointer<FolderNode>::create(PathUtils::fileName(destFolderPath),
|
||||
notebook,
|
||||
p_dest);
|
||||
destNode->loadFolder(notebook->getAndUpdateNextNodeId(), QDateTime::currentDateTimeUtc(), QVector<QSharedPointer<Node>>());
|
||||
|
||||
writeNodeConfig(destNode.data());
|
||||
|
||||
addChildNode(p_dest, destNode);
|
||||
writeNodeConfig(p_dest);
|
||||
|
||||
return destNode;
|
||||
}
|
||||
|
||||
void VXNotebookConfigMgr::inheritNodeFlags(const Node *p_node, Node *p_child) const
|
||||
{
|
||||
if (p_node->getFlags() & Node::Flag::ReadOnly) {
|
||||
markNodeReadOnly(p_child);
|
||||
}
|
||||
}
|
205
src/core/notebookconfigmgr/vxnotebookconfigmgr.h
Normal file
205
src/core/notebookconfigmgr/vxnotebookconfigmgr.h
Normal file
@ -0,0 +1,205 @@
|
||||
#ifndef VXNOTEBOOKCONFIGMGR_H
|
||||
#define VXNOTEBOOKCONFIGMGR_H
|
||||
|
||||
#include "bundlenotebookconfigmgr.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QVector>
|
||||
|
||||
#include "../global.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class FolderNode;
|
||||
|
||||
// Config manager for VNoteX's bundle notebook.
|
||||
class VXNotebookConfigMgr : public BundleNotebookConfigMgr
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VXNotebookConfigMgr(const QString &p_name,
|
||||
const QString &p_displayName,
|
||||
const QString &p_description,
|
||||
const QSharedPointer<INotebookBackend> &p_backend,
|
||||
QObject *p_parent = nullptr);
|
||||
|
||||
QString getName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getDisplayName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getDescription() const Q_DECL_OVERRIDE;
|
||||
|
||||
void createEmptySkeleton(const NotebookParameters &p_paras) Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<Node> loadRootNode() const Q_DECL_OVERRIDE;
|
||||
|
||||
void loadNode(Node *p_node) const Q_DECL_OVERRIDE;
|
||||
void saveNode(const Node *p_node) Q_DECL_OVERRIDE;
|
||||
|
||||
void renameNode(Node *p_node, const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<Node> newNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_name) Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<Node> addAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_name,
|
||||
const NodeParameters &p_paras) Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<Node> copyAsNode(Node *p_parent,
|
||||
Node::Type p_type,
|
||||
const QString &p_path) Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<Node> loadNodeByPath(const QSharedPointer<Node> &p_root,
|
||||
const QString &p_relativePath) Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<Node> copyNodeAsChildOf(const QSharedPointer<Node> &p_src,
|
||||
Node *p_dest,
|
||||
bool p_move) Q_DECL_OVERRIDE;
|
||||
|
||||
void removeNode(const QSharedPointer<Node> &p_node, bool p_force = false, bool p_configOnly = false) Q_DECL_OVERRIDE;
|
||||
|
||||
bool nodeExistsOnDisk(const Node *p_node) const Q_DECL_OVERRIDE;
|
||||
|
||||
QString readNode(const Node *p_node) const Q_DECL_OVERRIDE;
|
||||
|
||||
void writeNode(Node *p_node, const QString &p_content) Q_DECL_OVERRIDE;
|
||||
|
||||
QString fetchNodeImageFolderPath(Node *p_node) Q_DECL_OVERRIDE;
|
||||
|
||||
QString fetchNodeAttachmentFolderPath(Node *p_node) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
// Config of a file child.
|
||||
struct NodeFileConfig
|
||||
{
|
||||
QJsonObject toJson() const;
|
||||
|
||||
void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
QString m_name;
|
||||
ID m_id = Node::InvalidId;
|
||||
QDateTime m_createdTimeUtc;
|
||||
QDateTime m_modifiedTimeUtc;
|
||||
QString m_attachmentFolder;
|
||||
QStringList m_tags;
|
||||
};
|
||||
|
||||
// Config of a folder child.
|
||||
struct NodeFolderConfig
|
||||
{
|
||||
QJsonObject toJson() const;
|
||||
|
||||
void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
QString m_name;
|
||||
};
|
||||
|
||||
// Config of a folder node.
|
||||
struct NodeConfig
|
||||
{
|
||||
NodeConfig();
|
||||
|
||||
NodeConfig(const QString &p_version,
|
||||
ID p_id,
|
||||
const QDateTime &p_createdTimeUtc);
|
||||
|
||||
QJsonObject toJson() const;
|
||||
|
||||
void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
QString m_version;
|
||||
ID m_id = Node::InvalidId;
|
||||
QDateTime m_createdTimeUtc;
|
||||
QVector<NodeFileConfig> m_files;
|
||||
QVector<NodeFolderConfig> m_folders;
|
||||
|
||||
static const QString c_version;
|
||||
|
||||
static const QString c_id;
|
||||
|
||||
static const QString c_createdTimeUtc;
|
||||
|
||||
static const QString c_files;
|
||||
|
||||
static const QString c_folders;
|
||||
|
||||
static const QString c_name;
|
||||
|
||||
static const QString c_modifiedTimeUtc;
|
||||
|
||||
static const QString c_attachmentFolder;
|
||||
|
||||
static const QString c_tags;
|
||||
};
|
||||
|
||||
void createEmptyRootNode();
|
||||
|
||||
QSharedPointer<VXNotebookConfigMgr::NodeConfig> readNodeConfig(const QString &p_path) const;
|
||||
void writeNodeConfig(const QString &p_path, const NodeConfig &p_config) const;
|
||||
|
||||
void writeNodeConfig(const Node *p_node);
|
||||
|
||||
QSharedPointer<Node> nodeConfigToNode(const NodeConfig &p_config,
|
||||
const QString &p_name,
|
||||
Node *p_parent = nullptr) const;
|
||||
|
||||
void loadFolderNode(FolderNode *p_node, const NodeConfig &p_config) const;
|
||||
|
||||
QSharedPointer<VXNotebookConfigMgr::NodeConfig> nodeToNodeConfig(const Node *p_node) const;
|
||||
|
||||
QSharedPointer<Node> newFileNode(Node *p_parent,
|
||||
const QString &p_name,
|
||||
bool p_create,
|
||||
const NodeParameters &p_paras);
|
||||
|
||||
QSharedPointer<Node> newFolderNode(Node *p_parent,
|
||||
const QString &p_name,
|
||||
bool p_create,
|
||||
const NodeParameters &p_paras);
|
||||
|
||||
QString getNodeConfigFilePath(const Node *p_node) const;
|
||||
|
||||
void addChildNode(Node *p_parent, const QSharedPointer<Node> &p_child) const;
|
||||
|
||||
QSharedPointer<Node> copyFileNodeAsChildOf(const QSharedPointer<Node> &p_src, Node *p_dest, bool p_move);
|
||||
|
||||
QSharedPointer<Node> copyFolderNodeAsChildOf(const QSharedPointer<Node> &p_src, Node *p_dest, bool p_move);
|
||||
|
||||
QSharedPointer<Node> copyFileAsChildOf(const QString &p_srcPath, Node *p_dest);
|
||||
|
||||
QSharedPointer<Node> copyFolderAsChildOf(const QString &p_srcPath, Node *p_dest);
|
||||
|
||||
void removeFilesOfNode(Node *p_node, bool p_force);
|
||||
|
||||
bool markRecycleBinNode(const QSharedPointer<Node> &p_root) const;
|
||||
|
||||
void markNodeReadOnly(Node *p_node) const;
|
||||
|
||||
void createRecycleBinNode(const QSharedPointer<Node> &p_root);
|
||||
|
||||
// Generate node attachment folder.
|
||||
// @p_folderName: suggested folder name if not empty, may be renamed due to conflicts.
|
||||
// Return the attachment folder path.
|
||||
QString fetchNodeAttachmentFolder(const QString &p_nodePath, QString &p_folderName);
|
||||
|
||||
bool isBuiltInFile(const Node *p_node, const QString &p_name) const Q_DECL_OVERRIDE;
|
||||
|
||||
bool isBuiltInFolder(const Node *p_node, const QString &p_name) const Q_DECL_OVERRIDE;
|
||||
|
||||
void inheritNodeFlags(const Node *p_node, Node *p_child) const;
|
||||
|
||||
Info m_info;
|
||||
|
||||
// Name of the node's config file.
|
||||
static const QString c_nodeConfigName;
|
||||
|
||||
// Name of the recycle bin folder which should be a child of the root node.
|
||||
static const QString c_recycleBinFolderName;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // VXNOTEBOOKCONFIGMGR_H
|
35
src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.cpp
Normal file
35
src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include "vxnotebookconfigmgrfactory.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "vxnotebookconfigmgr.h"
|
||||
#include "../notebookbackend/inotebookbackend.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
VXNotebookConfigMgrFactory::VXNotebookConfigMgrFactory()
|
||||
{
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgrFactory::getName() const
|
||||
{
|
||||
return QStringLiteral("vx.vnotex");
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgrFactory::getDisplayName() const
|
||||
{
|
||||
return QObject::tr("VNoteX Notebook Configuration");
|
||||
}
|
||||
|
||||
QString VXNotebookConfigMgrFactory::getDescription() const
|
||||
{
|
||||
return QObject::tr("Built-in VNoteX notebook configuration");
|
||||
}
|
||||
|
||||
QSharedPointer<INotebookConfigMgr> VXNotebookConfigMgrFactory::createNotebookConfigMgr(const QSharedPointer<INotebookBackend> &p_backend)
|
||||
{
|
||||
return QSharedPointer<VXNotebookConfigMgr>::create(getName(),
|
||||
getDisplayName(),
|
||||
getDescription(),
|
||||
p_backend);
|
||||
}
|
25
src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.h
Normal file
25
src/core/notebookconfigmgr/vxnotebookconfigmgrfactory.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef VXNOTEBOOKCONFIGMGRFACTORY_H
|
||||
#define VXNOTEBOOKCONFIGMGRFACTORY_H
|
||||
|
||||
|
||||
#include "inotebookconfigmgrfactory.h"
|
||||
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class VXNotebookConfigMgrFactory : public INotebookConfigMgrFactory
|
||||
{
|
||||
public:
|
||||
VXNotebookConfigMgrFactory();
|
||||
|
||||
QString getName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getDisplayName() const Q_DECL_OVERRIDE;
|
||||
|
||||
QString getDescription()const Q_DECL_OVERRIDE;
|
||||
|
||||
QSharedPointer<INotebookConfigMgr> createNotebookConfigMgr(const QSharedPointer<INotebookBackend> &p_backend) Q_DECL_OVERRIDE;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // VXNOTEBOOKCONFIGMGRFACTORY_H
|
376
src/core/notebookmgr.cpp
Normal file
376
src/core/notebookmgr.cpp
Normal file
@ -0,0 +1,376 @@
|
||||
#include "notebookmgr.h"
|
||||
|
||||
#include <versioncontroller/dummyversioncontrollerfactory.h>
|
||||
#include <versioncontroller/iversioncontroller.h>
|
||||
#include <notebookconfigmgr/vxnotebookconfigmgrfactory.h>
|
||||
#include <notebookconfigmgr/inotebookconfigmgr.h>
|
||||
#include <notebookbackend/localnotebookbackendfactory.h>
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
#include <notebook/bundlenotebookfactory.h>
|
||||
#include <notebook/notebook.h>
|
||||
#include <notebook/notebookparameters.h>
|
||||
#include "exception.h"
|
||||
#include "configmgr.h"
|
||||
#include <utils/pathutils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
NotebookMgr::NotebookMgr(QObject *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_currentNotebookId(Notebook::InvalidId)
|
||||
{
|
||||
}
|
||||
|
||||
void NotebookMgr::init()
|
||||
{
|
||||
initVersionControllerServer();
|
||||
|
||||
initConfigMgrServer();
|
||||
|
||||
initBackendServer();
|
||||
|
||||
initNotebookServer();
|
||||
}
|
||||
|
||||
void NotebookMgr::initVersionControllerServer()
|
||||
{
|
||||
m_versionControllerServer.reset(new NameBasedServer<IVersionControllerFactory>);
|
||||
|
||||
// Dummy Version Controller.
|
||||
auto dummyFactory = QSharedPointer<DummyVersionControllerFactory>::create();
|
||||
m_versionControllerServer->registerItem(dummyFactory->getName(), dummyFactory);
|
||||
}
|
||||
|
||||
void NotebookMgr::initConfigMgrServer()
|
||||
{
|
||||
m_configMgrServer.reset(new NameBasedServer<INotebookConfigMgrFactory>);
|
||||
|
||||
// VX Notebook Config Manager.
|
||||
auto vxFactory = QSharedPointer<VXNotebookConfigMgrFactory>::create();
|
||||
m_configMgrServer->registerItem(vxFactory->getName(), vxFactory);
|
||||
|
||||
}
|
||||
|
||||
void NotebookMgr::initBackendServer()
|
||||
{
|
||||
m_backendServer.reset(new NameBasedServer<INotebookBackendFactory>);
|
||||
|
||||
// Local Notebook Backend.
|
||||
auto localFactory = QSharedPointer<LocalNotebookBackendFactory>::create();
|
||||
m_backendServer->registerItem(localFactory->getName(), localFactory);
|
||||
}
|
||||
|
||||
void NotebookMgr::initNotebookServer()
|
||||
{
|
||||
m_notebookServer.reset(new NameBasedServer<INotebookFactory>);
|
||||
|
||||
// Bundle Notebook.
|
||||
auto bundleFacotry = QSharedPointer<BundleNotebookFactory>::create();
|
||||
m_notebookServer->registerItem(bundleFacotry->getName(), bundleFacotry);
|
||||
}
|
||||
|
||||
QSharedPointer<INotebookFactory> NotebookMgr::getBundleNotebookFactory() const
|
||||
{
|
||||
return m_notebookServer->getItem(QStringLiteral("bundle.vnotex"));
|
||||
}
|
||||
|
||||
QList<QSharedPointer<INotebookFactory>> NotebookMgr::getAllNotebookFactories() const
|
||||
{
|
||||
return m_notebookServer->getAllItems();
|
||||
}
|
||||
|
||||
QList<QSharedPointer<IVersionControllerFactory>> NotebookMgr::getAllVersionControllerFactories() const
|
||||
{
|
||||
return m_versionControllerServer->getAllItems();
|
||||
}
|
||||
|
||||
QList<QSharedPointer<INotebookConfigMgrFactory>> NotebookMgr::getAllNotebookConfigMgrFactories() const
|
||||
{
|
||||
return m_configMgrServer->getAllItems();
|
||||
}
|
||||
|
||||
QList<QSharedPointer<INotebookBackendFactory>> NotebookMgr::getAllNotebookBackendFactories() const
|
||||
{
|
||||
return m_backendServer->getAllItems();
|
||||
}
|
||||
|
||||
QSharedPointer<INotebookBackend> NotebookMgr::createNotebookBackend(const QString &p_backendName,
|
||||
const QString &p_rootFolderPath) const
|
||||
{
|
||||
auto factory = m_backendServer->getItem(p_backendName);
|
||||
if (factory) {
|
||||
return factory->createNotebookBackend(p_rootFolderPath);
|
||||
} else {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to find notebook backend factory %1").arg(p_backendName));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<IVersionController> NotebookMgr::createVersionController(const QString &p_controllerName) const
|
||||
{
|
||||
auto factory = m_versionControllerServer->getItem(p_controllerName);
|
||||
if (factory) {
|
||||
return factory->createVersionController();
|
||||
} else {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to find version controller factory %1").arg(p_controllerName));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<INotebookConfigMgr> NotebookMgr::createNotebookConfigMgr(const QString &p_mgrName,
|
||||
const QSharedPointer<INotebookBackend> &p_backend) const
|
||||
{
|
||||
auto factory = m_configMgrServer->getItem(p_mgrName);
|
||||
if (factory) {
|
||||
return factory->createNotebookConfigMgr(p_backend);
|
||||
} else {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to find notebook config manager factory %1").arg(p_mgrName));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NotebookMgr::loadNotebooks()
|
||||
{
|
||||
readNotebooksFromConfig();
|
||||
|
||||
loadCurrentNotebookId();
|
||||
}
|
||||
|
||||
static SessionConfig &getSessionConfig()
|
||||
{
|
||||
return ConfigMgr::getInst().getSessionConfig();
|
||||
}
|
||||
|
||||
void NotebookMgr::loadCurrentNotebookId()
|
||||
{
|
||||
auto &rootFolderPath = getSessionConfig().getCurrentNotebookRootFolderPath();
|
||||
auto notebook = findNotebookByRootFolderPath(rootFolderPath);
|
||||
if (notebook) {
|
||||
m_currentNotebookId = notebook->getId();
|
||||
} else {
|
||||
m_currentNotebookId = Notebook::InvalidId;
|
||||
}
|
||||
|
||||
emit currentNotebookChanged(notebook);
|
||||
}
|
||||
|
||||
QSharedPointer<Notebook> NotebookMgr::newNotebook(const QSharedPointer<NotebookParameters> &p_parameters)
|
||||
{
|
||||
auto factory = m_notebookServer->getItem(p_parameters->m_type);
|
||||
if (!factory) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to find notebook factory %1").arg(p_parameters->m_type));
|
||||
}
|
||||
|
||||
auto notebook = factory->newNotebook(*p_parameters);
|
||||
addNotebook(notebook);
|
||||
|
||||
saveNotebooksToConfig();
|
||||
|
||||
emit notebooksUpdated();
|
||||
|
||||
setCurrentNotebook(notebook->getId());
|
||||
|
||||
return notebook;
|
||||
}
|
||||
|
||||
void NotebookMgr::importNotebook(const QSharedPointer<Notebook> &p_notebook)
|
||||
{
|
||||
Q_ASSERT(p_notebook);
|
||||
if (m_notebooks.indexOf(p_notebook) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
addNotebook(p_notebook);
|
||||
|
||||
saveNotebooksToConfig();
|
||||
|
||||
emit notebooksUpdated();
|
||||
|
||||
setCurrentNotebook(p_notebook->getId());
|
||||
}
|
||||
|
||||
static SessionConfig::NotebookItem notebookToSessionConfig(const QSharedPointer<const Notebook> &p_notebook)
|
||||
{
|
||||
SessionConfig::NotebookItem item;
|
||||
item.m_type = p_notebook->getType();
|
||||
item.m_rootFolderPath = p_notebook->getRootFolderPath();
|
||||
item.m_backend = p_notebook->getBackend()->getName();
|
||||
return item;
|
||||
}
|
||||
|
||||
void NotebookMgr::saveNotebooksToConfig() const
|
||||
{
|
||||
QVector<SessionConfig::NotebookItem> items;
|
||||
items.reserve(m_notebooks.size());
|
||||
for (auto &nb : m_notebooks) {
|
||||
items.push_back(notebookToSessionConfig(nb));
|
||||
}
|
||||
|
||||
getSessionConfig().setNotebooks(items);
|
||||
}
|
||||
|
||||
void NotebookMgr::readNotebooksFromConfig()
|
||||
{
|
||||
Q_ASSERT(m_notebooks.isEmpty());
|
||||
auto items = getSessionConfig().getNotebooks();
|
||||
for (auto &item : items) {
|
||||
try {
|
||||
auto nb = readNotebookFromConfig(item);
|
||||
addNotebook(nb);
|
||||
} catch (Exception &p_e) {
|
||||
qCritical("fail to read notebook (%s) from config (%s)",
|
||||
item.m_rootFolderPath.toStdString().c_str(),
|
||||
p_e.what());
|
||||
}
|
||||
}
|
||||
|
||||
emit notebooksUpdated();
|
||||
}
|
||||
|
||||
QSharedPointer<Notebook> NotebookMgr::readNotebookFromConfig(const SessionConfig::NotebookItem &p_item)
|
||||
{
|
||||
auto factory = m_notebookServer->getItem(p_item.m_type);
|
||||
if (!factory) {
|
||||
Exception::throwOne(Exception::Type::InvalidArgument,
|
||||
QString("fail to find notebook factory %1").arg(p_item.m_type));
|
||||
}
|
||||
|
||||
auto backend = createNotebookBackend(p_item.m_backend, p_item.m_rootFolderPath);
|
||||
|
||||
auto notebook = factory->createNotebook(*this, p_item.m_rootFolderPath, backend);
|
||||
return notebook;
|
||||
}
|
||||
|
||||
const QVector<QSharedPointer<Notebook>> &NotebookMgr::getNotebooks() const
|
||||
{
|
||||
return m_notebooks;
|
||||
}
|
||||
|
||||
ID NotebookMgr::getCurrentNotebookId() const
|
||||
{
|
||||
return m_currentNotebookId;
|
||||
}
|
||||
|
||||
void NotebookMgr::setCurrentNotebook(ID p_notebookId)
|
||||
{
|
||||
auto lastId = m_currentNotebookId;
|
||||
m_currentNotebookId = p_notebookId;
|
||||
auto nb = findNotebookById(p_notebookId);
|
||||
if (!nb) {
|
||||
m_currentNotebookId = Notebook::InvalidId;
|
||||
}
|
||||
|
||||
if (lastId != m_currentNotebookId) {
|
||||
emit currentNotebookChanged(nb);
|
||||
}
|
||||
|
||||
getSessionConfig().setCurrentNotebookRootFolderPath(nb ? nb->getRootFolderPath() : "");
|
||||
}
|
||||
|
||||
QSharedPointer<Notebook> NotebookMgr::findNotebookByRootFolderPath(const QString &p_rootFolderPath) const
|
||||
{
|
||||
for (auto &nb : m_notebooks) {
|
||||
if (PathUtils::areSamePaths(nb->getRootFolderPath(), p_rootFolderPath)) {
|
||||
return nb;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSharedPointer<Notebook> NotebookMgr::findNotebookById(ID p_id) const
|
||||
{
|
||||
for (auto &nb : m_notebooks) {
|
||||
if (nb->getId() == p_id) {
|
||||
return nb;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NotebookMgr::closeNotebook(ID p_id)
|
||||
{
|
||||
auto it = std::find_if(m_notebooks.begin(),
|
||||
m_notebooks.end(),
|
||||
[p_id](const QSharedPointer<Notebook> &p_nb) {
|
||||
return p_nb->getId() == p_id;
|
||||
});
|
||||
if (it == m_notebooks.end()) {
|
||||
qWarning() << "fail to find notebook of given id to close" << p_id;
|
||||
return;
|
||||
}
|
||||
|
||||
auto notebookToClose = *it;
|
||||
emit notebookAboutToClose(notebookToClose.data());
|
||||
|
||||
m_notebooks.erase(it);
|
||||
|
||||
saveNotebooksToConfig();
|
||||
|
||||
emit notebooksUpdated();
|
||||
setCurrentNotebookAfterUpdate();
|
||||
|
||||
qInfo() << QString("notebook %1 (%2) is closed").arg(notebookToClose->getName(),
|
||||
notebookToClose->getRootFolderPath());
|
||||
}
|
||||
|
||||
void NotebookMgr::removeNotebook(ID p_id)
|
||||
{
|
||||
auto it = std::find_if(m_notebooks.begin(),
|
||||
m_notebooks.end(),
|
||||
[p_id](const QSharedPointer<Notebook> &p_nb) {
|
||||
return p_nb->getId() == p_id;
|
||||
});
|
||||
if (it == m_notebooks.end()) {
|
||||
qWarning() << "fail to find notebook of given id to remove" << p_id;
|
||||
return;
|
||||
}
|
||||
|
||||
auto nbToRemove = *it;
|
||||
emit notebookAboutToRemove(nbToRemove.data());
|
||||
|
||||
m_notebooks.erase(it);
|
||||
|
||||
saveNotebooksToConfig();
|
||||
|
||||
emit notebooksUpdated();
|
||||
setCurrentNotebookAfterUpdate();
|
||||
|
||||
try {
|
||||
nbToRemove->remove();
|
||||
} catch (Exception &p_e) {
|
||||
qWarning() << QString("fail to remove notebook %1 (%2) (%3)").arg(nbToRemove->getName(),
|
||||
nbToRemove->getRootFolderPath(),
|
||||
p_e.what());
|
||||
throw;
|
||||
}
|
||||
|
||||
qInfo() << QString("notebook %1 (%2) is removed").arg(nbToRemove->getName(),
|
||||
nbToRemove->getRootFolderPath());
|
||||
}
|
||||
|
||||
void NotebookMgr::setCurrentNotebookAfterUpdate()
|
||||
{
|
||||
if (!m_notebooks.isEmpty()) {
|
||||
setCurrentNotebook(m_notebooks.first()->getId());
|
||||
} else {
|
||||
setCurrentNotebook(Notebook::InvalidId);
|
||||
}
|
||||
}
|
||||
|
||||
void NotebookMgr::addNotebook(const QSharedPointer<Notebook> &p_notebook)
|
||||
{
|
||||
m_notebooks.push_back(p_notebook);
|
||||
connect(p_notebook.data(), &Notebook::updated,
|
||||
this, [this, notebook = p_notebook.data()]() {
|
||||
emit notebookUpdated(notebook);
|
||||
});
|
||||
}
|
118
src/core/notebookmgr.h
Normal file
118
src/core/notebookmgr.h
Normal file
@ -0,0 +1,118 @@
|
||||
#ifndef NOTEBOOKMGR_H
|
||||
#define NOTEBOOKMGR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
|
||||
#include "namebasedserver.h"
|
||||
#include "sessionconfig.h"
|
||||
#include "global.h"
|
||||
#include "notebook/notebook.h"
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class IVersionController;
|
||||
class IVersionControllerFactory;
|
||||
class INotebookConfigMgr;
|
||||
class INotebookConfigMgrFactory;
|
||||
class INotebookBackend;
|
||||
class INotebookBackendFactory;
|
||||
class INotebookFactory;
|
||||
class NotebookParameters;
|
||||
|
||||
class NotebookMgr : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NotebookMgr(QObject *p_parent = nullptr);
|
||||
|
||||
void init();
|
||||
|
||||
QSharedPointer<INotebookFactory> getBundleNotebookFactory() const;
|
||||
|
||||
QList<QSharedPointer<INotebookFactory>> getAllNotebookFactories() const;
|
||||
|
||||
QList<QSharedPointer<IVersionControllerFactory>> getAllVersionControllerFactories() const;
|
||||
|
||||
QList<QSharedPointer<INotebookConfigMgrFactory>> getAllNotebookConfigMgrFactories() const;
|
||||
|
||||
QList<QSharedPointer<INotebookBackendFactory>> getAllNotebookBackendFactories() const;
|
||||
|
||||
QSharedPointer<INotebookBackend> createNotebookBackend(const QString &p_backendName,
|
||||
const QString &p_rootFolderPath) const;
|
||||
|
||||
QSharedPointer<IVersionController> createVersionController(const QString &p_controllerName) const;
|
||||
|
||||
QSharedPointer<INotebookConfigMgr> createNotebookConfigMgr(const QString &p_mgrName,
|
||||
const QSharedPointer<INotebookBackend> &p_backend) const;
|
||||
|
||||
void loadNotebooks();
|
||||
|
||||
QSharedPointer<Notebook> newNotebook(const QSharedPointer<NotebookParameters> &p_parameters);
|
||||
|
||||
void importNotebook(const QSharedPointer<Notebook> &p_notebook);
|
||||
|
||||
const QVector<QSharedPointer<Notebook>> &getNotebooks() const;
|
||||
|
||||
ID getCurrentNotebookId() const;
|
||||
|
||||
// Find the notebook with the same directory as root folder.
|
||||
QSharedPointer<Notebook> findNotebookByRootFolderPath(const QString &p_rootFolderPath) const;
|
||||
|
||||
QSharedPointer<Notebook> findNotebookById(ID p_id) const;
|
||||
|
||||
void closeNotebook(ID p_id);
|
||||
|
||||
void removeNotebook(ID p_id);
|
||||
|
||||
public slots:
|
||||
void setCurrentNotebook(ID p_notebookId);
|
||||
|
||||
signals:
|
||||
void notebooksUpdated();
|
||||
|
||||
void notebookUpdated(const Notebook *p_notebook);
|
||||
|
||||
void notebookAboutToClose(const Notebook *p_notebook);
|
||||
|
||||
void notebookAboutToRemove(const Notebook *p_notebook);
|
||||
|
||||
void currentNotebookChanged(const QSharedPointer<Notebook> &p_notebook);
|
||||
|
||||
private:
|
||||
void initVersionControllerServer();
|
||||
|
||||
void initConfigMgrServer();
|
||||
|
||||
void initBackendServer();
|
||||
|
||||
void initNotebookServer();
|
||||
|
||||
void saveNotebooksToConfig() const;
|
||||
void readNotebooksFromConfig();
|
||||
|
||||
void loadCurrentNotebookId();
|
||||
|
||||
QSharedPointer<Notebook> readNotebookFromConfig(const SessionConfig::NotebookItem &p_item);
|
||||
|
||||
void setCurrentNotebookAfterUpdate();
|
||||
|
||||
void addNotebook(const QSharedPointer<Notebook> &p_notebook);
|
||||
|
||||
QSharedPointer<NameBasedServer<IVersionControllerFactory>> m_versionControllerServer;
|
||||
|
||||
QSharedPointer<NameBasedServer<INotebookConfigMgrFactory>> m_configMgrServer;
|
||||
|
||||
QSharedPointer<NameBasedServer<INotebookBackendFactory>> m_backendServer;
|
||||
|
||||
QSharedPointer<NameBasedServer<INotebookFactory>> m_notebookServer;
|
||||
|
||||
QVector<QSharedPointer<Notebook>> m_notebooks;
|
||||
|
||||
ID m_currentNotebookId = 0;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // NOTEBOOKMGR_H
|
253
src/core/sessionconfig.cpp
Normal file
253
src/core/sessionconfig.cpp
Normal file
@ -0,0 +1,253 @@
|
||||
#include "sessionconfig.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include "configmgr.h"
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
bool SessionConfig::NotebookItem::operator==(const NotebookItem &p_other) const
|
||||
{
|
||||
return m_type == p_other.m_type
|
||||
&& m_rootFolderPath == p_other.m_rootFolderPath
|
||||
&& m_backend == p_other.m_backend;
|
||||
}
|
||||
|
||||
void SessionConfig::NotebookItem::fromJson(const QJsonObject &p_jobj)
|
||||
{
|
||||
m_type = p_jobj[QStringLiteral("type")].toString();
|
||||
m_rootFolderPath = p_jobj[QStringLiteral("root_folder")].toString();
|
||||
m_backend = p_jobj[QStringLiteral("backend")].toString();
|
||||
}
|
||||
|
||||
QJsonObject SessionConfig::NotebookItem::toJson() const
|
||||
{
|
||||
QJsonObject jobj;
|
||||
|
||||
jobj[QStringLiteral("type")] = m_type;
|
||||
jobj[QStringLiteral("root_folder")] = m_rootFolderPath;
|
||||
jobj[QStringLiteral("backend")] = m_backend;
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
SessionConfig::SessionConfig(ConfigMgr *p_mgr)
|
||||
: IConfig(p_mgr, nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SessionConfig::~SessionConfig()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SessionConfig::init()
|
||||
{
|
||||
auto mgr = getMgr();
|
||||
auto sessionSettings = mgr->getSettings(ConfigMgr::Source::Session);
|
||||
const auto &sessionJobj = sessionSettings->getJson();
|
||||
|
||||
loadCore(sessionJobj);
|
||||
}
|
||||
|
||||
void SessionConfig::loadCore(const QJsonObject &p_session)
|
||||
{
|
||||
const auto coreObj = p_session.value(QStringLiteral("core")).toObject();
|
||||
m_newNotebookDefaultRootFolderPath = readString(coreObj,
|
||||
QStringLiteral("new_notebook_default_root_folder_path"));
|
||||
if (m_newNotebookDefaultRootFolderPath.isEmpty()) {
|
||||
m_newNotebookDefaultRootFolderPath = QDir::homePath();
|
||||
}
|
||||
|
||||
m_currentNotebookRootFolderPath = readString(coreObj,
|
||||
QStringLiteral("current_notebook_root_folder_path"));
|
||||
|
||||
{
|
||||
auto option = readString(coreObj, QStringLiteral("opengl"));
|
||||
m_openGL = stringToOpenGL(option);
|
||||
}
|
||||
|
||||
m_systemTitleBarEnabled = readBool(coreObj, QStringLiteral("system_title_bar"));
|
||||
}
|
||||
|
||||
QJsonObject SessionConfig::saveCore() const
|
||||
{
|
||||
QJsonObject coreObj;
|
||||
coreObj[QStringLiteral("new_notebook_default_root_folder_path")] = m_newNotebookDefaultRootFolderPath;
|
||||
coreObj[QStringLiteral("current_notebook_root_folder_path")] = m_currentNotebookRootFolderPath;
|
||||
coreObj[QStringLiteral("opengl")] = openGLToString(m_openGL);
|
||||
coreObj[QStringLiteral("system_title_bar")] = m_systemTitleBarEnabled;
|
||||
return coreObj;
|
||||
}
|
||||
|
||||
const QString &SessionConfig::getNewNotebookDefaultRootFolderPath() const
|
||||
{
|
||||
return m_newNotebookDefaultRootFolderPath;
|
||||
}
|
||||
|
||||
void SessionConfig::setNewNotebookDefaultRootFolderPath(const QString &p_path)
|
||||
{
|
||||
updateConfig(m_newNotebookDefaultRootFolderPath,
|
||||
p_path,
|
||||
this);
|
||||
}
|
||||
|
||||
const QVector<SessionConfig::NotebookItem> &SessionConfig::getNotebooks()
|
||||
{
|
||||
if (m_notebooks.isEmpty()) {
|
||||
auto mgr = getMgr();
|
||||
auto sessionSettings = mgr->getSettings(ConfigMgr::Source::Session);
|
||||
const auto &sessionJobj = sessionSettings->getJson();
|
||||
loadNotebooks(sessionJobj);
|
||||
}
|
||||
return m_notebooks;
|
||||
}
|
||||
|
||||
void SessionConfig::setNotebooks(const QVector<SessionConfig::NotebookItem> &p_notebooks)
|
||||
{
|
||||
updateConfig(m_notebooks,
|
||||
p_notebooks,
|
||||
this);
|
||||
}
|
||||
|
||||
void SessionConfig::loadNotebooks(const QJsonObject &p_session)
|
||||
{
|
||||
const auto notebooksJson = p_session.value(QStringLiteral("notebooks")).toArray();
|
||||
m_notebooks.resize(notebooksJson.size());
|
||||
for (int i = 0; i < notebooksJson.size(); ++i) {
|
||||
m_notebooks[i].fromJson(notebooksJson[i].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray SessionConfig::saveNotebooks() const
|
||||
{
|
||||
QJsonArray nbArray;
|
||||
for (const auto &nb : m_notebooks) {
|
||||
nbArray.append(nb.toJson());
|
||||
}
|
||||
return nbArray;
|
||||
}
|
||||
|
||||
const QString &SessionConfig::getCurrentNotebookRootFolderPath() const
|
||||
{
|
||||
return m_currentNotebookRootFolderPath;
|
||||
}
|
||||
|
||||
void SessionConfig::setCurrentNotebookRootFolderPath(const QString &p_path)
|
||||
{
|
||||
updateConfig(m_currentNotebookRootFolderPath,
|
||||
p_path,
|
||||
this);
|
||||
}
|
||||
|
||||
void SessionConfig::writeToSettings() const
|
||||
{
|
||||
getMgr()->writeSessionSettings(toJson());
|
||||
}
|
||||
|
||||
QJsonObject SessionConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("core")] = saveCore();
|
||||
obj[QStringLiteral("notebooks")] = saveNotebooks();
|
||||
obj[QStringLiteral("state_geometry")] = saveStateAndGeometry();
|
||||
return obj;
|
||||
}
|
||||
|
||||
QJsonObject SessionConfig::saveStateAndGeometry() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
writeByteArray(obj, QStringLiteral("main_window_state"), m_mainWindowStateGeometry.m_mainState);
|
||||
writeByteArray(obj, QStringLiteral("main_window_geometry"), m_mainWindowStateGeometry.m_mainGeometry);
|
||||
return obj;
|
||||
}
|
||||
|
||||
SessionConfig::MainWindowStateGeometry SessionConfig::getMainWindowStateGeometry() const
|
||||
{
|
||||
auto sessionSettings = getMgr()->getSettings(ConfigMgr::Source::Session);
|
||||
const auto &sessionJobj = sessionSettings->getJson();
|
||||
const auto obj = sessionJobj.value(QStringLiteral("state_geometry")).toObject();
|
||||
|
||||
MainWindowStateGeometry sg;
|
||||
sg.m_mainState = readByteArray(obj, QStringLiteral("main_window_state"));
|
||||
sg.m_mainGeometry = readByteArray(obj, QStringLiteral("main_window_geometry"));
|
||||
|
||||
return sg;
|
||||
}
|
||||
|
||||
void SessionConfig::setMainWindowStateGeometry(const SessionConfig::MainWindowStateGeometry &p_state)
|
||||
{
|
||||
m_mainWindowStateGeometry = p_state;
|
||||
++m_revision;
|
||||
writeToSettings();
|
||||
}
|
||||
|
||||
SessionConfig::OpenGL SessionConfig::getOpenGLAtBootstrap()
|
||||
{
|
||||
auto userConfigFile = ConfigMgr::locateSessionConfigFilePathAtBootstrap();
|
||||
if (!userConfigFile.isEmpty()) {
|
||||
auto bytes = FileUtils::readFile(userConfigFile);
|
||||
auto obj = QJsonDocument::fromJson(bytes).object();
|
||||
auto coreObj = obj.value(QStringLiteral("core")).toObject();
|
||||
auto str = coreObj.value(QStringLiteral("opengl")).toString();
|
||||
return stringToOpenGL(str);
|
||||
}
|
||||
|
||||
return OpenGL::None;
|
||||
}
|
||||
|
||||
SessionConfig::OpenGL SessionConfig::getOpenGL() const
|
||||
{
|
||||
return m_openGL;
|
||||
}
|
||||
|
||||
void SessionConfig::setOpenGL(OpenGL p_option)
|
||||
{
|
||||
updateConfig(m_openGL, p_option, this);
|
||||
}
|
||||
|
||||
QString SessionConfig::openGLToString(OpenGL p_option)
|
||||
{
|
||||
switch (p_option) {
|
||||
case OpenGL::Desktop:
|
||||
return QStringLiteral("desktop");
|
||||
|
||||
case OpenGL::Angle:
|
||||
return QStringLiteral("angle");
|
||||
|
||||
case OpenGL::Software:
|
||||
return QStringLiteral("software");
|
||||
|
||||
default:
|
||||
return QStringLiteral("none");
|
||||
}
|
||||
}
|
||||
|
||||
SessionConfig::OpenGL SessionConfig::stringToOpenGL(const QString &p_str)
|
||||
{
|
||||
auto option = p_str.toLower();
|
||||
if (option == QStringLiteral("software")) {
|
||||
return OpenGL::Software;
|
||||
} else if (option == QStringLiteral("desktop")) {
|
||||
return OpenGL::Desktop;
|
||||
} else if (option == QStringLiteral("angle")) {
|
||||
return OpenGL::Angle;
|
||||
} else {
|
||||
return OpenGL::None;
|
||||
}
|
||||
}
|
||||
|
||||
bool SessionConfig::getSystemTitleBarEnabled() const
|
||||
{
|
||||
return m_systemTitleBarEnabled;
|
||||
}
|
||||
|
||||
void SessionConfig::setSystemTitleBarEnabled(bool p_enabled)
|
||||
{
|
||||
updateConfig(m_systemTitleBarEnabled, p_enabled, this);
|
||||
}
|
111
src/core/sessionconfig.h
Normal file
111
src/core/sessionconfig.h
Normal file
@ -0,0 +1,111 @@
|
||||
#ifndef SESSIONCONFIG_H
|
||||
#define SESSIONCONFIG_H
|
||||
|
||||
#include "iconfig.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class SessionConfig : public IConfig
|
||||
{
|
||||
public:
|
||||
struct NotebookItem
|
||||
{
|
||||
NotebookItem() = default;
|
||||
|
||||
bool operator==(const NotebookItem &p_other) const;
|
||||
|
||||
void fromJson(const QJsonObject &p_jobj);
|
||||
|
||||
QJsonObject toJson() const;
|
||||
|
||||
QString m_type;
|
||||
QString m_rootFolderPath;
|
||||
QString m_backend;
|
||||
};
|
||||
|
||||
struct MainWindowStateGeometry
|
||||
{
|
||||
bool operator==(const MainWindowStateGeometry &p_other) const
|
||||
{
|
||||
return m_mainState == p_other.m_mainState
|
||||
&& m_mainGeometry == p_other.m_mainGeometry;
|
||||
}
|
||||
|
||||
QByteArray m_mainState;
|
||||
QByteArray m_mainGeometry;
|
||||
};
|
||||
|
||||
enum OpenGL
|
||||
{
|
||||
None,
|
||||
Desktop,
|
||||
Angle,
|
||||
Software
|
||||
};
|
||||
|
||||
explicit SessionConfig(ConfigMgr *p_mgr);
|
||||
|
||||
~SessionConfig();
|
||||
|
||||
void init() Q_DECL_OVERRIDE;
|
||||
|
||||
const QString &getNewNotebookDefaultRootFolderPath() const;
|
||||
void setNewNotebookDefaultRootFolderPath(const QString &p_path);
|
||||
|
||||
const QString &getCurrentNotebookRootFolderPath() const;
|
||||
void setCurrentNotebookRootFolderPath(const QString &p_path);
|
||||
|
||||
const QVector<SessionConfig::NotebookItem> &getNotebooks();
|
||||
void setNotebooks(const QVector<SessionConfig::NotebookItem> &p_notebooks);
|
||||
|
||||
void writeToSettings() const Q_DECL_OVERRIDE;
|
||||
|
||||
QJsonObject toJson() const Q_DECL_OVERRIDE;
|
||||
|
||||
SessionConfig::MainWindowStateGeometry getMainWindowStateGeometry() const;
|
||||
void setMainWindowStateGeometry(const SessionConfig::MainWindowStateGeometry &p_state);
|
||||
|
||||
OpenGL getOpenGL() const;
|
||||
void setOpenGL(OpenGL p_option);
|
||||
|
||||
bool getSystemTitleBarEnabled() const;
|
||||
void setSystemTitleBarEnabled(bool p_enabled);
|
||||
|
||||
static OpenGL getOpenGLAtBootstrap();
|
||||
|
||||
static QString openGLToString(OpenGL p_option);
|
||||
static OpenGL stringToOpenGL(const QString &p_str);
|
||||
|
||||
private:
|
||||
void loadCore(const QJsonObject &p_session);
|
||||
|
||||
QJsonObject saveCore() const;
|
||||
|
||||
void loadNotebooks(const QJsonObject &p_session);
|
||||
|
||||
QJsonArray saveNotebooks() const;
|
||||
|
||||
QJsonObject saveStateAndGeometry() const;
|
||||
|
||||
QString m_newNotebookDefaultRootFolderPath;
|
||||
|
||||
// Use root folder to identify a notebook uniquely.
|
||||
QString m_currentNotebookRootFolderPath;
|
||||
|
||||
QVector<SessionConfig::NotebookItem> m_notebooks;
|
||||
|
||||
// Used to store newly-set state and geometry, since there is no need to store the read-in
|
||||
// data all the time.
|
||||
MainWindowStateGeometry m_mainWindowStateGeometry;
|
||||
|
||||
OpenGL m_openGL = OpenGL::None;
|
||||
|
||||
// Whether use system's title bar or not.
|
||||
bool m_systemTitleBarEnabled = false;
|
||||
};
|
||||
} // ns vnotex
|
||||
|
||||
#endif // SESSIONCONFIG_H
|
184
src/core/singleinstanceguard.cpp
Normal file
184
src/core/singleinstanceguard.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
#include "singleinstanceguard.h"
|
||||
#include <QDebug>
|
||||
|
||||
#include <utils/utils.h>
|
||||
|
||||
using namespace vnotex;
|
||||
|
||||
const QString SingleInstanceGuard::c_memKey = "vnotex_shared_memory";
|
||||
const int SingleInstanceGuard::c_magic = 376686683;
|
||||
|
||||
SingleInstanceGuard::SingleInstanceGuard()
|
||||
: m_online(false),
|
||||
m_sharedMemory(c_memKey)
|
||||
{
|
||||
}
|
||||
|
||||
bool SingleInstanceGuard::tryRun()
|
||||
{
|
||||
m_online = false;
|
||||
|
||||
// If we can attach to the sharedmemory, there is another instance running.
|
||||
// In Linux, crashes may cause the shared memory segment remains. In this case,
|
||||
// this will attach to the old segment, then exit, freeing the old segment.
|
||||
if (m_sharedMemory.attach()) {
|
||||
qInfo() << "another instance is running";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to create it.
|
||||
bool ret = m_sharedMemory.create(sizeof(SharedStruct));
|
||||
if (ret) {
|
||||
// We created it.
|
||||
m_sharedMemory.lock();
|
||||
SharedStruct *str = (SharedStruct *)m_sharedMemory.data();
|
||||
str->m_magic = c_magic;
|
||||
str->m_filesBufIdx = 0;
|
||||
str->m_askedToShow = false;
|
||||
m_sharedMemory.unlock();
|
||||
|
||||
m_online = true;
|
||||
return true;
|
||||
} else {
|
||||
qCritical() << "fail to create shared memory segment";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SingleInstanceGuard::openExternalFiles(const QStringList &p_files)
|
||||
{
|
||||
if (p_files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_sharedMemory.isAttached()) {
|
||||
if (!m_sharedMemory.attach()) {
|
||||
qCritical() << "fail to attach to the shared memory segment"
|
||||
<< (m_sharedMemory.error() ? m_sharedMemory.errorString() : "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
int tryCount = 100;
|
||||
while (tryCount--) {
|
||||
m_sharedMemory.lock();
|
||||
SharedStruct *str = (SharedStruct *)m_sharedMemory.data();
|
||||
V_ASSERT(str->m_magic == c_magic);
|
||||
for (; idx < p_files.size(); ++idx) {
|
||||
if (p_files[idx].size() + 1 > FilesBufCount) {
|
||||
// Skip this long long name file.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!appendFileToBuffer(str, p_files[idx])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_sharedMemory.unlock();
|
||||
|
||||
if (idx < p_files.size()) {
|
||||
Utils::sleepWait(500);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SingleInstanceGuard::appendFileToBuffer(SharedStruct *p_str, const QString &p_file)
|
||||
{
|
||||
if (p_file.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int strSize = p_file.size();
|
||||
if (strSize + 1 > FilesBufCount - p_str->m_filesBufIdx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Put the size first.
|
||||
p_str->m_filesBuf[p_str->m_filesBufIdx++] = (ushort)strSize;
|
||||
const QChar *data = p_file.constData();
|
||||
for (int i = 0; i < strSize; ++i) {
|
||||
p_str->m_filesBuf[p_str->m_filesBufIdx++] = data[i].unicode();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList SingleInstanceGuard::fetchFilesToOpen()
|
||||
{
|
||||
QStringList files;
|
||||
|
||||
if (!m_online) {
|
||||
return files;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_sharedMemory.isAttached());
|
||||
m_sharedMemory.lock();
|
||||
SharedStruct *str = (SharedStruct *)m_sharedMemory.data();
|
||||
V_ASSERT(str->m_magic == c_magic);
|
||||
Q_ASSERT(str->m_filesBufIdx <= FilesBufCount);
|
||||
int idx = 0;
|
||||
while (idx < str->m_filesBufIdx) {
|
||||
int strSize = str->m_filesBuf[idx++];
|
||||
Q_ASSERT(strSize <= str->m_filesBufIdx - idx);
|
||||
QString file;
|
||||
for (int i = 0; i < strSize; ++i) {
|
||||
file.append(QChar(str->m_filesBuf[idx++]));
|
||||
}
|
||||
|
||||
files.append(file);
|
||||
}
|
||||
|
||||
str->m_filesBufIdx = 0;
|
||||
m_sharedMemory.unlock();
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
void SingleInstanceGuard::showInstance()
|
||||
{
|
||||
if (!m_sharedMemory.isAttached()) {
|
||||
if (!m_sharedMemory.attach()) {
|
||||
qCritical() << "fail to attach to the shared memory segment"
|
||||
<< (m_sharedMemory.error() ? m_sharedMemory.errorString() : "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_sharedMemory.lock();
|
||||
SharedStruct *str = (SharedStruct *)m_sharedMemory.data();
|
||||
V_ASSERT(str->m_magic == c_magic);
|
||||
str->m_askedToShow = true;
|
||||
m_sharedMemory.unlock();
|
||||
}
|
||||
|
||||
bool SingleInstanceGuard::fetchAskedToShow()
|
||||
{
|
||||
if (!m_online) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_sharedMemory.isAttached());
|
||||
m_sharedMemory.lock();
|
||||
SharedStruct *str = (SharedStruct *)m_sharedMemory.data();
|
||||
V_ASSERT(str->m_magic == c_magic);
|
||||
bool ret = str->m_askedToShow;
|
||||
str->m_askedToShow = false;
|
||||
m_sharedMemory.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SingleInstanceGuard::exit()
|
||||
{
|
||||
if (!m_online) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_sharedMemory.isAttached());
|
||||
m_sharedMemory.detach();
|
||||
m_online = false;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user