refactor: add VMdEdit to inherit VEdit for markdown edit

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-12-11 14:42:01 +08:00
parent 6a745df2eb
commit a884991150
9 changed files with 357 additions and 325 deletions

View File

@ -47,7 +47,8 @@ SOURCES += main.cpp\
vfile.cpp \ vfile.cpp \
vnotebookselector.cpp \ vnotebookselector.cpp \
vnofocusitemdelegate.cpp \ vnofocusitemdelegate.cpp \
vavatar.cpp vavatar.cpp \
vmdedit.cpp
HEADERS += vmainwindow.h \ HEADERS += vmainwindow.h \
vdirectorytree.h \ vdirectorytree.h \
@ -83,7 +84,8 @@ HEADERS += vmainwindow.h \
vfile.h \ vfile.h \
vnotebookselector.h \ vnotebookselector.h \
vnofocusitemdelegate.h \ vnofocusitemdelegate.h \
vavatar.h vavatar.h \
vmdedit.h
RESOURCES += \ RESOURCES += \
vnote.qrc vnote.qrc

View File

@ -3,107 +3,34 @@
#include "vedit.h" #include "vedit.h"
#include "vnote.h" #include "vnote.h"
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "hgmarkdownhighlighter.h"
#include "vmdeditoperations.h"
#include "vtoc.h" #include "vtoc.h"
#include "utils/vutils.h" #include "utils/vutils.h"
#include "veditoperations.h"
extern VConfigManager vconfig; extern VConfigManager vconfig;
VEdit::VEdit(VFile *p_file, QWidget *p_parent) VEdit::VEdit(VFile *p_file, QWidget *p_parent)
: QTextEdit(p_parent), m_file(p_file), mdHighlighter(NULL) : QTextEdit(p_parent), m_file(p_file), m_editOps(NULL)
{ {
connect(document(), &QTextDocument::modificationChanged, connect(document(), &QTextDocument::modificationChanged,
(VFile *)m_file, &VFile::setModified); (VFile *)m_file, &VFile::setModified);
if (m_file->getDocType() == DocType::Markdown) {
setAcceptRichText(false);
mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
500, document());
connect(mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
this, &VEdit::generateEditOutline);
editOps = new VMdEditOperations(this, m_file);
} else {
editOps = NULL;
}
updateTabSettings();
updateFontAndPalette();
connect(this, &VEdit::cursorPositionChanged,
this, &VEdit::updateCurHeader);
} }
VEdit::~VEdit() VEdit::~VEdit()
{ {
qDebug() << "VEdit destruction";
if (m_file) { if (m_file) {
disconnect(document(), &QTextDocument::modificationChanged, disconnect(document(), &QTextDocument::modificationChanged,
(VFile *)m_file, &VFile::setModified); (VFile *)m_file, &VFile::setModified);
} }
if (editOps) { if (m_editOps) {
delete editOps; delete m_editOps;
editOps = NULL; m_editOps = NULL;
}
}
void VEdit::updateFontAndPalette()
{
switch (m_file->getDocType()) {
case DocType::Markdown:
setFont(vconfig.getMdEditFont());
setPalette(vconfig.getMdEditPalette());
break;
case DocType::Html:
setFont(vconfig.getBaseEditFont());
setPalette(vconfig.getBaseEditPalette());
break;
default:
qWarning() << "error: unknown doc type" << int(m_file->getDocType());
return;
}
}
void VEdit::updateTabSettings()
{
switch (m_file->getDocType()) {
case DocType::Markdown:
if (vconfig.getTabStopWidth() > 0) {
QFontMetrics metrics(vconfig.getMdEditFont());
setTabStopWidth(vconfig.getTabStopWidth() * metrics.width(' '));
}
break;
case DocType::Html:
if (vconfig.getTabStopWidth() > 0) {
QFontMetrics metrics(vconfig.getBaseEditFont());
setTabStopWidth(vconfig.getTabStopWidth() * metrics.width(' '));
}
break;
default:
qWarning() << "error: unknown doc type" << int(m_file->getDocType());
return;
}
isExpandTab = vconfig.getIsExpandTab();
if (isExpandTab && (vconfig.getTabStopWidth() > 0)) {
tabSpaces = QString(vconfig.getTabStopWidth(), ' ');
} }
} }
void VEdit::beginEdit() void VEdit::beginEdit()
{ {
updateTabSettings();
updateFontAndPalette();
switch (m_file->getDocType()) {
case DocType::Html:
setHtml(m_file->getContent());
break;
case DocType::Markdown:
setFont(vconfig.getMdEditFont());
setPlainText(m_file->getContent());
initInitImages();
break;
default:
qWarning() << "error: unknown doc type" << int(m_file->getDocType());
}
setReadOnly(false); setReadOnly(false);
setModified(false); setModified(false);
} }
@ -111,9 +38,6 @@ void VEdit::beginEdit()
void VEdit::endEdit() void VEdit::endEdit()
{ {
setReadOnly(true); setReadOnly(true);
if (m_file->getDocType() == DocType::Markdown) {
clearUnusedImages();
}
} }
void VEdit::saveFile() void VEdit::saveFile()
@ -121,176 +45,38 @@ void VEdit::saveFile()
if (!document()->isModified()) { if (!document()->isModified()) {
return; return;
} }
switch (m_file->getDocType()) {
case DocType::Html:
m_file->setContent(toHtml()); m_file->setContent(toHtml());
break;
case DocType::Markdown:
m_file->setContent(toPlainText());
break;
default:
qWarning() << "error: unknown doc type" << int(m_file->getDocType());
}
document()->setModified(false); document()->setModified(false);
} }
void VEdit::reloadFile() void VEdit::reloadFile()
{ {
switch (m_file->getDocType()) {
case DocType::Html:
setHtml(m_file->getContent()); setHtml(m_file->getContent());
break;
case DocType::Markdown:
setPlainText(m_file->getContent());
break;
default:
qWarning() << "error: unknown doc type" << int(m_file->getDocType());
}
setModified(false); setModified(false);
} }
void VEdit::keyPressEvent(QKeyEvent *event) void VEdit::scrollToLine(int p_lineNumber)
{ {
if ((event->key() == Qt::Key_Tab) && isExpandTab) { Q_ASSERT(p_lineNumber >= 0);
QTextCursor cursor(document());
cursor.setPosition(textCursor().position());
cursor.insertText(tabSpaces);
return;
}
QTextEdit::keyPressEvent(event);
}
bool VEdit::canInsertFromMimeData(const QMimeData *source) const
{
return source->hasImage() || source->hasUrls()
|| QTextEdit::canInsertFromMimeData(source);
}
void VEdit::insertFromMimeData(const QMimeData *source)
{
if (source->hasImage()) {
// Image data in the clipboard
if (editOps) {
bool ret = editOps->insertImageFromMimeData(source);
if (ret) {
return;
}
}
} else if (source->hasUrls()) {
// Paste an image file
if (editOps) {
bool ret = editOps->insertURLFromMimeData(source);
if (ret) {
return;
}
}
}
QTextEdit::insertFromMimeData(source);
}
void VEdit::generateEditOutline()
{
QTextDocument *doc = document();
headers.clear();
// Assume that each block contains only one line
// Only support # syntax for now
QRegExp headerReg("(#{1,6})\\s*(\\S.*)"); // Need to trim the spaces
for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
Q_ASSERT(block.lineCount() == 1);
if ((block.userState() == HighlightBlockState::BlockNormal) &&
headerReg.exactMatch(block.text())) {
VHeader header(headerReg.cap(1).length(),
headerReg.cap(2).trimmed(), "", block.firstLineNumber());
headers.append(header);
}
}
emit headersChanged(headers);
updateCurHeader();
}
void VEdit::scrollToLine(int lineNumber)
{
Q_ASSERT(lineNumber >= 0);
// Move the cursor to the end first // Move the cursor to the end first
moveCursor(QTextCursor::End); moveCursor(QTextCursor::End);
QTextCursor cursor(document()->findBlockByLineNumber(lineNumber)); QTextCursor cursor(document()->findBlockByLineNumber(p_lineNumber));
cursor.movePosition(QTextCursor::EndOfBlock); cursor.movePosition(QTextCursor::EndOfBlock);
setTextCursor(cursor); setTextCursor(cursor);
setFocus(); setFocus();
} }
void VEdit::updateCurHeader() bool VEdit::isModified() const
{ {
int curHeader = 0; return document()->isModified();
QTextCursor cursor(this->textCursor());
int curLine = cursor.block().firstLineNumber();
for (int i = headers.size() - 1; i >= 0; --i) {
if (headers[i].lineNumber <= curLine) {
curHeader = headers[i].lineNumber;
break;
}
}
emit curHeaderChanged(curHeader);
} }
void VEdit::insertImage(const QString &name) void VEdit::setModified(bool p_modified)
{ {
m_insertedImages.append(name); document()->setModified(p_modified);
} if (m_file) {
m_file->setModified(p_modified);
void VEdit::initInitImages() }
{
m_initImages = VUtils::imagesFromMarkdownFile(m_file->retrivePath());
}
void VEdit::clearUnusedImages()
{
QVector<QString> images = VUtils::imagesFromMarkdownFile(m_file->retrivePath());
if (!m_insertedImages.isEmpty()) {
QVector<QString> imageNames(images.size());
for (int i = 0; i < imageNames.size(); ++i) {
imageNames[i] = VUtils::fileNameFromPath(images[i]);
}
QDir dir = QDir(m_file->retriveImagePath());
for (int i = 0; i < m_insertedImages.size(); ++i) {
QString name = m_insertedImages[i];
int j;
for (j = 0; j < imageNames.size(); ++j) {
if (name == imageNames[j]) {
break;
}
}
// Delete it
if (j == imageNames.size()) {
QString imagePath = dir.filePath(name);
QFile(imagePath).remove();
qDebug() << "delete inserted image" << imagePath;
}
}
m_insertedImages.clear();
}
for (int i = 0; i < m_initImages.size(); ++i) {
QString imagePath = m_initImages[i];
int j;
for (j = 0; j < images.size(); ++j) {
if (imagePath == images[j]) {
break;
}
}
// Delete it
if (j == images.size()) {
QFile(imagePath).remove();
qDebug() << "delete existing image" << imagePath;
}
}
m_initImages.clear();
} }

