diff --git a/src/src.pro b/src/src.pro index 8ebf24c2..c98d65a5 100644 --- a/src/src.pro +++ b/src/src.pro @@ -62,7 +62,9 @@ SOURCES += main.cpp\ vcodeblockhighlighthelper.cpp \ vwebview.cpp \ vimagepreviewer.cpp \ - vexporter.cpp + vexporter.cpp \ + vmdtab.cpp \ + vhtmltab.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -111,7 +113,9 @@ HEADERS += vmainwindow.h \ vcodeblockhighlighthelper.h \ vwebview.h \ vimagepreviewer.h \ - vexporter.h + vexporter.h \ + vmdtab.h \ + vhtmltab.h RESOURCES += \ vnote.qrc \ diff --git a/src/vedit.cpp b/src/vedit.cpp index e2f79b31..eca3d2b3 100644 --- a/src/vedit.cpp +++ b/src/vedit.cpp @@ -568,7 +568,7 @@ void VEdit::contextMenuEvent(QContextMenuEvent *p_event) if (!textCursor().hasSelection()) { VEditTab *editTab = dynamic_cast(parent()); V_ASSERT(editTab); - if (editTab->getIsEditMode()) { + if (editTab->isEditMode()) { QAction *saveExitAct = new QAction(QIcon(":/resources/icons/save_exit.svg"), tr("&Save Changes And Read"), this); saveExitAct->setToolTip(tr("Save changes and exit edit mode")); diff --git a/src/veditarea.h b/src/veditarea.h index 9546b234..1602d36e 100644 --- a/src/veditarea.h +++ b/src/veditarea.h @@ -19,6 +19,7 @@ class VNote; class VFile; class VDirectory; class VFindReplaceDialog; +class QLabel; class VEditArea : public QWidget, public VNavigationMode { diff --git a/src/vedittab.cpp b/src/vedittab.cpp index 6f16c6d1..72d756cc 100644 --- a/src/vedittab.cpp +++ b/src/vedittab.cpp @@ -1,42 +1,14 @@ -#include -#include -#include -#include #include "vedittab.h" -#include "vedit.h" -#include "vdocument.h" -#include "vnote.h" -#include "utils/vutils.h" -#include "vpreviewpage.h" -#include "hgmarkdownhighlighter.h" -#include "vconfigmanager.h" -#include "vmarkdownconverter.h" -#include "vnotebook.h" -#include "vtoc.h" -#include "vmdedit.h" -#include "dialog/vfindreplacedialog.h" -#include "veditarea.h" -#include "vconstants.h" -#include "vwebview.h" +#include +#include -extern VConfigManager vconfig; - -VEditTab::VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent) - : QStackedWidget(p_parent), m_file(p_file), isEditMode(false), - webPreviewer(NULL), document(p_file, this), - mdConverterType(vconfig.getMdConverterType()), m_fileModified(false), - m_editArea(NULL) +VEditTab::VEditTab(VFile *p_file, VEditArea *p_editArea, QWidget *p_parent) + : QWidget(p_parent), m_file(p_file), m_isEditMode(false), + m_modified(false), m_editArea(p_editArea) { - tableOfContent.filePath = p_file->retrivePath(); - curHeader.filePath = p_file->retrivePath(); - Q_ASSERT(!m_file->isOpened()); - m_file->open(); - setupUI(); - if (p_mode == OpenFileMode::Edit) { - showFileEditMode(); - } else { - showFileReadMode(); - } + m_toc.filePath = m_file->retrivePath(); + m_curHeader.filePath = m_file->retrivePath(); + connect(qApp, &QApplication::focusChanged, this, &VEditTab::handleFocusChanged); } @@ -48,667 +20,60 @@ VEditTab::~VEditTab() } } -void VEditTab::init(VEditArea *p_editArea) -{ - m_editArea = p_editArea; -} - -void VEditTab::setupUI() -{ - switch (m_file->getDocType()) { - case DocType::Markdown: - if (m_file->isModifiable()) { - m_textEditor = new VMdEdit(m_file, &document, mdConverterType, this); - connect(dynamic_cast(m_textEditor), &VMdEdit::headersChanged, - this, &VEditTab::updateTocFromHeaders); - connect(dynamic_cast(m_textEditor), &VMdEdit::statusChanged, - this, &VEditTab::noticeStatusChanged); - connect(m_textEditor, SIGNAL(curHeaderChanged(int, int)), - this, SLOT(updateCurHeader(int, int))); - connect(m_textEditor, &VEdit::textChanged, - this, &VEditTab::handleTextChanged); - connect(m_textEditor, &VEdit::saveAndRead, - this, &VEditTab::saveAndRead); - connect(m_textEditor, &VEdit::discardAndRead, - this, &VEditTab::discardAndRead); - m_textEditor->reloadFile(); - addWidget(m_textEditor); - } else { - m_textEditor = NULL; - } - setupMarkdownPreview(); - break; - - case DocType::Html: - m_textEditor = new VEdit(m_file, this); - connect(m_textEditor, &VEdit::textChanged, - this, &VEditTab::handleTextChanged); - connect(m_textEditor, &VEdit::saveAndRead, - this, &VEditTab::saveAndRead); - connect(m_textEditor, &VEdit::discardAndRead, - this, &VEditTab::discardAndRead); - connect(m_textEditor, &VEdit::editNote, - this, &VEditTab::editFile); - m_textEditor->reloadFile(); - addWidget(m_textEditor); - webPreviewer = NULL; - break; - - default: - qWarning() << "unknown doc type" << int(m_file->getDocType()); - Q_ASSERT(false); - } -} - -void VEditTab::handleTextChanged() -{ - Q_ASSERT(m_file->isModifiable()); - if (m_fileModified) { - return; - } - noticeStatusChanged(); -} - -void VEditTab::noticeStatusChanged() -{ - m_fileModified = m_file->isModified(); - emit statusChanged(); -} - -void VEditTab::showFileReadMode() -{ - isEditMode = false; - int outlineIndex = curHeader.m_outlineIndex; - switch (m_file->getDocType()) { - case DocType::Html: - m_textEditor->setReadOnly(true); - break; - - case DocType::Markdown: - if (mdConverterType == MarkdownConverterType::Hoedown) { - previewByConverter(); - } else { - document.updateText(); - updateTocFromHtml(document.getToc()); - } - setCurrentWidget(webPreviewer); - clearSearchedWordHighlight(); - scrollPreviewToHeader(outlineIndex); - break; - - default: - qWarning() << "unknown doc type" << int(m_file->getDocType()); - Q_ASSERT(false); - } - noticeStatusChanged(); -} - -void VEditTab::scrollPreviewToHeader(int p_outlineIndex) -{ - Q_ASSERT(p_outlineIndex >= 0); - if (p_outlineIndex < tableOfContent.headers.size()) { - QString anchor = tableOfContent.headers[p_outlineIndex].anchor; - qDebug() << "scroll preview to" << p_outlineIndex << anchor; - if (!anchor.isEmpty()) { - document.scrollToAnchor(anchor.mid(1)); - } - } -} - -void VEditTab::previewByConverter() -{ - VMarkdownConverter mdConverter; - QString toc; - QString html = mdConverter.generateHtml(m_file->getContent(), - vconfig.getMarkdownExtensions(), - toc); - document.setHtml(html); - updateTocFromHtml(toc); -} - -void VEditTab::showFileEditMode() -{ - if (!m_file->isModifiable()) { - return; - } - isEditMode = true; - - // beginEdit() may change curHeader. - int outlineIndex = curHeader.m_outlineIndex; - m_textEditor->beginEdit(); - setCurrentWidget(m_textEditor); - if (m_file->getDocType() == DocType::Markdown) { - dynamic_cast(m_textEditor)->scrollToHeader(outlineIndex); - } - m_textEditor->setFocus(); - noticeStatusChanged(); -} - -bool VEditTab::closeFile(bool p_forced) -{ - if (p_forced && isEditMode) { - // Discard buffer content - m_textEditor->reloadFile(); - m_textEditor->endEdit(); - showFileReadMode(); - } else { - readFile(); - } - return !isEditMode; -} - -void VEditTab::editFile() -{ - if (isEditMode || !m_file->isModifiable()) { - return; - } - - showFileEditMode(); -} - -void VEditTab::readFile() -{ - if (!isEditMode) { - return; - } - - if (m_textEditor && m_textEditor->isModified()) { - // Prompt to save the changes - int ret = VUtils::showMessage(QMessageBox::Information, tr("Information"), - tr("Note %2 has been modified.") - .arg(vconfig.c_dataTextStyle).arg(m_file->getName()), - tr("Do you want to save your changes?"), - QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, - QMessageBox::Save, this); - switch (ret) { - case QMessageBox::Save: - saveFile(); - // Fall through - case QMessageBox::Discard: - m_textEditor->reloadFile(); - break; - case QMessageBox::Cancel: - // Nothing to do if user cancel this action - return; - default: - qWarning() << "wrong return value from QMessageBox:" << ret; - return; - } - } - - if (m_textEditor) { - m_textEditor->endEdit(); - } - - showFileReadMode(); -} - -bool VEditTab::saveFile() -{ - if (!isEditMode || !m_textEditor->isModified()) { - return true; - } - - bool ret; - // Make sure the file already exists. Temporary deal with cases when user delete or move - // a file. - QString filePath = m_file->retrivePath(); - if (!QFile(filePath).exists()) { - qWarning() << filePath << "being written has been removed"; - VUtils::showMessage(QMessageBox::Warning, tr("Warning"), tr("Fail to save note."), - tr("File %2 being written has been removed.") - .arg(vconfig.c_dataTextStyle).arg(filePath), - QMessageBox::Ok, QMessageBox::Ok, this); - return false; - } - m_textEditor->saveFile(); - ret = m_file->save(); - if (!ret) { - 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."), - QMessageBox::Ok, QMessageBox::Ok, this); - m_textEditor->setModified(true); - } - noticeStatusChanged(); - return ret; -} - -void VEditTab::saveAndRead() -{ - saveFile(); - readFile(); -} - -void VEditTab::discardAndRead() -{ - readFile(); -} - -void VEditTab::setupMarkdownPreview() -{ - const QString &jsHolder = c_htmlJSHolder; - const QString &extraHolder = c_htmlExtraHolder; - - webPreviewer = new VWebView(m_file, this); - connect(webPreviewer, &VWebView::editNote, - this, &VEditTab::editFile); - - VPreviewPage *page = new VPreviewPage(webPreviewer); - webPreviewer->setPage(page); - webPreviewer->setZoomFactor(vconfig.getWebZoomFactor()); - - QWebChannel *channel = new QWebChannel(webPreviewer); - channel->registerObject(QStringLiteral("content"), &document); - connect(&document, &VDocument::tocChanged, - this, &VEditTab::updateTocFromHtml); - connect(&document, SIGNAL(headerChanged(const QString&)), - this, SLOT(updateCurHeader(const QString &))); - connect(&document, &VDocument::keyPressed, - this, &VEditTab::handleWebKeyPressed); - page->setWebChannel(channel); - - QString jsFile, extraFile; - switch (mdConverterType) { - case MarkdownConverterType::Marked: - jsFile = "qrc" + VNote::c_markedJsFile; - extraFile = "\n"; - break; - - case MarkdownConverterType::Hoedown: - jsFile = "qrc" + VNote::c_hoedownJsFile; - // Use Marked to highlight code blocks. - extraFile = "\n"; - break; - - case MarkdownConverterType::MarkdownIt: - jsFile = "qrc" + VNote::c_markdownitJsFile; - extraFile = "\n" + - "\n" + - "\n"; - break; - - case MarkdownConverterType::Showdown: - jsFile = "qrc" + VNote::c_showdownJsFile; - extraFile = "\n" + - "\n"; - - break; - - default: - Q_ASSERT(false); - } - - if (vconfig.getEnableMermaid()) { - extraFile += "\n" + "\n" + - "\n"; - } - - if (vconfig.getEnableMathjax()) { - extraFile += "\n" - "\n" + - "\n"; - } - - if (vconfig.getEnableImageCaption()) { - extraFile += "\n"; - } - - QString htmlTemplate = VNote::s_markdownTemplate; - htmlTemplate.replace(jsHolder, jsFile); - if (!extraFile.isEmpty()) { - htmlTemplate.replace(extraHolder, extraFile); - } - - webPreviewer->setHtml(htmlTemplate, m_file->getBaseUrl()); - addWidget(webPreviewer); -} - void VEditTab::focusTab() { - currentWidget()->setFocus(); + focusChild(); emit getFocused(); } -void VEditTab::handleFocusChanged(QWidget * /* old */, QWidget *now) +bool VEditTab::isEditMode() const { - if (isChild(now)) { - if (now == this) { - // When VEditTab get focus, it should focus to current widget. - currentWidget()->setFocus(); - } + return m_isEditMode; +} + +bool VEditTab::isModified() const +{ + return m_modified; +} + +VFile *VEditTab::getFile() const +{ + return m_file; +} + +void VEditTab::handleFocusChanged(QWidget * /* p_old */, QWidget *p_now) +{ + if (p_now == this) { + // When VEditTab get focus, it should focus to current widget. + focusChild(); + + emit getFocused(); + } else if (isAncestorOf(p_now)) { emit getFocused(); - } -} - -void VEditTab::updateTocFromHtml(const QString &tocHtml) -{ - if (isEditMode) { - return; - } - tableOfContent.type = VHeaderType::Anchor; - QVector &headers = tableOfContent.headers; - headers.clear(); - - if (!tocHtml.isEmpty()) { - QXmlStreamReader xml(tocHtml); - if (xml.readNextStartElement()) { - if (xml.name() == "ul") { - parseTocUl(xml, headers, 1); - } else { - qWarning() << "TOC HTML does not start with