123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395 |
- #include "markdownviewwindow.h"
- #include <QSplitter>
- #include <QToolBar>
- #include <QStackedWidget>
- #include <QEvent>
- #include <QCoreApplication>
- #include <QScrollBar>
- #include <QLabel>
- #include <QApplication>
- #include <QProgressDialog>
- #include <QMenu>
- #include <QActionGroup>
- #include <QTimer>
- #include <QPrinter>
- #include <core/fileopenparameters.h>
- #include <core/editorconfig.h>
- #include <core/htmltemplatehelper.h>
- #include <vtextedit/vtextedit.h>
- #include <vtextedit/pegmarkdownhighlighter.h>
- #include <vtextedit/markdowneditorconfig.h>
- #include <utils/pathutils.h>
- #include <utils/widgetutils.h>
- #include <utils/printutils.h>
- #include <buffer/markdownbuffer.h>
- #include <core/vnotex.h>
- #include <core/thememgr.h>
- #include <imagehost/imagehostmgr.h>
- #include <imagehost/imagehost.h>
- #include "editors/markdowneditor.h"
- #include "textviewwindowhelper.h"
- #include "editors/markdownviewer.h"
- #include "editors/editormarkdownvieweradapter.h"
- #include "editors/previewhelper.h"
- #include "dialogs/deleteconfirmdialog.h"
- #include "outlineprovider.h"
- #include "toolbarhelper.h"
- #include "findandreplacewidget.h"
- #include "editors/statuswidget.h"
- #include "editors/plantumlhelper.h"
- #include "editors/graphvizhelper.h"
- #include "messageboxhelper.h"
- using namespace vnotex;
- MarkdownViewWindow::MarkdownViewWindow(QWidget *p_parent)
- : ViewWindow(p_parent)
- {
- // Need to setup before setupUI() since the tool bar action will need the provider.
- setupOutlineProvider();
- setupUI();
- setupPreviewHelper();
- }
- MarkdownViewWindow::~MarkdownViewWindow()
- {
- if (m_textEditorStatusWidget) {
- getMainStatusWidget()->removeWidget(m_textEditorStatusWidget.get());
- m_textEditorStatusWidget->setParent(nullptr);
- }
- if (m_viewerStatusWidget) {
- getMainStatusWidget()->removeWidget(m_viewerStatusWidget.get());
- m_viewerStatusWidget->setParent(nullptr);
- }
- m_mainStatusWidget->setParent(nullptr);
- }
- void MarkdownViewWindow::setupUI()
- {
- // Central widget.
- m_splitter = new QSplitter(this);
- m_splitter->setContentsMargins(0, 0, 0, 0);
- setCentralWidget(m_splitter);
- // Get the focus event from splitter.
- m_splitter->installEventFilter(this);
- // Status widget.
- // We use a QTabWidget as status widget since we have two widgets for different modes.
- {
- auto statusWidget = QSharedPointer<StatusWidget>::create(this);
- m_mainStatusWidget = QSharedPointer<QStackedWidget>::create(this);
- m_mainStatusWidget->setContentsMargins(0, 0, 0, 0);
- statusWidget->setEditorStatusWidget(m_mainStatusWidget);
- setStatusWidget(statusWidget);
- }
- setupToolBar();
- }
- void MarkdownViewWindow::setMode(ViewWindowMode p_mode)
- {
- setModeInternal(p_mode, true);
- }
- void MarkdownViewWindow::setModeInternal(ViewWindowMode p_mode, bool p_syncBuffer)
- {
- if (p_mode == m_mode) {
- return;
- }
- m_previousMode = m_mode;
- m_mode = p_mode;
- switch (m_mode) {
- case ViewWindowMode::Read:
- {
- if (!m_viewer) {
- setupViewer();
- if (p_syncBuffer) {
- syncViewerFromBuffer(true);
- }
- }
- // Avoid focus glitch.
- m_viewer->show();
- m_viewer->setFocus();
- if (m_editor) {
- m_editor->hide();
- }
- getMainStatusWidget()->setCurrentWidget(m_viewerStatusWidget.get());
- break;
- }
- case ViewWindowMode::Edit:
- {
- if (m_debugViewer && m_debugViewer->isVisible()) {
- toggleDebug();
- }
- if (!m_editor) {
- // We need viewer to preview.
- if (!m_viewer) {
- setupViewer();
- // Must show the viewer to let it init with the correct DPI.
- // Will hide it when viewerReady().
- m_viewer->show();
- }
- setupTextEditor();
- if (p_syncBuffer) {
- syncTextEditorFromBuffer(true);
- }
- m_editor->show();
- setEditViewMode(ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig().getEditViewMode());
- } else {
- setEditViewMode(m_editViewMode);
- }
- // Avoid focus glitch.
- m_editor->show();
- m_editor->setFocus();
- getMainStatusWidget()->setCurrentWidget(m_textEditorStatusWidget.get());
- break;
- }
- default:
- Q_ASSERT(false);
- break;
- }
- // Let editor to show, or scrollToLine will not work correctly.
- QCoreApplication::processEvents();
- if (p_syncBuffer) {
- doSyncEditorFromBufferContent(true);
- }
- emit modeChanged();
- if (m_findAndReplace && m_findAndReplace->isVisible()) {
- m_findAndReplace->setReplaceEnabled(!isReadMode());
- }
- }
- void MarkdownViewWindow::setModified(bool p_modified)
- {
- if (m_editor) {
- m_editor->setModified(p_modified);
- }
- }
- void MarkdownViewWindow::handleEditorConfigChange()
- {
- const auto &editorConfig = ConfigMgr::getInst().getEditorConfig();
- const auto &markdownEditorConfig = editorConfig.getMarkdownEditorConfig();
- if (updateConfigRevision()) {
- updatePreviewHelperFromConfig(markdownEditorConfig);
- HtmlTemplateHelper::updateMarkdownViewerTemplate(markdownEditorConfig);
- if (m_editor) {
- auto config = createMarkdownEditorConfig(editorConfig, markdownEditorConfig);
- m_editor->setConfig(config);
- m_editor->updateFromConfig();
- }
- updateWebViewerConfig(markdownEditorConfig);
- }
- }
- QString MarkdownViewWindow::getLatestContent() const
- {
- Q_ASSERT(m_editor);
- return m_editor->getText();
- }
- void MarkdownViewWindow::syncEditorFromBuffer()
- {
- m_bufferRevision = getBuffer() ? getBuffer()->getRevision() : 0;
- syncTextEditorFromBuffer(false);
- syncViewerFromBuffer(false);
- }
- void MarkdownViewWindow::syncEditorFromBufferContent()
- {
- doSyncEditorFromBufferContent(false);
- }
- void MarkdownViewWindow::doSyncEditorFromBufferContent(bool p_syncPosition)
- {
- auto buffer = getBuffer();
- Q_ASSERT(buffer);
- // m_bufferRevision may already be the same as the buffer revision, in which
- // case we will call editor or viewer to update its content.
- m_bufferRevision = buffer->getRevision();
- switch (m_mode) {
- case ViewWindowMode::Read:
- syncViewerFromBufferContent(p_syncPosition);
- break;
- case ViewWindowMode::Edit:
- syncTextEditorFromBufferContent(p_syncPosition);
- break;
- default:
- Q_ASSERT(false);
- }
- }
- void MarkdownViewWindow::handleBufferChangedInternal(const QSharedPointer<FileOpenParameters> &p_paras)
- {
- if (getBuffer()) {
- // Will sync buffer right behind this.
- setModeInternal(p_paras ? p_paras->m_mode : ViewWindowMode::Read, false);
- }
- TextViewWindowHelper::handleBufferChanged(this);
- handleFileOpenParameters(p_paras, false);
- }
- void MarkdownViewWindow::setupToolBar()
- {
- auto toolBar = createToolBar(this);
- addToolBar(toolBar);
- addAction(toolBar, ViewWindowToolBarHelper::EditReadDiscard);
- addAction(toolBar, ViewWindowToolBarHelper::Save);
- addAction(toolBar, ViewWindowToolBarHelper::ViewMode);
- toolBar->addSeparator();
- addAction(toolBar, ViewWindowToolBarHelper::Attachment);
- addAction(toolBar, ViewWindowToolBarHelper::Tag);
- toolBar->addSeparator();
- addAction(toolBar, ViewWindowToolBarHelper::SectionNumber);
- {
- auto act = addAction(toolBar, ViewWindowToolBarHelper::InplacePreview);
- connect(act, &QAction::triggered,
- this, [this](bool p_checked) {
- if (!isReadMode()) {
- m_editor->setInplacePreviewEnabled(p_checked);
- }
- });
- connect(this, &ViewWindow::modeChanged,
- this, [this, act]() {
- act->setEnabled(inModeCanInsert() && getBuffer());
- });
- }
- addAction(toolBar, ViewWindowToolBarHelper::ImageHost);
- toolBar->addSeparator();
- addAction(toolBar, ViewWindowToolBarHelper::TypeHeading);
- addAction(toolBar, ViewWindowToolBarHelper::TypeBold);
- addAction(toolBar, ViewWindowToolBarHelper::TypeItalic);
- addAction(toolBar, ViewWindowToolBarHelper::TypeStrikethrough);
- addAction(toolBar, ViewWindowToolBarHelper::TypeMark);
- addAction(toolBar, ViewWindowToolBarHelper::TypeUnorderedList);
- addAction(toolBar, ViewWindowToolBarHelper::TypeOrderedList);
- addAction(toolBar, ViewWindowToolBarHelper::TypeTodoList);
- addAction(toolBar, ViewWindowToolBarHelper::TypeCheckedTodoList);
- addAction(toolBar, ViewWindowToolBarHelper::TypeCode);
- addAction(toolBar, ViewWindowToolBarHelper::TypeCodeBlock);
- addAction(toolBar, ViewWindowToolBarHelper::TypeMath);
- addAction(toolBar, ViewWindowToolBarHelper::TypeMathBlock);
- addAction(toolBar, ViewWindowToolBarHelper::TypeQuote);
- addAction(toolBar, ViewWindowToolBarHelper::TypeLink);
- addAction(toolBar, ViewWindowToolBarHelper::TypeImage);
- addAction(toolBar, ViewWindowToolBarHelper::TypeTable);
- ToolBarHelper::addSpacer(toolBar);
- addAction(toolBar, ViewWindowToolBarHelper::Outline);
- addAction(toolBar, ViewWindowToolBarHelper::FindAndReplace);
- addAction(toolBar, ViewWindowToolBarHelper::Print);
- {
- auto act = addAction(toolBar, ViewWindowToolBarHelper::Debug);
- connect(this, &ViewWindow::modeChanged,
- this, [this, act]() {
- act->setEnabled(m_mode == ViewWindowMode::Read || m_editViewMode != MarkdownEditorConfig::EditViewMode::EditOnly);
- });
- }
- }
- void MarkdownViewWindow::setupTextEditor()
- {
- Q_ASSERT(!m_editor);
- Q_ASSERT(m_viewer);
- const auto &editorConfig = ConfigMgr::getInst().getEditorConfig();
- const auto &markdownEditorConfig = editorConfig.getMarkdownEditorConfig();
- updateConfigRevision();
- m_editor = new MarkdownEditor(markdownEditorConfig,
- createMarkdownEditorConfig(editorConfig, markdownEditorConfig),
- createMarkdownEditorParameters(editorConfig, markdownEditorConfig),
- this);
- // Always editor comes first.
- m_splitter->insertWidget(0, m_editor);
- TextViewWindowHelper::connectEditor(this);
- // Status widget.
- m_textEditorStatusWidget = m_editor->statusWidget();
- getMainStatusWidget()->addWidget(m_textEditorStatusWidget.get());
- m_textEditorStatusWidget->show();
- m_previewHelper->setMarkdownEditor(m_editor);
- m_editor->setPreviewHelper(m_previewHelper);
- m_editor->setImageHost(m_imageHost);
- // Connect viewer and editor.
- connect(adapter(), &MarkdownViewerAdapter::viewerReady,
- m_editor->getHighlighter(), &vte::PegMarkdownHighlighter::updateHighlight);
- connect(m_editor, &MarkdownEditor::htmlToMarkdownRequested,
- adapter(), &MarkdownViewerAdapter::htmlToMarkdownRequested);
- connect(adapter(), &MarkdownViewerAdapter::htmlToMarkdownReady,
- m_editor, &MarkdownEditor::handleHtmlToMarkdownData);
- // Connect outline pipeline.
- connect(m_editor, &MarkdownEditor::headingsChanged,
- this, [this]() {
- if (!isReadMode()) {
- auto outline = headingsToOutline(m_editor->getHeadings());
- m_outlineProvider->setOutline(outline);
- }
- });
- connect(m_editor, &MarkdownEditor::currentHeadingChanged,
- this, [this]() {
- if (!isReadMode()) {
- m_outlineProvider->setCurrentHeadingIndex(m_editor->getCurrentHeadingIndex());
- }
- });
- connect(m_editor, &MarkdownEditor::readRequested,
- this, [this]() {
- read(true);
- });
- connect(m_editor, &MarkdownEditor::applySnippetRequested,
- this, QOverload<>::of(&MarkdownViewWindow::applySnippet));
- }
- QStackedWidget *MarkdownViewWindow::getMainStatusWidget() const
- {
- return m_mainStatusWidget.data();
- }
- bool MarkdownViewWindow::eventFilter(QObject *p_obj, QEvent *p_event)
- {
- if (p_obj == m_splitter) {
- if (p_event->type() == QEvent::FocusIn) {
- focusEditor();
- }
- }
- return ViewWindow::eventFilter(p_obj, p_event);
- }
- void MarkdownViewWindow::focusEditor()
- {
- switch (m_mode) {
- case ViewWindowMode::Read:
- m_viewer->setFocus();
- break;
- case ViewWindowMode::Edit:
- m_editor->setFocus();
- break;
- default:
- Q_ASSERT(false);
- break;
- }
- }
- void MarkdownViewWindow::setupViewer()
- {
- Q_ASSERT(!m_viewer);
- const auto &editorConfig = ConfigMgr::getInst().getEditorConfig();
- const auto &markdownEditorConfig = editorConfig.getMarkdownEditorConfig();
- updateConfigRevision();
- HtmlTemplateHelper::updateMarkdownViewerTemplate(markdownEditorConfig);
- auto adapter = new EditorMarkdownViewerAdapter(nullptr, this);
- m_viewer = new MarkdownViewer(adapter,
- this,
- VNoteX::getInst().getThemeMgr().getBaseBackground(),
- markdownEditorConfig.getZoomFactorInReadMode(),
- this);
- m_splitter->addWidget(m_viewer);
- // Status widget.
- {
- // TODO: implement a real status widget for viewer.
- auto label = new QLabel(tr("Markdown Viewer"), this);
- label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
- m_viewerStatusWidget.reset(label);
- getMainStatusWidget()->addWidget(m_viewerStatusWidget.get());
- m_viewerStatusWidget->show();
- }
- m_viewer->setPreviewHelper(m_previewHelper);
- connect(m_viewer, &MarkdownViewer::zoomFactorChanged,
- this, [this](qreal p_factor) {
- auto &markdownEditorConfig = ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig();
- markdownEditorConfig.setZoomFactorInReadMode(p_factor);
- showZoomFactor(p_factor);
- });
- connect(m_viewer, &WebViewer::linkHovered,
- this, [this](const QString &p_url) {
- showMessage(p_url);
- });
- connect(m_viewer, &MarkdownViewer::editRequested,
- this, [this]() {
- edit();
- });
- // Connect outline pipeline.
- connect(adapter, &MarkdownViewerAdapter::headingsChanged,
- this, [this]() {
- if (isReadMode()) {
- auto outline = headingsToOutline(this->adapter()->getHeadings());
- m_outlineProvider->setOutline(outline);
- }
- });
- connect(adapter, &MarkdownViewerAdapter::currentHeadingChanged,
- this, [this]() {
- if (isReadMode()) {
- m_outlineProvider->setCurrentHeadingIndex(this->adapter()->getCurrentHeadingIndex());
- }
- });
- connect(adapter, &MarkdownViewerAdapter::findTextReady,
- this, [this](const QStringList &p_texts, int p_totalMatches, int p_currentMatchIndex) {
- this->showFindResult(p_texts, p_totalMatches, p_currentMatchIndex);
- });
- connect(adapter, &MarkdownViewerAdapter::viewerReady,
- this, [this]() {
- m_viewerReady = true;
- if (m_mode == ViewWindowMode::Edit) {
- setEditViewMode(m_editViewMode);
- }
- });
- }
- void MarkdownViewWindow::syncTextEditorFromBuffer(bool p_syncPositionFromReadMode)
- {
- if (!m_editor) {
- return;
- }
- const bool old = m_propogateEditorToBuffer;
- m_propogateEditorToBuffer = false;
- auto buffer = getBuffer();
- m_editor->setBuffer(buffer);
- if (buffer) {
- m_editor->setReadOnly(buffer->isReadOnly());
- m_editor->setBasePath(buffer->getResourcePath());
- m_editor->setText(buffer->getContent());
- m_editor->setModified(buffer->isModified());
- int lineNumber = -1;
- if (p_syncPositionFromReadMode) {
- lineNumber = getReadLineNumber();
- }
- m_editor->scrollToLine(lineNumber, false);
- } else {
- m_editor->setReadOnly(true);
- m_editor->setBasePath("");
- m_editor->setText("");
- m_editor->setModified(false);
- }
- m_textEditorBufferRevision = m_bufferRevision;
- m_propogateEditorToBuffer = old;
- }
- void MarkdownViewWindow::syncViewerFromBuffer(bool p_syncPositionFromEditMode)
- {
- if (!m_viewer) {
- return;
- }
- auto buffer = getBuffer();
- adapter()->setBuffer(buffer);
- if (buffer) {
- int lineNumber = -1;
- if (p_syncPositionFromEditMode) {
- lineNumber = getEditLineNumber();
- }
- // TODO: Check buffer for last position recover.
- // Use getPath() instead of getBasePath() to make in-page anchor work.
- adapter()->reset();
- m_viewer->setHtml(HtmlTemplateHelper::getMarkdownViewerTemplate(),
- PathUtils::pathToUrl(buffer->getContentPath()));
- adapter()->setText(m_bufferRevision, buffer->getContent(), lineNumber);
- } else {
- adapter()->reset();
- m_viewer->setHtml("");
- adapter()->setText(0, "", -1);
- }
- m_viewerBufferRevision = m_bufferRevision;
- }
- void MarkdownViewWindow::syncTextEditorFromBufferContent(bool p_syncPosition)
- {
- Q_ASSERT(m_editor);
- if (m_textEditorBufferRevision == m_bufferRevision) {
- if (p_syncPosition) {
- m_editor->scrollToLine(getReadLineNumber(), false);
- }
- return;
- }
- const bool old = m_propogateEditorToBuffer;
- m_propogateEditorToBuffer = false;
- auto buffer = getBuffer();
- Q_ASSERT(buffer);
- m_editor->setText(buffer->getContent());
- m_editor->setModified(buffer->isModified());
- m_textEditorBufferRevision = m_bufferRevision;
- m_propogateEditorToBuffer = old;
- }
- void MarkdownViewWindow::syncViewerFromBufferContent(bool p_syncPosition)
- {
- Q_ASSERT(m_viewer);
- if (m_viewerBufferRevision == m_bufferRevision) {
- if (p_syncPosition) {
- adapter()->scrollToPosition(MarkdownViewerAdapter::Position(getEditLineNumber(), QString()));
- }
- return;
- }
- auto buffer = getBuffer();
- Q_ASSERT(buffer);
- adapter()->setText(m_bufferRevision,
- buffer->getContent(),
- p_syncPosition ? getEditLineNumber() : -1);
- m_viewerBufferRevision = m_bufferRevision;
- }
- void MarkdownViewWindow::setBufferRevisionAfterInvalidation(int p_bufferRevision)
- {
- m_bufferRevision = p_bufferRevision;
- switch (m_mode) {
- case ViewWindowMode::Edit:
- m_textEditorBufferRevision = p_bufferRevision;
- break;
- case ViewWindowMode::Read:
- m_viewerBufferRevision = p_bufferRevision;
- break;
- default:
- Q_ASSERT(false);
- }
- }
- EditorMarkdownViewerAdapter *MarkdownViewWindow::adapter() const
- {
- if (m_viewer) {
- return dynamic_cast<EditorMarkdownViewerAdapter *>(m_viewer->adapter());
- }
- return nullptr;
- }
- int MarkdownViewWindow::getEditLineNumber() const
- {
- if (m_previousMode == ViewWindowMode::Edit) {
- if (m_editor) {
- return m_editor->getTopLine();
- }
- }
- return -1;
- }
- int MarkdownViewWindow::getReadLineNumber() const
- {
- if (m_previousMode == ViewWindowMode::Read) {
- if (m_viewer) {
- return adapter()->getTopLineNumber();
- }
- }
- return -1;
- }
- void MarkdownViewWindow::handleTypeAction(TypeAction p_action)
- {
- Q_ASSERT(inModeCanInsert() && m_editor);
- Q_ASSERT(!getBuffer()->isReadOnly());
- switch (p_action) {
- case TypeAction::Heading1:
- Q_FALLTHROUGH();
- case TypeAction::Heading2:
- Q_FALLTHROUGH();
- case TypeAction::Heading3:
- Q_FALLTHROUGH();
- case TypeAction::Heading4:
- Q_FALLTHROUGH();
- case TypeAction::Heading5:
- Q_FALLTHROUGH();
- case TypeAction::Heading6:
- m_editor->typeHeading(p_action - TypeAction::Heading1 + 1);
- break;
- case TypeAction::HeadingNone:
- m_editor->typeHeading(0);
- break;
- case TypeAction::Bold:
- m_editor->typeBold();
- break;
- case TypeAction::Italic:
- m_editor->typeItalic();
- break;
- case TypeAction::Strikethrough:
- m_editor->typeStrikethrough();
- break;
- case TypeAction::Mark:
- m_editor->typeMark();
- break;
- case TypeAction::UnorderedList:
- m_editor->typeUnorderedList();
- break;
- case TypeAction::OrderedList:
- m_editor->typeOrderedList();
- break;
- case TypeAction::TodoList:
- m_editor->typeTodoList(false);
- break;
- case TypeAction::CheckedTodoList:
- m_editor->typeTodoList(true);
- break;
- case TypeAction::Code:
- m_editor->typeCode();
- break;
- case TypeAction::CodeBlock:
- m_editor->typeCodeBlock();
- break;
- case TypeAction::Math:
- m_editor->typeMath();
- break;
- case TypeAction::MathBlock:
- m_editor->typeMathBlock();
- break;
- case TypeAction::Quote:
- m_editor->typeQuote();
- break;
- case TypeAction::Link:
- m_editor->typeLink();
- break;
- case TypeAction::Image:
- m_editor->typeImage();
- break;
- case TypeAction::Table:
- m_editor->typeTable();
- break;
- default:
- qWarning() << "TypeAction not handled" << p_action;
- break;
- }
- }
- void MarkdownViewWindow::handleSectionNumberOverride(OverrideState p_state)
- {
- if (!isReadMode()) {
- m_editor->overrideSectionNumber(p_state);
- }
- }
- void MarkdownViewWindow::detachFromBufferInternal()
- {
- auto buffer = getBuffer();
- if (buffer && buffer->getAttachViewWindowCount() == 1) {
- const auto state = buffer->state();
- if (state == Buffer::StateFlag::Normal || state == Buffer::StateFlag::Discarded) {
- // We are the last ViewWindow of this buffer. Clear obsolete images.
- clearObsoleteImages();
- }
- }
- }
- void MarkdownViewWindow::clearObsoleteImages()
- {
- const auto obsoleteImages = static_cast<MarkdownBuffer *>(getBuffer())->clearObsoleteImages();
- if (obsoleteImages.isEmpty()) {
- return;
- }
- auto buffer = getBuffer();
- Q_ASSERT(buffer);
- auto &editorConfig = ConfigMgr::getInst().getEditorConfig();
- auto &markdownEditorConfig = editorConfig.getMarkdownEditorConfig();
- const bool clearRemote = editorConfig.isClearObsoleteImageAtImageHostEnabled();
- const auto &hostMgr = ImageHostMgr::getInst();
- QVector<QPair<QString, bool>> imagesToDelete;
- imagesToDelete.reserve(obsoleteImages.size());
- if (markdownEditorConfig.getConfirmBeforeClearObsoleteImages()) {
- QVector<ConfirmItemInfo> items;
- for (auto it = obsoleteImages.constBegin(); it != obsoleteImages.constEnd(); ++it) {
- if (!it.value() || (clearRemote && hostMgr.findByImageUrl(it.key()))) {
- const auto imgPath = it.key();
- // Use the @m_data field to denote whether it is remote.
- items.push_back(ConfirmItemInfo(imgPath, imgPath, imgPath, it.value() ? reinterpret_cast<void *>(1ULL) : nullptr));
- }
- }
- if (items.isEmpty()) {
- return;
- }
- DeleteConfirmDialog dialog(tr("Clear Obsolete Images"),
- tr("These images seems to be not in use anymore. Please confirm the deletion of them."),
- tr("Deleted local images could be found in the recycle bin of notebook if it is from a bundle notebook."),
- items,
- DeleteConfirmDialog::Flag::AskAgain | DeleteConfirmDialog::Flag::Preview,
- false,
- this);
- if (dialog.exec() == QDialog::Accepted) {
- items = dialog.getConfirmedItems();
- markdownEditorConfig.setConfirmBeforeClearObsoleteImages(!dialog.isNoAskChecked());
- for (const auto &item : items) {
- imagesToDelete.push_back(qMakePair(item.m_path, item.m_data != nullptr));
- }
- }
- } else {
- for (auto it = obsoleteImages.constBegin(); it != obsoleteImages.constEnd(); ++it) {
- if (clearRemote || !it.value()) {
- imagesToDelete.push_back(qMakePair(it.key(), it.value()));
- }
- }
- }
- if (imagesToDelete.isEmpty()) {
- return;
- }
- QProgressDialog proDlg(tr("Clearing obsolete images..."),
- tr("Abort"),
- 0,
- imagesToDelete.size(),
- this);
- proDlg.setWindowModality(Qt::WindowModal);
- proDlg.setWindowTitle(tr("Clear Obsolete Images"));
- int cnt = 0;
- for (int i = 0; i < imagesToDelete.size(); ++i) {
- proDlg.setValue(i + 1);
- if (proDlg.wasCanceled()) {
- break;
- }
- proDlg.setLabelText(tr("Clear image (%1)").arg(imagesToDelete[i].first));
- if (imagesToDelete[i].second) {
- removeFromImageHost(imagesToDelete[i].first);
- } else {
- buffer->removeImage(imagesToDelete[i].first);
- }
- ++cnt;
- }
- proDlg.setValue(imagesToDelete.size());
- // It may be deleted so showMessage() is not available.
- VNoteX::getInst().showStatusMessageShort(tr("Cleared %n obsolete images", "", cnt));
- }
- QSharedPointer<OutlineProvider> MarkdownViewWindow::getOutlineProvider()
- {
- return m_outlineProvider;
- }
- template <class T>
- QSharedPointer<Outline> MarkdownViewWindow::headingsToOutline(const QVector<T> &p_headings)
- {
- auto outline = QSharedPointer<Outline>::create();
- if (!p_headings.isEmpty()) {
- outline->m_headings.reserve(p_headings.size());
- for (const auto &heading : p_headings) {
- outline->m_headings.push_back(Outline::Heading(heading.m_name, heading.m_level));
- }
- }
- const auto &markdownConfig = ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig();
- if (markdownConfig.getSectionNumberMode() == MarkdownEditorConfig::SectionNumberMode::Edit) {
- outline->m_sectionNumberBaseLevel = -1;
- } else {
- outline->m_sectionNumberBaseLevel = markdownConfig.getSectionNumberBaseLevel();
- outline->m_sectionNumberEndingDot = markdownConfig.getSectionNumberStyle() == MarkdownEditorConfig::SectionNumberStyle::DigDotDigDot;
- }
- return outline;
- }
- void MarkdownViewWindow::setupOutlineProvider()
- {
- m_outlineProvider.reset(new OutlineProvider(nullptr));
- connect(m_outlineProvider.data(), &OutlineProvider::headingClicked,
- this, [this](int p_idx) {
- switch (getMode()) {
- case ViewWindowMode::Read:
- adapter()->scrollToHeading(p_idx);
- break;
- default:
- m_editor->scrollToHeading(p_idx);
- break;
- }
- });
- }
- QSharedPointer<vte::MarkdownEditorConfig> MarkdownViewWindow::createMarkdownEditorConfig(const EditorConfig &p_editorConfig, const MarkdownEditorConfig &p_config)
- {
- const auto &themeMgr = VNoteX::getInst().getThemeMgr();
- auto textEditorConfig = TextViewWindowHelper::createTextEditorConfig(p_config.getTextEditorConfig(),
- p_editorConfig.getViConfig(),
- themeMgr.getFile(Theme::File::MarkdownEditorStyle),
- themeMgr.getMarkdownEditorHighlightTheme(),
- p_editorConfig.getLineEndingPolicy());
- auto editorConfig = QSharedPointer<vte::MarkdownEditorConfig>::create(textEditorConfig);
- editorConfig->overrideTextFontFamily(p_config.getEditorOverriddenFontFamily());
- editorConfig->m_constrainInplacePreviewWidthEnabled = p_config.getConstrainInplacePreviewWidthEnabled();
- {
- auto srcs = p_config.getInplacePreviewSources();
- vte::MarkdownEditorConfig::InplacePreviewSources editorSrcs = vte::MarkdownEditorConfig::NoInplacePreview;
- if (srcs & MarkdownEditorConfig::InplacePreviewSource::ImageLink) {
- editorSrcs |= vte::MarkdownEditorConfig::ImageLink;
- }
- if (srcs & MarkdownEditorConfig::InplacePreviewSource::CodeBlock) {
- editorSrcs |= vte::MarkdownEditorConfig::CodeBlock;
- }
- if (srcs & MarkdownEditorConfig::InplacePreviewSource::Math) {
- editorSrcs |= vte::MarkdownEditorConfig::Math;
- }
- editorConfig->m_inplacePreviewSources = editorSrcs;
- }
- return editorConfig;
- }
- QSharedPointer<vte::TextEditorParameters> MarkdownViewWindow::createMarkdownEditorParameters(const EditorConfig &p_editorConfig, const MarkdownEditorConfig &p_config)
- {
- auto paras = QSharedPointer<vte::TextEditorParameters>::create();
- paras->m_spellCheckEnabled = p_config.isSpellCheckEnabled();
- paras->m_autoDetectLanguageEnabled = p_editorConfig.isSpellCheckAutoDetectLanguageEnabled();
- paras->m_defaultSpellCheckLanguage = p_editorConfig.getSpellCheckDefaultDictionary();
- return paras;
- }
- void MarkdownViewWindow::scrollUp()
- {
- if (isReadMode()) {
- adapter()->scroll(true);
- } else {
- QScrollBar *vbar = m_editor->getTextEdit()->verticalScrollBar();
- if (vbar && (vbar->minimum() != vbar->maximum())) {
- vbar->triggerAction(QAbstractSlider::SliderSingleStepAdd);
- }
- }
- }
- void MarkdownViewWindow::scrollDown()
- {
- if (isReadMode()) {
- adapter()->scroll(false);
- } else {
- QScrollBar *vbar = m_editor->getTextEdit()->verticalScrollBar();
- if (vbar && (vbar->minimum() != vbar->maximum())) {
- vbar->triggerAction(QAbstractSlider::SliderSingleStepSub);
- }
- }
- }
- void MarkdownViewWindow::updateWebViewerConfig(const MarkdownEditorConfig &p_config)
- {
- if (!m_viewer) {
- return;
- }
- m_viewer->setZoomFactor(p_config.getZoomFactorInReadMode());
- }
- void MarkdownViewWindow::zoom(bool p_zoomIn)
- {
- // Only editor will receive the wheel event.
- Q_ASSERT(!isReadMode());
- m_editor->zoom(m_editor->zoomDelta() + (p_zoomIn ? 1 : -1));
- auto &textEditorConfig = ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig().getTextEditorConfig();
- textEditorConfig.setZoomDelta(m_editor->zoomDelta());
- showZoomDelta(m_editor->zoomDelta());
- }
- void MarkdownViewWindow::handleFindTextChanged(const QString &p_text, FindOptions p_options)
- {
- if (isReadMode()) {
- if (p_options & FindOption::IncrementalSearch) {
- adapter()->findText(QStringList(p_text), p_options);
- }
- } else {
- TextViewWindowHelper::handleFindTextChanged(this, p_text, p_options);
- }
- }
- void MarkdownViewWindow::handleFindNext(const QStringList &p_texts, FindOptions p_options)
- {
- if (isReadMode()) {
- adapter()->findText(p_texts, p_options);
- } else {
- TextViewWindowHelper::handleFindNext(this, p_texts, p_options);
- }
- }
- void MarkdownViewWindow::handleReplace(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
- {
- if (isReadMode()) {
- showMessage(tr("Replace is not supported in read mode"));
- } else {
- TextViewWindowHelper::handleReplace(this, p_text, p_options, p_replaceText);
- }
- }
- void MarkdownViewWindow::handleReplaceAll(const QString &p_text, FindOptions p_options, const QString &p_replaceText)
- {
- if (isReadMode()) {
- showMessage(tr("Replace is not supported in read mode"));
- } else {
- TextViewWindowHelper::handleReplaceAll(this, p_text, p_options, p_replaceText);
- }
- }
- void MarkdownViewWindow::handleFindAndReplaceWidgetClosed()
- {
- if (m_editor) {
- TextViewWindowHelper::handleFindAndReplaceWidgetClosed(this);
- } else {
- adapter()->findText(QStringList(), FindOption::FindNone);
- }
- }
- void MarkdownViewWindow::handleFindAndReplaceWidgetOpened()
- {
- Q_ASSERT(m_findAndReplace);
- m_findAndReplace->setReplaceEnabled(!isReadMode());
- }
- void MarkdownViewWindow::handleFileOpenParameters(const QSharedPointer<FileOpenParameters> &p_paras, bool p_twice)
- {
- if (!p_paras) {
- return;
- }
- auto buffer = getBuffer();
- if (p_paras->m_newFile) {
- Q_ASSERT(!isReadMode());
- const auto &markdownEditorConfig = ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig();
- if (markdownEditorConfig.getInsertFileNameAsTitle() && buffer->getContent().isEmpty()) {
- const auto title = QString("# %1\n").arg(QFileInfo(buffer->getName()).completeBaseName());
- m_editor->insertText(title);
- }
- } else {
- if (!p_twice || p_paras->m_forceMode) {
- setMode(p_paras->m_mode);
- }
- scrollToLine(p_paras->m_lineNumber);
- if (p_paras->m_searchToken) {
- findTextBySearchToken(p_paras->m_searchToken, p_paras->m_lineNumber);
- }
- }
- }
- void MarkdownViewWindow::scrollToLine(int p_lineNumber)
- {
- if (p_lineNumber < 0) {
- return;
- }
- if (isReadMode()) {
- Q_ASSERT(m_viewer);
- adapter()->scrollToPosition(MarkdownViewerAdapter::Position(p_lineNumber, QString()));
- } else {
- Q_ASSERT(m_editor);
- m_editor->scrollToLine(p_lineNumber, true);
- }
- }
- void MarkdownViewWindow::findTextBySearchToken(const QSharedPointer<SearchToken> &p_token, int p_currentMatchLine)
- {
- if (isReadMode()) {
- Q_ASSERT(m_viewer);
- const auto patterns = p_token->toPatterns();
- updateLastFindInfo(patterns.first, patterns.second);
- adapter()->findText(patterns.first, patterns.second, p_currentMatchLine);
- } else {
- Q_ASSERT(m_editor);
- TextViewWindowHelper::findTextBySearchToken(this, p_token, p_currentMatchLine);
- }
- }
- bool MarkdownViewWindow::isReadMode() const
- {
- return m_mode == ViewWindowMode::Read;
- }
- void MarkdownViewWindow::openTwice(const QSharedPointer<FileOpenParameters> &p_paras)
- {
- Q_ASSERT(!p_paras || !p_paras->m_newFile);
- handleFileOpenParameters(p_paras, true);
- }
- ViewWindowSession MarkdownViewWindow::saveSession() const
- {
- auto session = ViewWindow::saveSession();
- if (getBuffer()) {
- session.m_lineNumber = isReadMode() ? adapter()->getTopLineNumber()
- : m_editor->getCursorPosition().first;
- }
- return session;
- }
- void MarkdownViewWindow::setupPreviewHelper()
- {
- Q_ASSERT(!m_previewHelper);
- m_previewHelper = new PreviewHelper(nullptr, this);
- const auto &markdownEditorConfig = ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig();
- updatePreviewHelperFromConfig(markdownEditorConfig);
- }
- void MarkdownViewWindow::updatePreviewHelperFromConfig(const MarkdownEditorConfig &p_config)
- {
- m_previewHelper->setWebPlantUmlEnabled(p_config.getWebPlantUml());
- m_previewHelper->setWebGraphvizEnabled(p_config.getWebGraphviz());
- const auto srcs = p_config.getInplacePreviewSources();
- m_previewHelper->setInplacePreviewCodeBlocksEnabled(srcs & MarkdownEditorConfig::CodeBlock);
- m_previewHelper->setInplacePreviewMathBlocksEnabled(srcs & MarkdownEditorConfig::Math);
- }
- void MarkdownViewWindow::applySnippet(const QString &p_name)
- {
- if (isReadMode()) {
- qWarning() << "failed to apply snippet in read mode" << p_name;
- return;
- }
- TextViewWindowHelper::applySnippet(this, p_name);
- }
- void MarkdownViewWindow::applySnippet()
- {
- if (isReadMode()) {
- qWarning() << "failed to apply snippet in read mode";
- return;
- }
- TextViewWindowHelper::applySnippet(this);
- }
- QPoint MarkdownViewWindow::getFloatingWidgetPosition()
- {
- return TextViewWindowHelper::getFloatingWidgetPosition(this);
- }
- QString MarkdownViewWindow::selectedText() const
- {
- switch (m_mode) {
- case ViewWindowMode::Read:
- Q_ASSERT(m_viewer);
- return m_viewer->selectedText();
- case ViewWindowMode::Edit:
- Q_ASSERT(m_editor);
- return m_editor->getTextEdit()->selectedText();
- default:
- return QString();
- }
- }
- void MarkdownViewWindow::handleImageHostChanged(const QString &p_hostName)
- {
- m_imageHost = ImageHostMgr::getInst().find(p_hostName);
- if (m_editor) {
- m_editor->setImageHost(m_imageHost);
- }
- }
- void MarkdownViewWindow::removeFromImageHost(const QString &p_url)
- {
- auto host = ImageHostMgr::getInst().findByImageUrl(p_url);
- if (!host) {
- return;
- }
- QString errMsg;
- QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- auto ret = host->remove(p_url, errMsg);
- QApplication::restoreOverrideCursor();
- if (!ret) {
- MessageBoxHelper::notify(MessageBoxHelper::Warning,
- QString("Failed to delete image (%1) from image host (%2).").arg(p_url, host->getName()),
- QString(),
- errMsg,
- this);
- }
- }
- bool MarkdownViewWindow::updateConfigRevision()
- {
- bool changed = false;
- const auto &editorConfig = ConfigMgr::getInst().getEditorConfig();
- if (m_editorConfigRevision != editorConfig.revision()) {
- changed = true;
- m_editorConfigRevision = editorConfig.revision();
- }
- if (m_textEditorConfigRevision != editorConfig.getTextEditorConfig().revision()) {
- changed = true;
- m_textEditorConfigRevision = editorConfig.getTextEditorConfig().revision();
- }
- if (m_markdownEditorConfigRevision != editorConfig.getMarkdownEditorConfig().revision()) {
- changed = true;
- m_markdownEditorConfigRevision = editorConfig.getMarkdownEditorConfig().revision();
- }
- return changed;
- }
- void MarkdownViewWindow::toggleDebug()
- {
- Q_ASSERT(m_viewer);
- if (m_debugViewer) {
- bool shouldEnable = !m_debugViewer->isVisible();
- m_debugViewer->setVisible(shouldEnable);
- m_viewer->page()->setDevToolsPage(shouldEnable ? m_debugViewer->page() : nullptr);
- } else {
- setupDebugViewer();
- m_viewer->page()->setDevToolsPage(m_debugViewer->page());
- }
- }
- void MarkdownViewWindow::setupDebugViewer()
- {
- Q_ASSERT(!m_debugViewer);
- // Need a vertical QSplitter to hold the original QSplitter and the debug viewer.
- auto mainSplitter = new QSplitter(this);
- mainSplitter->setContentsMargins(0, 0, 0, 0);
- mainSplitter->setOrientation(Qt::Vertical);
- replaceCentralWidget(mainSplitter);
- mainSplitter->addWidget(m_splitter);
- mainSplitter->setFocusProxy(m_splitter);
- m_debugViewer = new WebViewer(VNoteX::getInst().getThemeMgr().getBaseBackground(), this);
- m_debugViewer->resize(m_splitter->width(), m_splitter->height() / 2);
- mainSplitter->addWidget(m_debugViewer);
- }
- void MarkdownViewWindow::updateViewModeMenu(QMenu *p_menu)
- {
- p_menu->clear();
- if (isReadMode()) {
- auto act = p_menu->addAction(tr("View Mode Not Supported In Read Mode"));
- act->setEnabled(false);
- return;
- }
- if (!m_viewModeActionGroup) {
- m_viewModeActionGroup = new QActionGroup(this);
- connect(m_viewModeActionGroup, &QActionGroup::triggered,
- this, [this](QAction *act) {
- auto mode = static_cast<MarkdownEditorConfig::EditViewMode>(act->data().toInt());
- if (mode != m_editViewMode) {
- ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig().setEditViewMode(mode);
- setEditViewMode(mode);
- }
- });
- }
- {
- auto act = p_menu->addAction(tr("Edit Only"));
- act->setCheckable(true);
- act->setData(static_cast<int>(MarkdownEditorConfig::EditViewMode::EditOnly));
- m_viewModeActionGroup->addAction(act);
- if (act->data().toInt() == m_editViewMode) {
- act->setChecked(true);
- }
- }
- {
- auto act = p_menu->addAction(tr("Edit with Preview"));
- act->setCheckable(true);
- act->setData(static_cast<int>(MarkdownEditorConfig::EditViewMode::EditPreview));
- m_viewModeActionGroup->addAction(act);
- if (act->data().toInt() == m_editViewMode) {
- act->setChecked(true);
- }
- }
- }
- void MarkdownViewWindow::setEditViewMode(MarkdownEditorConfig::EditViewMode p_mode)
- {
- Q_ASSERT(m_mode == ViewWindowMode::Edit);
- bool modeChanged = false;
- if (m_editViewMode != p_mode) {
- m_editViewMode = p_mode;
- modeChanged = true;
- }
- switch (p_mode) {
- case MarkdownEditorConfig::EditViewMode::EditOnly:
- {
- if (m_viewerReady) {
- m_viewer->hide();
- }
- if (modeChanged) {
- disconnect(m_editor->getTextEdit(), &vte::VTextEdit::contentsChanged,
- m_syncPreviewTimer, QOverload<>::of(&QTimer::start));
- disconnect(m_editor, &MarkdownEditor::topLineChanged,
- this, &MarkdownViewWindow::syncEditorPositionToPreview);
- }
- break;
- }
- case MarkdownEditorConfig::EditViewMode::EditPreview:
- {
- m_viewer->show();
- WidgetUtils::distributeWidgetsOfSplitter(m_splitter);
- if (modeChanged) {
- if (!m_syncPreviewTimer) {
- m_syncPreviewTimer = new QTimer(this);
- m_syncPreviewTimer->setSingleShot(true);
- m_syncPreviewTimer->setInterval(300);
- connect(m_syncPreviewTimer, &QTimer::timeout,
- this, &MarkdownViewWindow::syncEditorContentsToPreview);
- }
- connect(m_editor->getTextEdit(), &vte::VTextEdit::contentsChanged,
- m_syncPreviewTimer, QOverload<>::of(&QTimer::start),
- Qt::UniqueConnection);
- connect(m_editor, &MarkdownEditor::topLineChanged,
- this, &MarkdownViewWindow::syncEditorPositionToPreview,
- Qt::UniqueConnection);
- }
- syncEditorContentsToPreview();
- break;
- }
- default:
- Q_ASSERT(false);
- break;
- }
- }
- void MarkdownViewWindow::syncEditorContentsToPreview()
- {
- if (!m_viewerReady || isReadMode() || m_editViewMode == MarkdownEditorConfig::EditViewMode::EditOnly) {
- return;
- }
- adapter()->setText(m_editor->getText(), m_editor->getTopLine());
- }
- void MarkdownViewWindow::syncEditorPositionToPreview()
- {
- if (!m_viewerReady || isReadMode() || m_editViewMode == MarkdownEditorConfig::EditViewMode::EditOnly) {
- return;
- }
- adapter()->scrollToPosition(MarkdownViewerAdapter::Position(m_editor->getTopLine(), QString()));
- }
- void MarkdownViewWindow::print()
- {
- if (!m_viewer || !m_viewerReady) {
- return;
- }
- auto printer = PrintUtils::promptForPrint(m_viewer->hasSelection(), this);
- if (printer) {
- m_viewer->page()->print(printer.data(), [printer](bool p_succeeded) mutable {
- Q_UNUSED(p_succeeded);
- printer.reset();
- });
- }
- }
|