View File

@ -8,7 +8,6 @@
#include "vtoc.h" #include "vtoc.h"
#include "vfile.h" #include "vfile.h"
class HGMarkdownHighlighter;
class VEditOperations; class VEditOperations;
class VEdit : public QTextEdit class VEdit : public QTextEdit
@ -16,61 +15,21 @@ class VEdit : public QTextEdit
Q_OBJECT Q_OBJECT
public: public:
VEdit(VFile *p_file, QWidget *p_parent = 0); VEdit(VFile *p_file, QWidget *p_parent = 0);
~VEdit(); virtual ~VEdit();
void beginEdit(); virtual void beginEdit();
void endEdit(); virtual void endEdit();
// Save buffer content to VFile. // Save buffer content to VFile.
void saveFile(); virtual void saveFile();
virtual void setModified(bool p_modified);
inline void setModified(bool modified); bool isModified() const;
inline bool isModified() const; virtual void reloadFile();
virtual void scrollToLine(int p_lineNumber);
void reloadFile();
void scrollToLine(int lineNumber);
void insertImage(const QString &name);
signals:
void headersChanged(const QVector<VHeader> &headers);
void curHeaderChanged(int lineNumber);
protected: protected:
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
bool canInsertFromMimeData(const QMimeData *source) const Q_DECL_OVERRIDE;
void insertFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
private slots:
void generateEditOutline();
void updateCurHeader();
private:
void updateTabSettings();
void updateFontAndPalette();
void initInitImages();
void clearUnusedImages();
QPointer<VFile> m_file; QPointer<VFile> m_file;
bool isExpandTab; VEditOperations *m_editOps;
QString tabSpaces;
HGMarkdownHighlighter *mdHighlighter;
VEditOperations *editOps;
QVector<VHeader> headers;
QVector<QString> m_insertedImages;
QVector<QString> m_initImages;
}; };
inline bool VEdit::isModified() const
{
return document()->isModified();
}
inline void VEdit::setModified(bool modified)
{
document()->setModified(modified);
if (m_file) {
m_file->setModified(modified);
}
}
#endif // VEDIT_H #endif // VEDIT_H

