diff --git a/VNote.pro b/VNote.pro index f335ae93..85f1ef75 100644 --- a/VNote.pro +++ b/VNote.pro @@ -22,7 +22,9 @@ SOURCES += main.cpp\ vfilelist.cpp \ vnewfiledialog.cpp \ vtabwidget.cpp \ - vedit.cpp + vedit.cpp \ + veditor.cpp \ + vnotefile.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -33,7 +35,10 @@ HEADERS += vmainwindow.h \ vfilelist.h \ vnewfiledialog.h \ vtabwidget.h \ - vedit.h + vedit.h \ + veditor.h \ + vconstants.h \ + vnotefile.h RESOURCES += \ vnote.qrc diff --git a/vconstants.h b/vconstants.h new file mode 100644 index 00000000..cdd19d40 --- /dev/null +++ b/vconstants.h @@ -0,0 +1,6 @@ +#ifndef VCONSTANTS_H +#define VCONSTANTS_H + +enum class DocType { Html, Markdown }; + +#endif diff --git a/vedit.cpp b/vedit.cpp index 2e3692fd..63dd8112 100644 --- a/vedit.cpp +++ b/vedit.cpp @@ -1,154 +1,64 @@ #include #include "vedit.h" -VEdit::VEdit(const QString &path, const QString &name, bool modifiable, - QWidget *parent) - : QTextEdit(parent), path(path), name(name), modifiable(modifiable) +VEdit::VEdit(VNoteFile *noteFile, QWidget *parent) + : QTextEdit(parent), noteFile(noteFile) { - docType = isMarkdown() ? DocType::Markdown : DocType::Html; - fileText = readFileFromDisk(QDir(path).filePath(name)); - showFileReadMode(); - fileLoaded = true; - qDebug() << "VEdit:" << name << (docType == DocType::Markdown ? "Markdown" : "Html"); } -void VEdit::showFileReadMode() -{ - setReadOnly(true); - switch (docType) { - case DocType::Html: - if (!fileLoaded) { - setHtml(fileText); - } - break; - case DocType::Markdown: - setText(fileText); - break; - default: - qWarning() << "error: unknown doc type" << int(docType); - } -} - -void VEdit::showFileEditMode() +void VEdit::beginEdit() { setReadOnly(false); - switch (docType) { + switch (noteFile->docType) { case DocType::Html: - if (!fileLoaded) { - setHtml(fileText); - } + setHtml(noteFile->content); break; case DocType::Markdown: - setText(fileText); + setPlainText(noteFile->content); break; default: - qWarning() << "error: unknown doc type" << int(docType); + qWarning() << "error: unknown doc type" << int(noteFile->docType); } } -QString VEdit::readFileFromDisk(const QString &filePath) +bool VEdit::tryEndEdit() { - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qWarning() << "error: fail to read file" << filePath; - return QString(); - } - QString fileText(file.readAll()); - file.close(); - qDebug() << "read file content:" << filePath; - return fileText; + return !document()->isModified(); } -bool VEdit::writeFileToDisk(const QString &filePath, const QString &text) +void VEdit::beginSave() { - QFile file(filePath); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - qWarning() << "error: fail to open file" << filePath << "to write to"; - return false; - } - QTextStream stream(&file); - stream << text; - file.close(); - qDebug() << "write file content:" << filePath; - return true; -} - -bool VEdit::requestClose() -{ - readFile(); - return isReadOnly(); -} - -void VEdit::editFile() -{ - if (!modifiable || !isReadOnly()) { - return; - } - showFileEditMode(); -} - -void VEdit::readFile() -{ - if (isReadOnly()) { - return; - } - if (document()->isModified()) { - QMessageBox msgBox(QMessageBox::Information, tr("Exit edit mode"), - QString("Note has been changed. Do you want to save it before exit?")); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::No | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - int ret = msgBox.exec(); - switch (ret) { - case QMessageBox::Save: - saveFile(); - // Fall through - case QMessageBox::No: - showFileReadMode(); - break; - case QMessageBox::Cancel: - break; - default: - qWarning() << "error: wrong return value from QMessageBox:" << ret; - } - } else { - showFileReadMode(); - } -} - -void VEdit::saveFile() -{ - if (!modifiable || !document()->isModified()) { + if (!document()->isModified()) { return; } - switch (docType) { + switch (noteFile->docType) { case DocType::Html: - fileText = toHtml(); - writeFileToDisk(QDir(path).filePath(name), fileText); + noteFile->content = toHtml(); break; case DocType::Markdown: break; default: - qWarning() << "error: unknown doc type" << int(docType); + qWarning() << "error: unknown doc type" << int(noteFile->docType); } +} +void VEdit::endSave() +{ document()->setModified(false); } -bool VEdit::isMarkdown() +void VEdit::reloadFile() { - const QVector mdPostfix({"md", "markdown", "mkd"}); - - QStringList list = name.split('.', QString::SkipEmptyParts); - if (list.isEmpty()) { - return false; + switch (noteFile->docType) { + case DocType::Html: + setHtml(noteFile->content); + break; + case DocType::Markdown: + setPlainText(noteFile->content); + break; + default: + qWarning() << "error: unknown doc type" << int(noteFile->docType); } - const QString &postfix = list.last(); - for (int i = 0; i < mdPostfix.size(); ++i) { - if (postfix == mdPostfix[i]) { - return true; - } - } - return false; } diff --git a/vedit.h b/vedit.h index 380aee4d..9543cef0 100644 --- a/vedit.h +++ b/vedit.h @@ -3,20 +3,23 @@ #include #include +#include "vconstants.h" +#include "vnotefile.h" class VEdit : public QTextEdit { Q_OBJECT public: - explicit VEdit(const QString &path, const QString &name, bool modifiable = false, - QWidget *parent = 0); - bool requestClose(); - // Enter edit mode - void editFile(); - // Enter read mode - void readFile(); - // Save file - void saveFile(); + VEdit(VNoteFile *noteFile, QWidget *parent = 0); + void beginEdit(); + bool tryEndEdit(); + + // begin: sync the buffer to noteFile->content; + // end: setModified(false) + void beginSave(); + void endSave(); + + void reloadFile(); signals: @@ -24,19 +27,7 @@ public slots: private: - enum class DocType { Html, Markdown }; - QString readFileFromDisk(const QString &filePath); - bool writeFileToDisk(const QString &filePath, const QString &text); - void showFileReadMode(); - void showFileEditMode(); - bool isMarkdown(); - - QString path; - QString name; - QString fileText; - DocType docType; - bool modifiable; - bool fileLoaded; + VNoteFile *noteFile; }; #endif // VEDIT_H diff --git a/veditor.cpp b/veditor.cpp new file mode 100644 index 00000000..f492faee --- /dev/null +++ b/veditor.cpp @@ -0,0 +1,167 @@ +#include +#include +#include "veditor.h" +#include "vedit.h" + +VEditor::VEditor(const QString &path, const QString &name, bool modifiable, + QWidget *parent) + : QStackedWidget(parent) +{ + DocType docType = isMarkdown(name) ? DocType::Markdown : DocType::Html; + QString fileText = readFileFromDisk(QDir(path).filePath(name)); + noteFile = new VNoteFile(path, name, fileText, docType, modifiable); + + isEditMode = false; + + setupUI(); + + showFileReadMode(); +} + +VEditor::~VEditor() +{ + delete noteFile; +} + +void VEditor::setupUI() +{ + textEditor = new VEdit(noteFile); + textBrowser = new QTextBrowser(); + addWidget(textBrowser); + addWidget(textEditor); +} + +bool VEditor::isMarkdown(const QString &name) +{ + const QVector mdPostfix({"md", "markdown", "mkd"}); + + QStringList list = name.split('.', QString::SkipEmptyParts); + if (list.isEmpty()) { + return false; + } + const QString &postfix = list.last(); + for (int i = 0; i < mdPostfix.size(); ++i) { + if (postfix == mdPostfix[i]) { + return true; + } + } + return false; +} + +QString VEditor::readFileFromDisk(const QString &filePath) +{ + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "error: fail to read file" << filePath; + return QString(); + } + QString fileText(file.readAll()); + file.close(); + qDebug() << "read file content:" << filePath; + return fileText; +} + +bool VEditor::writeFileToDisk(const QString &filePath, const QString &text) +{ + QFile file(filePath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << "error: fail to open file" << filePath << "to write to"; + return false; + } + QTextStream stream(&file); + stream << text; + file.close(); + qDebug() << "write file content:" << filePath; + return true; +} + +void VEditor::showFileReadMode() +{ + isEditMode = false; + switch (noteFile->docType) { + case DocType::Html: + textBrowser->setHtml(noteFile->content); + break; + case DocType::Markdown: + + break; + default: + qWarning() << "error: unknown doc type" << int(noteFile->docType); + } + setCurrentWidget(textBrowser); +} + +void VEditor::showFileEditMode() +{ + isEditMode = true; + textEditor->beginEdit(); + setCurrentWidget(textEditor); +} + +bool VEditor::requestClose() +{ + readFile(); + return !isEditMode; +} + +void VEditor::editFile() +{ + if (isEditMode || !noteFile->modifiable) { + return; + } + showFileEditMode(); +} + +void VEditor::readFile() +{ + if (!isEditMode) { + return; + } + bool canExit = textEditor->tryEndEdit(); + if (!canExit) { + // Need to save the changes + QMessageBox msgBox; + msgBox.setText("The note has been modified."); + msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setIcon(QMessageBox::Information); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + switch (ret) { + case QMessageBox::Save: + saveFile(); + // Fall through + case QMessageBox::Discard: + textEditor->reloadFile(); + break; + case QMessageBox::Cancel: + // Nothing to do if user cancel this action + return; + default: + qWarning() << "error: wrong return value from QMessageBox:" << ret; + return; + } + } + textEditor->setReadOnly(true); + showFileReadMode(); +} + +bool VEditor::saveFile() +{ + if (!isEditMode || !noteFile->modifiable) { + return true; + } + textEditor->beginSave(); + bool ret = writeFileToDisk(QDir(noteFile->path).filePath(noteFile->name), + noteFile->content); + if (!ret) { + QMessageBox msgBox(QMessageBox::Warning, tr("Fail to save to file"), + QString("Fail to write to disk when saving a note. Please try it again.")); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.exec(); + return false; + } + textEditor->endSave(); + return true; +} diff --git a/veditor.h b/veditor.h new file mode 100644 index 00000000..192b2cde --- /dev/null +++ b/veditor.h @@ -0,0 +1,40 @@ +#ifndef VEDITOR_H +#define VEDITOR_H + +#include +#include +#include "vconstants.h" +#include "vnotefile.h" + +class QTextBrowser; +class VEdit; + +class VEditor : public QStackedWidget +{ +public: + VEditor(const QString &path, const QString &name, bool modifiable, + QWidget *parent = 0); + ~VEditor(); + bool requestClose(); + // Enter edit mode + void editFile(); + // Enter read mode + void readFile(); + // Save file + bool saveFile(); + +private: + bool isMarkdown(const QString &name); + QString readFileFromDisk(const QString &filePath); + bool writeFileToDisk(const QString &filePath, const QString &text); + void setupUI(); + void showFileReadMode(); + void showFileEditMode(); + + VNoteFile *noteFile; + bool isEditMode; + QTextBrowser *textBrowser; + VEdit *textEditor; +}; + +#endif // VEDITOR_H diff --git a/vnotefile.cpp b/vnotefile.cpp new file mode 100644 index 00000000..21d2888a --- /dev/null +++ b/vnotefile.cpp @@ -0,0 +1,9 @@ +#include "vnotefile.h" + +VNoteFile::VNoteFile(const QString &path, const QString &name, + const QString &content, DocType docType, bool modifiable) + : path(path), name(name), content(content), docType(docType), + modifiable(modifiable) +{ + +} diff --git a/vnotefile.h b/vnotefile.h new file mode 100644 index 00000000..afc93b04 --- /dev/null +++ b/vnotefile.h @@ -0,0 +1,20 @@ +#ifndef VNOTEFILE_H +#define VNOTEFILE_H + +#include +#include "vconstants.h" + +class VNoteFile +{ +public: + VNoteFile(const QString &path, const QString &name, const QString &content, + DocType docType, bool modifiable); + + QString path; + QString name; + QString content; + DocType docType; + bool modifiable; +}; + +#endif // VNOTEFILE_H diff --git a/vtabwidget.cpp b/vtabwidget.cpp index 44f02739..91d4c1b1 100644 --- a/vtabwidget.cpp +++ b/vtabwidget.cpp @@ -1,7 +1,7 @@ #include #include #include "vtabwidget.h" -#include "vedit.h" +#include "veditor.h" VTabWidget::VTabWidget(const QString &welcomePageUrl, QWidget *parent) : QTabWidget(parent), welcomePageUrl(welcomePageUrl) @@ -59,11 +59,11 @@ void VTabWidget::openFile(QJsonObject fileJson) int VTabWidget::openFileInTab(const QString &path, const QString &name, bool modifiable) { - VEdit *edit = new VEdit(path, name, modifiable); + VEditor *editor = new VEditor(path, name, modifiable); QJsonObject tabJson; tabJson["path"] = path; tabJson["name"] = name; - int idx = appendTabWithData(edit, name, tabJson); + int idx = appendTabWithData(editor, name, tabJson); setTabToolTip(idx, path); return idx; } @@ -85,32 +85,32 @@ int VTabWidget::findTabByFile(const QString &path, const QString &name) void VTabWidget::handleTabCloseRequest(int index) { qDebug() << "request closing tab" << index; - VEdit *edit = dynamic_cast(widget(index)); - Q_ASSERT(edit); - bool ok = edit->requestClose(); + VEditor *editor = dynamic_cast(widget(index)); + Q_ASSERT(editor); + bool ok = editor->requestClose(); if (ok) { removeTab(index); - delete edit; + delete editor; } } void VTabWidget::readFile() { - VEdit *edit = dynamic_cast(currentWidget()); - Q_ASSERT(edit); - edit->readFile(); + VEditor *editor = dynamic_cast(currentWidget()); + Q_ASSERT(editor); + editor->readFile(); } void VTabWidget::editFile() { - VEdit *edit = dynamic_cast(currentWidget()); - Q_ASSERT(edit); - edit->editFile(); + VEditor *editor = dynamic_cast(currentWidget()); + Q_ASSERT(editor); + editor->editFile(); } void VTabWidget::saveFile() { - VEdit *edit = dynamic_cast(currentWidget()); - Q_ASSERT(edit); - edit->saveFile(); + VEditor *editor = dynamic_cast(currentWidget()); + Q_ASSERT(editor); + editor->saveFile(); }