mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 14:29:54 +08:00
support backup file
Add configs: - backup_directory - backup_extension - enable_backup_file
This commit is contained in:
parent
141b404240
commit
e6ce66ec7d
@ -116,7 +116,7 @@ tool_bar_icon_size=18
|
|||||||
; Markdown-it options
|
; Markdown-it options
|
||||||
; Enable HTML tags in source
|
; Enable HTML tags in source
|
||||||
markdownit_opt_html=true
|
markdownit_opt_html=true
|
||||||
; Convert '\n' in paragraphs into <br>
|
; Convert '\n' in paragraphs into <br/>
|
||||||
markdownit_opt_breaks=false
|
markdownit_opt_breaks=false
|
||||||
; Auto-convert URL-like text to links
|
; Auto-convert URL-like text to links
|
||||||
markdownit_opt_linkify=true
|
markdownit_opt_linkify=true
|
||||||
@ -155,6 +155,16 @@ startup_pages=
|
|||||||
; Timer interval to check file modification or save file to tmp file in milliseconds
|
; Timer interval to check file modification or save file to tmp file in milliseconds
|
||||||
file_timer_interval=2000
|
file_timer_interval=2000
|
||||||
|
|
||||||
|
; Directory for the backup file
|
||||||
|
; A directory "." means to put the backup file in the same directory as the edited file
|
||||||
|
backup_directory=.
|
||||||
|
|
||||||
|
; String which is appended to a file name to make the name of the backup file
|
||||||
|
backup_extension=.vswp
|
||||||
|
|
||||||
|
; Enable back file
|
||||||
|
enable_backup_file=true
|
||||||
|
|
||||||
[web]
|
[web]
|
||||||
; Location and configuration for Mathjax
|
; Location and configuration for Mathjax
|
||||||
mathjax_javascript=https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML
|
mathjax_javascript=https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML
|
||||||
|
@ -891,7 +891,14 @@ bool VUtils::deleteFile(const VNotebook *p_notebook,
|
|||||||
bool VUtils::deleteFile(const QString &p_path)
|
bool VUtils::deleteFile(const QString &p_path)
|
||||||
{
|
{
|
||||||
QFile file(p_path);
|
QFile file(p_path);
|
||||||
return file.remove();
|
bool ret = file.remove();
|
||||||
|
if (ret) {
|
||||||
|
qDebug() << "deleted file" << p_path;
|
||||||
|
} else {
|
||||||
|
qWarning() << "fail to delete file" << p_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VUtils::deleteFile(const VOrphanFile *p_file,
|
bool VUtils::deleteFile(const VOrphanFile *p_file,
|
||||||
|
@ -268,6 +268,18 @@ void VConfigManager::initialize()
|
|||||||
if (m_fileTimerInterval < 100) {
|
if (m_fileTimerInterval < 100) {
|
||||||
m_fileTimerInterval = 100;
|
m_fileTimerInterval = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_backupDirectory = getConfigFromSettings("global",
|
||||||
|
"backup_directory").toString();
|
||||||
|
|
||||||
|
m_backupExtension = getConfigFromSettings("global",
|
||||||
|
"backup_extension").toString();
|
||||||
|
if (m_backupExtension.isEmpty()) {
|
||||||
|
m_backupExtension = ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_enableBackupFile = getConfigFromSettings("global",
|
||||||
|
"enable_backup_file").toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VConfigManager::initSettings()
|
void VConfigManager::initSettings()
|
||||||
|
@ -375,6 +375,15 @@ public:
|
|||||||
// Return the timer interval for checking file.
|
// Return the timer interval for checking file.
|
||||||
int getFileTimerInterval() const;
|
int getFileTimerInterval() const;
|
||||||
|
|
||||||
|
// Get the backup directory.
|
||||||
|
const QString &getBackupDirectory() const;
|
||||||
|
|
||||||
|
// Get the backup file extension.
|
||||||
|
const QString &getBackupExtension() const;
|
||||||
|
|
||||||
|
// Whether backup file is enabled.
|
||||||
|
bool getEnableBackupFile() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Look up a config from user and default settings.
|
// Look up a config from user and default settings.
|
||||||
QVariant getConfigFromSettings(const QString §ion, const QString &key) const;
|
QVariant getConfigFromSettings(const QString §ion, const QString &key) const;
|
||||||
@ -709,6 +718,15 @@ private:
|
|||||||
// Timer interval to check file in milliseconds.
|
// Timer interval to check file in milliseconds.
|
||||||
int m_fileTimerInterval;
|
int m_fileTimerInterval;
|
||||||
|
|
||||||
|
// Directory for the backup file (relative or absolute path).
|
||||||
|
QString m_backupDirectory;
|
||||||
|
|
||||||
|
// Extension of the backup file.
|
||||||
|
QString m_backupExtension;
|
||||||
|
|
||||||
|
// Whether enable backup file.
|
||||||
|
bool m_enableBackupFile;
|
||||||
|
|
||||||
// The name of the config file in each directory, obsolete.
|
// The name of the config file in each directory, obsolete.
|
||||||
// Use c_dirConfigFile instead.
|
// Use c_dirConfigFile instead.
|
||||||
static const QString c_obsoleteDirConfigFile;
|
static const QString c_obsoleteDirConfigFile;
|
||||||
@ -1790,4 +1808,19 @@ inline int VConfigManager::getFileTimerInterval() const
|
|||||||
return m_fileTimerInterval;
|
return m_fileTimerInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getBackupDirectory() const
|
||||||
|
{
|
||||||
|
return m_backupDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getBackupExtension() const
|
||||||
|
{
|
||||||
|
return m_backupExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool VConfigManager::getEnableBackupFile() const
|
||||||
|
{
|
||||||
|
return m_enableBackupFile;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // VCONFIGMANAGER_H
|
#endif // VCONFIGMANAGER_H
|
||||||
|
@ -139,6 +139,11 @@ public:
|
|||||||
|
|
||||||
void setVimMode(VimMode p_mode);
|
void setVimMode(VimMode p_mode);
|
||||||
|
|
||||||
|
virtual QString getContent() const = 0;
|
||||||
|
|
||||||
|
// @p_modified: if true, delete the whole content and insert the new content.
|
||||||
|
virtual void setContent(const QString &p_content, bool p_modified = false) = 0;
|
||||||
|
|
||||||
// Wrapper functions for QPlainTextEdit/QTextEdit.
|
// Wrapper functions for QPlainTextEdit/QTextEdit.
|
||||||
// Ends with W to distinguish it from the original interfaces.
|
// Ends with W to distinguish it from the original interfaces.
|
||||||
public:
|
public:
|
||||||
|
@ -15,7 +15,9 @@ VEditTab::VEditTab(VFile *p_file, VEditArea *p_editArea, QWidget *p_parent)
|
|||||||
m_currentHeader(p_file, -1),
|
m_currentHeader(p_file, -1),
|
||||||
m_editArea(p_editArea),
|
m_editArea(p_editArea),
|
||||||
m_checkFileChange(true),
|
m_checkFileChange(true),
|
||||||
m_fileDiverged(false)
|
m_fileDiverged(false),
|
||||||
|
m_ready(0),
|
||||||
|
m_enableBackupFile(g_config->getEnableBackupFile())
|
||||||
{
|
{
|
||||||
connect(qApp, &QApplication::focusChanged,
|
connect(qApp, &QApplication::focusChanged,
|
||||||
this, &VEditTab::handleFocusChanged);
|
this, &VEditTab::handleFocusChanged);
|
||||||
@ -181,3 +183,7 @@ void VEditTab::reloadFromDisk()
|
|||||||
m_checkFileChange = true;
|
m_checkFileChange = true;
|
||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditTab::writeBackupFile()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -124,6 +124,9 @@ protected:
|
|||||||
// Return true if succeed.
|
// Return true if succeed.
|
||||||
virtual bool restoreFromTabInfo(const VEditTabInfo &p_info) = 0;
|
virtual bool restoreFromTabInfo(const VEditTabInfo &p_info) = 0;
|
||||||
|
|
||||||
|
// Write modified buffer content to backup file.
|
||||||
|
virtual void writeBackupFile();
|
||||||
|
|
||||||
// File related to this tab.
|
// File related to this tab.
|
||||||
QPointer<VFile> m_file;
|
QPointer<VFile> m_file;
|
||||||
|
|
||||||
@ -146,6 +149,12 @@ protected:
|
|||||||
// File has diverged from disk.
|
// File has diverged from disk.
|
||||||
bool m_fileDiverged;
|
bool m_fileDiverged;
|
||||||
|
|
||||||
|
// Tab has been ready or not.
|
||||||
|
int m_ready;
|
||||||
|
|
||||||
|
// Whether backup file is enabled.
|
||||||
|
bool m_enableBackupFile;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void getFocused();
|
void getFocused();
|
||||||
|
|
||||||
@ -161,6 +170,9 @@ signals:
|
|||||||
|
|
||||||
void vimStatusUpdated(const VVim *p_vim);
|
void vimStatusUpdated(const VVim *p_vim);
|
||||||
|
|
||||||
|
// Request to close itself.
|
||||||
|
void closeRequested(VEditTab *p_tab);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Called when app focus changed.
|
// Called when app focus changed.
|
||||||
void handleFocusChanged(QWidget *p_old, QWidget *p_now);
|
void handleFocusChanged(QWidget *p_old, QWidget *p_now);
|
||||||
|
@ -960,6 +960,8 @@ void VEditWindow::connectEditTab(const VEditTab *p_tab)
|
|||||||
this, &VEditWindow::handleTabStatusMessage);
|
this, &VEditWindow::handleTabStatusMessage);
|
||||||
connect(p_tab, &VEditTab::vimStatusUpdated,
|
connect(p_tab, &VEditTab::vimStatusUpdated,
|
||||||
this, &VEditWindow::handleTabVimStatusUpdated);
|
this, &VEditWindow::handleTabVimStatusUpdated);
|
||||||
|
connect(p_tab, &VEditTab::closeRequested,
|
||||||
|
this, &VEditWindow::tabRequestToClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditWindow::setCurrentWindow(bool p_current)
|
void VEditWindow::setCurrentWindow(bool p_current)
|
||||||
@ -1094,3 +1096,16 @@ void VEditWindow::checkFileChangeOutside()
|
|||||||
getTab(i)->checkFileChangeOutside();
|
getTab(i)->checkFileChangeOutside();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditWindow::tabRequestToClose(VEditTab *p_tab)
|
||||||
|
{
|
||||||
|
bool ok = p_tab->closeFile(false);
|
||||||
|
if (ok) {
|
||||||
|
removeTab(indexOf(p_tab));
|
||||||
|
|
||||||
|
// Disconnect all the signals.
|
||||||
|
disconnect(p_tab, 0, this, 0);
|
||||||
|
|
||||||
|
p_tab->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -138,6 +138,9 @@ private slots:
|
|||||||
// Handle the statusUpdated signal of VEditTab.
|
// Handle the statusUpdated signal of VEditTab.
|
||||||
void handleTabStatusUpdated(const VEditTabInfo &p_info);
|
void handleTabStatusUpdated(const VEditTabInfo &p_info);
|
||||||
|
|
||||||
|
// @p_tab request to close itself.
|
||||||
|
void tabRequestToClose(VEditTab *p_tab);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initTabActions();
|
void initTabActions();
|
||||||
void setupCornerWidget();
|
void setupCornerWidget();
|
||||||
|
@ -3,7 +3,15 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
|
#include "vconfigmanager.h"
|
||||||
|
|
||||||
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
const QString VFile::c_backupFileHeadMagic = "vnote_backup_file_826537664";
|
||||||
|
|
||||||
VFile::VFile(QObject *p_parent,
|
VFile::VFile(QObject *p_parent,
|
||||||
const QString &p_name,
|
const QString &p_name,
|
||||||
@ -50,6 +58,11 @@ void VFile::close()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_content.clear();
|
m_content.clear();
|
||||||
|
if (!m_backupName.isEmpty()) {
|
||||||
|
VUtils::deleteFile(fetchBackupFilePath());
|
||||||
|
m_backupName.clear();
|
||||||
|
}
|
||||||
|
|
||||||
m_opened = false;
|
m_opened = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +70,7 @@ bool VFile::save()
|
|||||||
{
|
{
|
||||||
Q_ASSERT(m_opened);
|
Q_ASSERT(m_opened);
|
||||||
Q_ASSERT(m_modifiable);
|
Q_ASSERT(m_modifiable);
|
||||||
|
|
||||||
bool ret = VUtils::writeFileToDisk(fetchPath(), m_content);
|
bool ret = VUtils::writeFileToDisk(fetchPath(), m_content);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
m_lastModified = QFileInfo(fetchPath()).lastModified();
|
m_lastModified = QFileInfo(fetchPath()).lastModified();
|
||||||
@ -110,3 +124,87 @@ void VFile::reload()
|
|||||||
m_content = VUtils::readFileFromDisk(filePath);
|
m_content = VUtils::readFileFromDisk(filePath);
|
||||||
m_lastModified = QFileInfo(filePath).lastModified();
|
m_lastModified = QFileInfo(filePath).lastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VFile::backupFileOfPreviousSession() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_modifiable && m_backupName.isEmpty());
|
||||||
|
|
||||||
|
QString basePath = QDir(fetchBasePath()).filePath(g_config->getBackupDirectory());
|
||||||
|
QDir dir(basePath);
|
||||||
|
|
||||||
|
QStringList files = getPotentialBackupFiles(basePath);
|
||||||
|
foreach (const QString &file, files) {
|
||||||
|
QString filePath = dir.filePath(file);
|
||||||
|
if (isBackupFile(filePath)) {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VFile::fetchBackupFilePath()
|
||||||
|
{
|
||||||
|
QString basePath = QDir(fetchBasePath()).filePath(g_config->getBackupDirectory());
|
||||||
|
QDir dir(basePath);
|
||||||
|
|
||||||
|
if (m_backupName.isEmpty()) {
|
||||||
|
m_backupName = VUtils::getFileNameWithSequence(basePath,
|
||||||
|
m_name + g_config->getBackupExtension(),
|
||||||
|
true);
|
||||||
|
|
||||||
|
m_lastBackupFilePath = dir.filePath(m_backupName);
|
||||||
|
} else {
|
||||||
|
QString filePath = dir.filePath(m_backupName);
|
||||||
|
if (filePath != m_lastBackupFilePath) {
|
||||||
|
// File has been moved.
|
||||||
|
// Delete the original backup file if it still exists.
|
||||||
|
VUtils::deleteFile(m_lastBackupFilePath);
|
||||||
|
|
||||||
|
m_lastBackupFilePath = filePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_lastBackupFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList VFile::getPotentialBackupFiles(const QString &p_dir) const
|
||||||
|
{
|
||||||
|
QString nameFilter = QString("%1*%2").arg(m_name).arg(g_config->getBackupExtension());
|
||||||
|
QStringList files = QDir(p_dir).entryList(QStringList(nameFilter),
|
||||||
|
QDir::Files
|
||||||
|
| QDir::Hidden
|
||||||
|
| QDir::NoSymLinks
|
||||||
|
| QDir::NoDotAndDotDot);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VFile::isBackupFile(const QString &p_file) const
|
||||||
|
{
|
||||||
|
QFile file(p_file);
|
||||||
|
if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream st(&file);
|
||||||
|
QString head = st.readLine();
|
||||||
|
return head == fetchBackupFileHead();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VFile::fetchBackupFileHead() const
|
||||||
|
{
|
||||||
|
return c_backupFileHeadMagic + " " + fetchPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VFile::writeBackupFile(const QString &p_content)
|
||||||
|
{
|
||||||
|
return VUtils::writeFileToDisk(fetchBackupFilePath(),
|
||||||
|
fetchBackupFileHead() + "\n" + p_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VFile::readBackupFile(const QString &p_file)
|
||||||
|
{
|
||||||
|
const QString content = VUtils::readFileFromDisk(p_file);
|
||||||
|
int idx = content.indexOf("\n");
|
||||||
|
return content.mid(idx + 1);
|
||||||
|
}
|
||||||
|
27
src/vfile.h
27
src/vfile.h
@ -79,6 +79,14 @@ public:
|
|||||||
// Whether this file was changed outside VNote.
|
// Whether this file was changed outside VNote.
|
||||||
bool isChangedOutside() const;
|
bool isChangedOutside() const;
|
||||||
|
|
||||||
|
// Return backup file of previous session if there exists one.
|
||||||
|
QString backupFileOfPreviousSession() const;
|
||||||
|
|
||||||
|
// Write @p_content to backup file.
|
||||||
|
bool writeBackupFile(const QString &p_content);
|
||||||
|
|
||||||
|
QString readBackupFile(const QString &p_file);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Name of this file.
|
// Name of this file.
|
||||||
QString m_name;
|
QString m_name;
|
||||||
@ -107,6 +115,25 @@ protected:
|
|||||||
// Last modified date and local time when the file is last modified
|
// Last modified date and local time when the file is last modified
|
||||||
// corresponding to m_content.
|
// corresponding to m_content.
|
||||||
QDateTime m_lastModified;
|
QDateTime m_lastModified;
|
||||||
|
|
||||||
|
// Name of the backup file.
|
||||||
|
QString m_backupName;
|
||||||
|
|
||||||
|
// Used to identify file path change.
|
||||||
|
QString m_lastBackupFilePath;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Fetch backup file path.
|
||||||
|
QString fetchBackupFilePath();
|
||||||
|
|
||||||
|
QStringList getPotentialBackupFiles(const QString &p_dir) const;
|
||||||
|
|
||||||
|
// Read the file content to check if it is a backup file.
|
||||||
|
bool isBackupFile(const QString &p_file) const;
|
||||||
|
|
||||||
|
QString fetchBackupFileHead() const;
|
||||||
|
|
||||||
|
static const QString c_backupFileHeadMagic;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const QString &VFile::getName() const
|
inline const QString &VFile::getName() const
|
||||||
|
@ -59,6 +59,11 @@ VMdEditor::VMdEditor(VFile *p_file,
|
|||||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
|
connect(m_mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
makeBlockVisible(textCursor().block());
|
makeBlockVisible(textCursor().block());
|
||||||
|
|
||||||
|
if (m_freshEdit) {
|
||||||
|
m_freshEdit = false;
|
||||||
|
emit m_object->ready();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_cbHighlighter = new VCodeBlockHighlightHelper(m_mdHighlighter,
|
m_cbHighlighter = new VCodeBlockHighlightHelper(m_mdHighlighter,
|
||||||
@ -106,11 +111,6 @@ void VMdEditor::beginEdit()
|
|||||||
emit statusChanged();
|
emit statusChanged();
|
||||||
|
|
||||||
updateHeaders(m_mdHighlighter->getHeaderRegions());
|
updateHeaders(m_mdHighlighter->getHeaderRegions());
|
||||||
|
|
||||||
if (m_freshEdit) {
|
|
||||||
m_freshEdit = false;
|
|
||||||
emit m_object->ready();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEditor::endEdit()
|
void VMdEditor::endEdit()
|
||||||
@ -161,14 +161,14 @@ void VMdEditor::makeBlockVisible(const QTextBlock &p_block)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QScrollBar *vbar = verticalScrollBar();
|
QScrollBar *vbar = verticalScrollBar();
|
||||||
if (!vbar || !vbar->isVisible()) {
|
if (!vbar || (vbar->minimum() == vbar->maximum())) {
|
||||||
// No vertical scrollbar. No need to scroll.
|
// No vertical scrollbar. No need to scroll.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int height = rect().height();
|
int height = rect().height();
|
||||||
QScrollBar *hbar = horizontalScrollBar();
|
QScrollBar *hbar = horizontalScrollBar();
|
||||||
if (hbar && hbar->isVisible()) {
|
if (hbar && (hbar->minimum() != hbar->maximum())) {
|
||||||
height -= hbar->height();
|
height -= hbar->height();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,3 +870,20 @@ void VMdEditor::updateConfig()
|
|||||||
updateEditConfig();
|
updateEditConfig();
|
||||||
updateTextEditConfig();
|
updateTextEditConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VMdEditor::getContent() const
|
||||||
|
{
|
||||||
|
return toPlainText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::setContent(const QString &p_content, bool p_modified)
|
||||||
|
{
|
||||||
|
if (p_modified) {
|
||||||
|
QTextCursor cursor = textCursor();
|
||||||
|
cursor.select(QTextCursor::Document);
|
||||||
|
cursor.insertText(p_content);
|
||||||
|
setTextCursor(cursor);
|
||||||
|
} else {
|
||||||
|
setPlainText(p_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -55,6 +55,10 @@ public:
|
|||||||
|
|
||||||
void updateConfig() Q_DECL_OVERRIDE;
|
void updateConfig() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
QString getContent() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void setContent(const QString &p_content, bool p_modified = false) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
|
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
141
src/vmdtab.cpp
141
src/vmdtab.cpp
@ -33,7 +33,8 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
|||||||
m_webViewer(NULL),
|
m_webViewer(NULL),
|
||||||
m_document(NULL),
|
m_document(NULL),
|
||||||
m_mdConType(g_config->getMdConverterType()),
|
m_mdConType(g_config->getMdConverterType()),
|
||||||
m_enableHeadingSequence(false)
|
m_enableHeadingSequence(false),
|
||||||
|
m_backupFileChecked(false)
|
||||||
{
|
{
|
||||||
V_ASSERT(m_file->getDocType() == DocType::Markdown);
|
V_ASSERT(m_file->getDocType() == DocType::Markdown);
|
||||||
|
|
||||||
@ -49,6 +50,14 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
|||||||
|
|
||||||
setupUI();
|
setupUI();
|
||||||
|
|
||||||
|
m_backupTimer = new QTimer(this);
|
||||||
|
m_backupTimer->setSingleShot(true);
|
||||||
|
m_backupTimer->setInterval(g_config->getFileTimerInterval());
|
||||||
|
connect(m_backupTimer, &QTimer::timeout,
|
||||||
|
this, [this]() {
|
||||||
|
writeBackupFile();
|
||||||
|
});
|
||||||
|
|
||||||
if (p_mode == OpenFileMode::Edit) {
|
if (p_mode == OpenFileMode::Edit) {
|
||||||
showFileEditMode();
|
showFileEditMode();
|
||||||
} else {
|
} else {
|
||||||
@ -325,9 +334,13 @@ bool VMdTab::saveFile()
|
|||||||
m_editor->saveFile();
|
m_editor->saveFile();
|
||||||
ret = m_file->save();
|
ret = m_file->save();
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
VUtils::showMessage(QMessageBox::Warning, tr("Warning"), tr("Fail to save note."),
|
VUtils::showMessage(QMessageBox::Warning,
|
||||||
|
tr("Warning"),
|
||||||
|
tr("Fail to save note."),
|
||||||
tr("Fail to write to disk when saving a note. Please try it again."),
|
tr("Fail to write to disk when saving a note. Please try it again."),
|
||||||
QMessageBox::Ok, QMessageBox::Ok, this);
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok,
|
||||||
|
this);
|
||||||
m_editor->setModified(true);
|
m_editor->setModified(true);
|
||||||
} else {
|
} else {
|
||||||
m_fileDiverged = false;
|
m_fileDiverged = false;
|
||||||
@ -376,8 +389,17 @@ void VMdTab::setupMarkdownViewer()
|
|||||||
this, SLOT(updateCurrentHeader(const QString &)));
|
this, SLOT(updateCurrentHeader(const QString &)));
|
||||||
connect(m_document, &VDocument::keyPressed,
|
connect(m_document, &VDocument::keyPressed,
|
||||||
this, &VMdTab::handleWebKeyPressed);
|
this, &VMdTab::handleWebKeyPressed);
|
||||||
connect(m_document, SIGNAL(logicsFinished(void)),
|
connect(m_document, &VDocument::logicsFinished,
|
||||||
this, SLOT(restoreFromTabInfo(void)));
|
this, [this]() {
|
||||||
|
if (m_ready & TabReady::ReadMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ready |= TabReady::ReadMode;
|
||||||
|
|
||||||
|
tabIsReady(TabReady::ReadMode);
|
||||||
|
});
|
||||||
|
|
||||||
page->setWebChannel(channel);
|
page->setWebChannel(channel);
|
||||||
|
|
||||||
m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType, false),
|
m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType, false),
|
||||||
@ -417,8 +439,16 @@ void VMdTab::setupMarkdownEditor()
|
|||||||
this, [this]() {
|
this, [this]() {
|
||||||
this->m_editArea->getFindReplaceDialog()->closeDialog();
|
this->m_editArea->getFindReplaceDialog()->closeDialog();
|
||||||
});
|
});
|
||||||
connect(m_editor->object(), SIGNAL(ready(void)),
|
connect(m_editor->object(), &VEditorObject::ready,
|
||||||
this, SLOT(restoreFromTabInfo(void)));
|
this, [this]() {
|
||||||
|
if (m_ready & TabReady::EditMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ready |= TabReady::EditMode;
|
||||||
|
|
||||||
|
tabIsReady(TabReady::EditMode);
|
||||||
|
});
|
||||||
|
|
||||||
enableHeadingSequence(m_enableHeadingSequence);
|
enableHeadingSequence(m_enableHeadingSequence);
|
||||||
m_editor->reloadFile();
|
m_editor->reloadFile();
|
||||||
@ -842,3 +872,100 @@ void VMdTab::reload()
|
|||||||
showFileReadMode();
|
showFileReadMode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMdTab::tabIsReady(TabReady p_mode)
|
||||||
|
{
|
||||||
|
bool isCurrentMode = m_isEditMode && p_mode == TabReady::EditMode
|
||||||
|
|| !m_isEditMode && p_mode == TabReady::ReadMode;
|
||||||
|
|
||||||
|
if (isCurrentMode) {
|
||||||
|
restoreFromTabInfo();
|
||||||
|
|
||||||
|
if (m_enableBackupFile
|
||||||
|
&& !m_backupFileChecked
|
||||||
|
&& m_file->isModifiable()) {
|
||||||
|
if (!checkPreviousBackupFile()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_enableBackupFile
|
||||||
|
&& m_file->isModifiable()
|
||||||
|
&& p_mode == TabReady::EditMode) {
|
||||||
|
// contentsChanged will be emitted even the content is not changed.
|
||||||
|
connect(m_editor->document(), &QTextDocument::contentsChange,
|
||||||
|
this, [this]() {
|
||||||
|
if (m_isEditMode) {
|
||||||
|
m_backupTimer->stop();
|
||||||
|
m_backupTimer->start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdTab::writeBackupFile()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_enableBackupFile && m_file->isModifiable());
|
||||||
|
m_file->writeBackupFile(m_editor->getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMdTab::checkPreviousBackupFile()
|
||||||
|
{
|
||||||
|
m_backupFileChecked = true;
|
||||||
|
|
||||||
|
QString preFile = m_file->backupFileOfPreviousSession();
|
||||||
|
if (preFile.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox box(QMessageBox::Warning,
|
||||||
|
tr("Backup File Found"),
|
||||||
|
tr("Found backup file <span style=\"%1\">%2</span> "
|
||||||
|
"when opening note <span style=\"%1\">%3</span>.")
|
||||||
|
.arg(g_config->c_dataTextStyle)
|
||||||
|
.arg(preFile)
|
||||||
|
.arg(m_file->fetchPath()),
|
||||||
|
QMessageBox::NoButton,
|
||||||
|
this);
|
||||||
|
QString backupContent = m_file->readBackupFile(preFile);
|
||||||
|
QString info = tr("VNote may crash while editing this note before.<br/>"
|
||||||
|
"Please choose to recover from the backup file or delete it.<br/><br/>"
|
||||||
|
"Note file last modified: <span style=\"%1\">%2</span><br/>"
|
||||||
|
"Backup file last modified: <span style=\"%1\">%3</span><br/>"
|
||||||
|
"Content comparison: <span style=\"%1\">%4</span>")
|
||||||
|
.arg(g_config->c_dataTextStyle)
|
||||||
|
.arg(VUtils::displayDateTime(QFileInfo(m_file->fetchPath()).lastModified()))
|
||||||
|
.arg(VUtils::displayDateTime(QFileInfo(preFile).lastModified()))
|
||||||
|
.arg(m_file->getContent() == backupContent ? tr("Identical")
|
||||||
|
: tr("Different"));
|
||||||
|
box.setInformativeText(info);
|
||||||
|
QPushButton *recoverBtn = box.addButton(tr("Recover From Backup File"), QMessageBox::YesRole);
|
||||||
|
box.addButton(tr("Discard Backup File"), QMessageBox::NoRole);
|
||||||
|
QPushButton *cancelBtn = box.addButton(tr("Cancel"), QMessageBox::RejectRole);
|
||||||
|
|
||||||
|
box.setDefaultButton(cancelBtn);
|
||||||
|
box.setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||||
|
|
||||||
|
box.exec();
|
||||||
|
QAbstractButton *btn = box.clickedButton();
|
||||||
|
if (btn == cancelBtn || !btn) {
|
||||||
|
// Close current tab.
|
||||||
|
emit closeRequested(this);
|
||||||
|
return false;
|
||||||
|
} else if (btn == recoverBtn) {
|
||||||
|
// Load content from the backup file.
|
||||||
|
if (!m_isEditMode) {
|
||||||
|
showFileEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(m_editor);
|
||||||
|
m_editor->setContent(backupContent, true);
|
||||||
|
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
VUtils::deleteFile(preFile);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
18
src/vmdtab.h
18
src/vmdtab.h
@ -13,6 +13,7 @@ class QStackedLayout;
|
|||||||
class VDocument;
|
class VDocument;
|
||||||
class VMdEditor;
|
class VMdEditor;
|
||||||
class VInsertSelector;
|
class VInsertSelector;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
class VMdTab : public VEditTab
|
class VMdTab : public VEditTab
|
||||||
{
|
{
|
||||||
@ -88,6 +89,9 @@ public slots:
|
|||||||
// Enter edit mode.
|
// Enter edit mode.
|
||||||
void editFile() Q_DECL_OVERRIDE;
|
void editFile() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void writeBackupFile() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Update m_outline according to @p_tocHtml for read mode.
|
// Update m_outline according to @p_tocHtml for read mode.
|
||||||
void updateOutlineFromHtml(const QString &p_tocHtml);
|
void updateOutlineFromHtml(const QString &p_tocHtml);
|
||||||
@ -115,6 +119,8 @@ private slots:
|
|||||||
void restoreFromTabInfo();
|
void restoreFromTabInfo();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum TabReady { None = 0, ReadMode = 0x1, EditMode = 0x2 };
|
||||||
|
|
||||||
// Setup UI.
|
// Setup UI.
|
||||||
void setupUI();
|
void setupUI();
|
||||||
|
|
||||||
@ -166,6 +172,13 @@ private:
|
|||||||
// Prepare insert selector with snippets.
|
// Prepare insert selector with snippets.
|
||||||
VInsertSelector *prepareSnippetSelector(QWidget *p_parent = nullptr);
|
VInsertSelector *prepareSnippetSelector(QWidget *p_parent = nullptr);
|
||||||
|
|
||||||
|
// Called once read or edit mode is ready.
|
||||||
|
void tabIsReady(TabReady p_mode);
|
||||||
|
|
||||||
|
// Check if there exists backup file from previous session.
|
||||||
|
// Return true if we could continue.
|
||||||
|
bool checkPreviousBackupFile();
|
||||||
|
|
||||||
VMdEditor *m_editor;
|
VMdEditor *m_editor;
|
||||||
VWebView *m_webViewer;
|
VWebView *m_webViewer;
|
||||||
VDocument *m_document;
|
VDocument *m_document;
|
||||||
@ -175,6 +188,11 @@ private:
|
|||||||
bool m_enableHeadingSequence;
|
bool m_enableHeadingSequence;
|
||||||
|
|
||||||
QStackedLayout *m_stacks;
|
QStackedLayout *m_stacks;
|
||||||
|
|
||||||
|
// Timer to write backup file when content has been changed.
|
||||||
|
QTimer *m_backupTimer;
|
||||||
|
|
||||||
|
bool m_backupFileChecked;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline VMdEditor *VMdTab::getEditor()
|
inline VMdEditor *VMdTab::getEditor()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user