View File

@ -1,5 +1,4 @@
#include <QtWidgets> #include <QtWidgets>
#include <QTextBrowser>
#include <QWebChannel> #include <QWebChannel>
#include <QWebEngineView> #include <QWebEngineView>
#include <QFileInfo> #include <QFileInfo>
@ -15,12 +14,13 @@
#include "vmarkdownconverter.h" #include "vmarkdownconverter.h"
#include "vnotebook.h" #include "vnotebook.h"
#include "vtoc.h" #include "vtoc.h"
#include "vmdedit.h"
extern VConfigManager vconfig; extern VConfigManager vconfig;
VEditTab::VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent) VEditTab::VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent)
: QStackedWidget(p_parent), m_file(p_file), isEditMode(false), : QStackedWidget(p_parent), m_file(p_file), isEditMode(false),
mdConverterType(vconfig.getMdConverterType()) mdConverterType(vconfig.getMdConverterType()), m_fileModified(false)
{ {
tableOfContent.filePath = p_file->retrivePath(); tableOfContent.filePath = p_file->retrivePath();
curHeader.filePath = p_file->retrivePath(); curHeader.filePath = p_file->retrivePath();
@ -45,43 +45,55 @@ VEditTab::~VEditTab()
void VEditTab::setupUI() void VEditTab::setupUI()
{ {
textEditor = new VEdit(m_file);
connect(textEditor, &VEdit::headersChanged,
this, &VEditTab::updateTocFromHeaders);
connect(textEditor, SIGNAL(curHeaderChanged(int)),
this, SLOT(updateCurHeader(int)));
connect(textEditor, &VEdit::textChanged,
this, &VEditTab::statusChanged);
addWidget(textEditor);
switch (m_file->getDocType()) { switch (m_file->getDocType()) {
case DocType::Markdown: case DocType::Markdown:
m_textEditor = new VMdEdit(m_file, this);
connect(dynamic_cast<VMdEdit *>(m_textEditor), &VMdEdit::headersChanged,
this, &VEditTab::updateTocFromHeaders);
connect(m_textEditor, SIGNAL(curHeaderChanged(int)),
this, SLOT(updateCurHeader(int)));
connect(m_textEditor, &VEdit::textChanged,
this, &VEditTab::handleTextChanged);
addWidget(m_textEditor);
setupMarkdownPreview(); setupMarkdownPreview();
textBrowser = NULL;
break; break;
case DocType::Html: case DocType::Html:
textBrowser = new QTextBrowser(); m_textEditor = new VEdit(m_file, this);
addWidget(textBrowser); connect(m_textEditor, &VEdit::textChanged,
textBrowser->setFont(vconfig.getBaseEditFont()); this, &VEditTab::handleTextChanged);
textBrowser->setPalette(vconfig.getBaseEditPalette()); m_textEditor->reloadFile();
addWidget(m_textEditor);
webPreviewer = NULL; webPreviewer = NULL;
break; break;
default: default:
qWarning() << "error: unknown doc type" << int(m_file->getDocType()); qWarning() << "error: unknown doc type" << int(m_file->getDocType());
Q_ASSERT(false);
} }
} }
void VEditTab::handleTextChanged()
{
if (m_fileModified) {
return;
}
noticeStatusChanged();
}
void VEditTab::noticeStatusChanged()
{
m_fileModified = m_file->isModified();
emit statusChanged();
}
void VEditTab::showFileReadMode() void VEditTab::showFileReadMode()
{ {
qDebug() << "read" << m_file->getName(); qDebug() << "read" << m_file->getName();
isEditMode = false; isEditMode = false;
switch (m_file->getDocType()) { switch (m_file->getDocType()) {
case DocType::Html: case DocType::Html:
textBrowser->setHtml(m_file->getContent()); m_textEditor->setReadOnly(true);
textBrowser->setFont(vconfig.getBaseEditFont());
textBrowser->setPalette(vconfig.getBaseEditPalette());
setCurrentWidget(textBrowser);
break; break;
case DocType::Markdown: case DocType::Markdown:
if (mdConverterType == MarkdownConverterType::Marked) { if (mdConverterType == MarkdownConverterType::Marked) {
@ -93,7 +105,9 @@ void VEditTab::showFileReadMode()
break; break;
default: default:
qWarning() << "error: unknown doc type" << int(m_file->getDocType()); qWarning() << "error: unknown doc type" << int(m_file->getDocType());
Q_ASSERT(false);
} }
noticeStatusChanged();
} }
void VEditTab::previewByConverter() void VEditTab::previewByConverter()
@ -112,17 +126,18 @@ void VEditTab::previewByConverter()
void VEditTab::showFileEditMode() void VEditTab::showFileEditMode()
{ {
isEditMode = true; isEditMode = true;
textEditor->beginEdit(); m_textEditor->beginEdit();
setCurrentWidget(textEditor); setCurrentWidget(m_textEditor);
textEditor->setFocus(); m_textEditor->setFocus();
noticeStatusChanged();
} }
bool VEditTab::closeFile(bool p_forced) bool VEditTab::closeFile(bool p_forced)
{ {
if (p_forced && isEditMode) { if (p_forced && isEditMode) {
// Discard buffer content // Discard buffer content
textEditor->reloadFile(); m_textEditor->reloadFile();
textEditor->endEdit(); m_textEditor->endEdit();
showFileReadMode(); showFileReadMode();
} else { } else {
readFile(); readFile();
@ -145,7 +160,7 @@ void VEditTab::readFile()
return; return;
} }
if (textEditor->isModified()) { if (m_textEditor->isModified()) {
// Prompt to save the changes // Prompt to save the changes
int ret = VUtils::showMessage(QMessageBox::Information, tr("Information"), int ret = VUtils::showMessage(QMessageBox::Information, tr("Information"),
QString("Note %1 has been modified.").arg(m_file->getName()), QString("Note %1 has been modified.").arg(m_file->getName()),
@ -157,7 +172,7 @@ void VEditTab::readFile()
saveFile(); saveFile();
// Fall through // Fall through
case QMessageBox::Discard: case QMessageBox::Discard:
textEditor->reloadFile(); m_textEditor->reloadFile();
break; break;
case QMessageBox::Cancel: case QMessageBox::Cancel:
// Nothing to do if user cancel this action // Nothing to do if user cancel this action
@ -167,14 +182,14 @@ void VEditTab::readFile()
return; return;
} }
} }
textEditor->endEdit(); m_textEditor->endEdit();
showFileReadMode(); showFileReadMode();
} }
bool VEditTab::saveFile() bool VEditTab::saveFile()
{ {
bool ret; bool ret;
if (!isEditMode || !textEditor->isModified()) { if (!isEditMode || !m_textEditor->isModified()) {
return true; return true;
} }
// Make sure the file already exists. Temporary deal with cases when user delete or move // Make sure the file already exists. Temporary deal with cases when user delete or move
@ -187,15 +202,15 @@ bool VEditTab::saveFile()
QMessageBox::Ok, QMessageBox::Ok, this); QMessageBox::Ok, QMessageBox::Ok, this);
return false; return false;
} }
textEditor->saveFile(); m_textEditor->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"),
QString("Fail to write to disk when saving a note. Please try it again."), QString("Fail to write to disk when saving a note. Please try it again."),
QMessageBox::Ok, QMessageBox::Ok, this); QMessageBox::Ok, QMessageBox::Ok, this);
textEditor->setModified(true); m_textEditor->setModified(true);
} }
emit statusChanged(); noticeStatusChanged();
return ret; return ret;
} }
@ -353,7 +368,7 @@ void VEditTab::scrollToAnchor(const VAnchor &anchor)
curHeader = anchor; curHeader = anchor;
if (isEditMode) { if (isEditMode) {
if (anchor.lineNumber > -1) { if (anchor.lineNumber > -1) {
textEditor->scrollToLine(anchor.lineNumber); m_textEditor->scrollToLine(anchor.lineNumber);
} }
} else { } else {
if (!anchor.anchor.isEmpty()) { if (!anchor.anchor.isEmpty()) {

View File

@ -12,7 +12,6 @@
#include "vtoc.h" #include "vtoc.h"
#include "vfile.h" #include "vfile.h"
class QTextBrowser;
class QWebEngineView; class QWebEngineView;
class VNote; class VNote;
class QXmlStreamReader; class QXmlStreamReader;
@ -38,6 +37,7 @@ public:
void requestUpdateCurHeader(); void requestUpdateCurHeader();
void scrollToAnchor(const VAnchor& anchor); void scrollToAnchor(const VAnchor& anchor);
inline VFile *getFile(); inline VFile *getFile();
signals: signals:
void getFocused(); void getFocused();
void outlineChanged(const VToc &toc); void outlineChanged(const VToc &toc);
@ -50,6 +50,7 @@ private slots:
void updateCurHeader(const QString &anchor); void updateCurHeader(const QString &anchor);
void updateCurHeader(int lineNumber); void updateCurHeader(int lineNumber);
void updateTocFromHeaders(const QVector<VHeader> &headers); void updateTocFromHeaders(const QVector<VHeader> &headers);
void handleTextChanged();
private: private:
void setupUI(); void setupUI();
@ -60,16 +61,17 @@ private:
inline bool isChild(QObject *obj); inline bool isChild(QObject *obj);
void parseTocUl(QXmlStreamReader &xml, QVector<VHeader> &headers, int level); void parseTocUl(QXmlStreamReader &xml, QVector<VHeader> &headers, int level);
void parseTocLi(QXmlStreamReader &xml, QVector<VHeader> &headers, int level); void parseTocLi(QXmlStreamReader &xml, QVector<VHeader> &headers, int level);
void noticeStatusChanged();
QPointer<VFile> m_file; QPointer<VFile> m_file;
bool isEditMode; bool isEditMode;
QTextBrowser *textBrowser; VEdit *m_textEditor;
VEdit *textEditor;
QWebEngineView *webPreviewer; QWebEngineView *webPreviewer;
VDocument document; VDocument document;
MarkdownConverterType mdConverterType; MarkdownConverterType mdConverterType;
VToc tableOfContent; VToc tableOfContent;
VAnchor curHeader; VAnchor curHeader;
bool m_fileModified;
}; };
inline bool VEditTab::getIsEditMode() const inline bool VEditTab::getIsEditMode() const
@ -79,7 +81,7 @@ inline bool VEditTab::getIsEditMode() const
inline bool VEditTab::isModified() const inline bool VEditTab::isModified() const
{ {
return textEditor->isModified(); return m_textEditor->isModified();
} }
inline bool VEditTab::isChild(QObject *obj) inline bool VEditTab::isChild(QObject *obj)

View File

@ -530,6 +530,7 @@ void VMainWindow::setRenderBackgroundColor(QAction *action)
void VMainWindow::updateToolbarFromTabChage(const VFile *p_file, bool p_editMode) void VMainWindow::updateToolbarFromTabChage(const VFile *p_file, bool p_editMode)
{ {
qDebug() << "update toolbar";
if (!p_file) { if (!p_file) {
editNoteAct->setVisible(false); editNoteAct->setVisible(false);
saveExitAct->setVisible(false); saveExitAct->setVisible(false);

211
src/vmdedit.cpp Normal file
View File

@ -0,0 +1,211 @@
#include <QtWidgets>
#include "vmdedit.h"
#include "hgmarkdownhighlighter.h"
#include "vmdeditoperations.h"
#include "vnote.h"
#include "vconfigmanager.h"
#include "vtoc.h"
#include "utils/vutils.h"
extern VConfigManager vconfig;
VMdEdit::VMdEdit(VFile *p_file, QWidget *p_parent)
: VEdit(p_file, p_parent), m_mdHighlighter(NULL)
{
Q_ASSERT(p_file->getDocType() == DocType::Markdown);
setAcceptRichText(false);
m_mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
500, document());
connect(m_mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
this, &VMdEdit::generateEditOutline);
m_editOps = new VMdEditOperations(this, m_file);
connect(this, &VMdEdit::cursorPositionChanged,
this, &VMdEdit::updateCurHeader);
updateTabSettings();
updateFontAndPalette();
}
void VMdEdit::updateFontAndPalette()
{
setFont(vconfig.getMdEditFont());
setPalette(vconfig.getMdEditPalette());
}
void VMdEdit::updateTabSettings()
{
if (vconfig.getTabStopWidth() > 0) {
QFontMetrics metrics(vconfig.getMdEditFont());
setTabStopWidth(vconfig.getTabStopWidth() * metrics.width(' '));
}
m_expandTab = vconfig.getIsExpandTab();
if (m_expandTab && (vconfig.getTabStopWidth() > 0)) {
m_tabSpaces = QString(vconfig.getTabStopWidth(), ' ');
}
}
void VMdEdit::beginEdit()
{
updateTabSettings();
updateFontAndPalette();
setFont(vconfig.getMdEditFont());
setPlainText(m_file->getContent());
initInitImages();
setReadOnly(false);
setModified(false);
}
void VMdEdit::endEdit()
{
setReadOnly(true);
clearUnusedImages();
}
void VMdEdit::saveFile()
{
if (!document()->isModified()) {
return;
}
m_file->setContent(toPlainText());
document()->setModified(false);
}
void VMdEdit::reloadFile()
{
setPlainText(m_file->getContent());
setModified(false);
}
void VMdEdit::keyPressEvent(QKeyEvent *event)
{
if ((event->key() == Qt::Key_Tab) && m_expandTab) {
QTextCursor cursor(document());
cursor.setPosition(textCursor().position());
cursor.insertText(m_tabSpaces);
return;
}
VEdit::keyPressEvent(event);
}
bool VMdEdit::canInsertFromMimeData(const QMimeData *source) const
{
return source->hasImage() || source->hasUrls()
|| VEdit::canInsertFromMimeData(source);
}
void VMdEdit::insertFromMimeData(const QMimeData *source)
{
if (source->hasImage()) {
// Image data in the clipboard
bool ret = m_editOps->insertImageFromMimeData(source);
if (ret) {
return;
}
} else if (source->hasUrls()) {
// Paste an image file
bool ret = m_editOps->insertURLFromMimeData(source);
if (ret) {
return;
}
}
VEdit::insertFromMimeData(source);
}
void VMdEdit::insertImage(const QString &p_name)
{
m_insertedImages.append(p_name);
}
void VMdEdit::initInitImages()
{
m_initImages = VUtils::imagesFromMarkdownFile(m_file->retrivePath());
}
void VMdEdit::clearUnusedImages()
{
QVector<QString> images = VUtils::imagesFromMarkdownFile(m_file->retrivePath());
if (!m_insertedImages.isEmpty()) {
QVector<QString> imageNames(images.size());
for (int i = 0; i < imageNames.size(); ++i) {
imageNames[i] = VUtils::fileNameFromPath(images[i]);
}
QDir dir = QDir(m_file->retriveImagePath());
for (int i = 0; i < m_insertedImages.size(); ++i) {
QString name = m_insertedImages[i];
int j;
for (j = 0; j < imageNames.size(); ++j) {
if (name == imageNames[j]) {
break;
}
}
// Delete it
if (j == imageNames.size()) {
QString imagePath = dir.filePath(name);
QFile(imagePath).remove();
qDebug() << "delete inserted image" << imagePath;
}
}
m_insertedImages.clear();
}
for (int i = 0; i < m_initImages.size(); ++i) {
QString imagePath = m_initImages[i];
int j;
for (j = 0; j < images.size(); ++j) {
if (imagePath == images[j]) {
break;
}
}
// Delete it
if (j == images.size()) {
QFile(imagePath).remove();
qDebug() << "delete existing image" << imagePath;
}
}
m_initImages.clear();
}
void VMdEdit::updateCurHeader()
{
int curHeader = 0;
QTextCursor cursor(this->textCursor());
int curLine = cursor.block().firstLineNumber();
for (int i = m_headers.size() - 1; i >= 0; --i) {
if (m_headers[i].lineNumber <= curLine) {
curHeader = m_headers[i].lineNumber;
break;
}
}
emit curHeaderChanged(curHeader);
}
void VMdEdit::generateEditOutline()
{
QTextDocument *doc = document();
m_headers.clear();
// Assume that each block contains only one line
// Only support # syntax for now
QRegExp headerReg("(#{1,6})\\s*(\\S.*)"); // Need to trim the spaces
for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
Q_ASSERT(block.lineCount() == 1);
if ((block.userState() == HighlightBlockState::BlockNormal) &&
headerReg.exactMatch(block.text())) {
VHeader header(headerReg.cap(1).length(),
headerReg.cap(2).trimmed(), "", block.firstLineNumber());
m_headers.append(header);
}
}
emit headersChanged(m_headers);
updateCurHeader();
}

51
src/vmdedit.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef VMDEDIT_H
#define VMDEDIT_H
#include "vedit.h"
#include <QVector>
#include <QString>
#include "vtoc.h"
class HGMarkdownHighlighter;
class VMdEdit : public VEdit
{
Q_OBJECT
public:
VMdEdit(VFile *p_file, QWidget *p_parent = 0);
void beginEdit() Q_DECL_OVERRIDE;
void endEdit() Q_DECL_OVERRIDE;
void saveFile() Q_DECL_OVERRIDE;
void reloadFile() Q_DECL_OVERRIDE;
void insertImage(const QString &p_name);
signals:
void headersChanged(const QVector<VHeader> &headers);
void curHeaderChanged(int lineNumber);
private slots:
void generateEditOutline();
void updateCurHeader();
protected:
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
bool canInsertFromMimeData(const QMimeData *source) const Q_DECL_OVERRIDE;
void insertFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
private:
void updateFontAndPalette();
void updateTabSettings();
void initInitImages();
void clearUnusedImages();
HGMarkdownHighlighter *m_mdHighlighter;
QVector<QString> m_insertedImages;
QVector<QString> m_initImages;
bool m_expandTab;
QString m_tabSpaces;
QVector<VHeader> m_headers;
};
#endif // VMDEDIT_H

View File

@ -13,6 +13,7 @@
#include "vedit.h" #include "vedit.h"
#include "vdownloader.h" #include "vdownloader.h"
#include "vfile.h" #include "vfile.h"
#include "vmdedit.h"
VMdEditOperations::VMdEditOperations(VEdit *p_editor, VFile *p_file) VMdEditOperations::VMdEditOperations(VEdit *p_editor, VFile *p_file)
: VEditOperations(p_editor, p_file) : VEditOperations(p_editor, p_file)
@ -54,7 +55,9 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin
QString md = QString("![%1](images/%2)").arg(title).arg(fileName); QString md = QString("![%1](images/%2)").arg(title).arg(fileName);
insertTextAtCurPos(md); insertTextAtCurPos(md);
m_editor->insertImage(fileName); VMdEdit *mdEditor = dynamic_cast<VMdEdit *>(m_editor);
Q_ASSERT(mdEditor);
mdEditor->insertImage(fileName);
} }
void VMdEditOperations::insertImageFromPath(const QString &title, void VMdEditOperations::insertImageFromPath(const QString &title,
@ -77,7 +80,9 @@ void VMdEditOperations::insertImageFromPath(const QString &title,
QString md = QString("![%1](images/%2)").arg(title).arg(fileName); QString md = QString("![%1](images/%2)").arg(title).arg(fileName);
insertTextAtCurPos(md); insertTextAtCurPos(md);
m_editor->insertImage(fileName); VMdEdit *mdEditor = dynamic_cast<VMdEdit *>(m_editor);
Q_ASSERT(mdEditor);
mdEditor->insertImage(fileName);
} }
bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl) bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)