mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 22:09:52 +08:00
MdEditor: support copying diagram in puml and graphviz
This commit is contained in:
parent
1fe975b1ad
commit
d4daf32f20
@ -44,6 +44,8 @@ public:
|
|||||||
|
|
||||||
const QVector<VElementRegion> &getImageRegions() const;
|
const QVector<VElementRegion> &getImageRegions() const;
|
||||||
|
|
||||||
|
const QVector<VCodeBlock> &getCodeBlocks() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Parse and rehighlight immediately.
|
// Parse and rehighlight immediately.
|
||||||
void updateHighlight();
|
void updateHighlight();
|
||||||
@ -360,4 +362,9 @@ inline bool PegMarkdownHighlighter::isFastParseBlock(int p_blockNum) const
|
|||||||
{
|
{
|
||||||
return p_blockNum >= m_fastParseBlocks.first && p_blockNum <= m_fastParseBlocks.second;
|
return p_blockNum >= m_fastParseBlocks.first && p_blockNum <= m_fastParseBlocks.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const QVector<VCodeBlock> &PegMarkdownHighlighter::getCodeBlocks() const
|
||||||
|
{
|
||||||
|
return m_result->m_codeBlocks;
|
||||||
|
}
|
||||||
#endif // PEGMARKDOWNHIGHLIGHTER_H
|
#endif // PEGMARKDOWNHIGHLIGHTER_H
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
|
||||||
#include "vutils.h"
|
#include "vutils.h"
|
||||||
|
#include "vcodeblockhighlighthelper.h"
|
||||||
|
|
||||||
void VEditUtils::removeBlock(QTextBlock &p_block, QString *p_text)
|
void VEditUtils::removeBlock(QTextBlock &p_block, QString *p_text)
|
||||||
{
|
{
|
||||||
@ -1083,3 +1084,11 @@ bool VEditUtils::isWordSeparator(QChar p_char)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VEditUtils::removeCodeBlockFence(const QString &p_text)
|
||||||
|
{
|
||||||
|
QString text = VCodeBlockHighlightHelper::unindentCodeBlock(p_text);
|
||||||
|
Q_ASSERT(text.startsWith("```") && text.endsWith("```"));
|
||||||
|
int idx = text.indexOf('\n') + 1;
|
||||||
|
return text.mid(idx, text.size() - idx - 3);
|
||||||
|
}
|
||||||
|
@ -211,6 +211,9 @@ public:
|
|||||||
|
|
||||||
static bool isWordSeparator(QChar p_char);
|
static bool isWordSeparator(QChar p_char);
|
||||||
|
|
||||||
|
// Remove the fence of fenced code block.
|
||||||
|
static QString removeCodeBlockFence(const QString &p_text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VEditUtils() {}
|
VEditUtils() {}
|
||||||
};
|
};
|
||||||
|
@ -11,24 +11,36 @@ int VProcessUtils::startProcess(const QString &p_program,
|
|||||||
QByteArray &p_out,
|
QByteArray &p_out,
|
||||||
QByteArray &p_err)
|
QByteArray &p_err)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
|
||||||
QScopedPointer<QProcess> process(new QProcess());
|
QScopedPointer<QProcess> process(new QProcess());
|
||||||
process->start(p_program, p_args);
|
process->start(p_program, p_args);
|
||||||
|
return startProcess(process.data(),
|
||||||
|
p_in,
|
||||||
|
p_exitCode,
|
||||||
|
p_out,
|
||||||
|
p_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
int VProcessUtils::startProcess(QProcess *p_process,
|
||||||
|
const QByteArray &p_in,
|
||||||
|
int &p_exitCode,
|
||||||
|
QByteArray &p_out,
|
||||||
|
QByteArray &p_err)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
if (!p_in.isEmpty()) {
|
if (!p_in.isEmpty()) {
|
||||||
if (process->write(p_in) == -1) {
|
if (p_process->write(p_in) == -1) {
|
||||||
process->closeWriteChannel();
|
p_process->closeWriteChannel();
|
||||||
qWarning() << "fail to write to QProcess:" << process->errorString();
|
qWarning() << "fail to write to QProcess:" << p_process->errorString();
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
process->closeWriteChannel();
|
p_process->closeWriteChannel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool finished = false;
|
bool finished = false;
|
||||||
bool started = false;
|
bool started = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
QProcess::ProcessError err = process->error();
|
QProcess::ProcessError err = p_process->error();
|
||||||
if (err == QProcess::FailedToStart
|
if (err == QProcess::FailedToStart
|
||||||
|| err == QProcess::Crashed) {
|
|| err == QProcess::Crashed) {
|
||||||
if (err == QProcess::FailedToStart) {
|
if (err == QProcess::FailedToStart) {
|
||||||
@ -41,34 +53,34 @@ int VProcessUtils::startProcess(const QString &p_program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (started) {
|
if (started) {
|
||||||
if (process->state() == QProcess::NotRunning) {
|
if (p_process->state() == QProcess::NotRunning) {
|
||||||
finished = true;
|
finished = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (process->state() != QProcess::NotRunning) {
|
if (p_process->state() != QProcess::NotRunning) {
|
||||||
started = true;
|
started = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process->waitForFinished(500)) {
|
if (p_process->waitForFinished(500)) {
|
||||||
// Finished.
|
// Finished.
|
||||||
finished = true;
|
finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finished) {
|
if (finished) {
|
||||||
QProcess::ExitStatus sta = process->exitStatus();
|
QProcess::ExitStatus sta = p_process->exitStatus();
|
||||||
if (sta == QProcess::CrashExit) {
|
if (sta == QProcess::CrashExit) {
|
||||||
ret = -1;
|
ret = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_exitCode = process->exitCode();
|
p_exitCode = p_process->exitCode();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p_out = process->readAllStandardOutput();
|
p_out = p_process->readAllStandardOutput();
|
||||||
p_err = process->readAllStandardError();
|
p_err = p_process->readAllStandardError();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -86,3 +98,18 @@ int VProcessUtils::startProcess(const QString &p_program,
|
|||||||
p_out,
|
p_out,
|
||||||
p_err);
|
p_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VProcessUtils::startProcess(const QString &p_cmd,
|
||||||
|
const QByteArray &p_in,
|
||||||
|
int &p_exitCode,
|
||||||
|
QByteArray &p_out,
|
||||||
|
QByteArray &p_err)
|
||||||
|
{
|
||||||
|
QScopedPointer<QProcess> process(new QProcess());
|
||||||
|
process->start(p_cmd);
|
||||||
|
return startProcess(process.data(),
|
||||||
|
p_in,
|
||||||
|
p_exitCode,
|
||||||
|
p_out,
|
||||||
|
p_err);
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
|
class QProcess;
|
||||||
|
|
||||||
class VProcessUtils
|
class VProcessUtils
|
||||||
{
|
{
|
||||||
@ -25,8 +26,20 @@ public:
|
|||||||
QByteArray &p_out,
|
QByteArray &p_out,
|
||||||
QByteArray &p_err);
|
QByteArray &p_err);
|
||||||
|
|
||||||
|
static int startProcess(const QString &p_cmd,
|
||||||
|
const QByteArray &p_in,
|
||||||
|
int &p_exitCode,
|
||||||
|
QByteArray &p_out,
|
||||||
|
QByteArray &p_err);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VProcessUtils() {}
|
VProcessUtils() {}
|
||||||
|
|
||||||
|
static int startProcess(QProcess *p_process,
|
||||||
|
const QByteArray &p_in,
|
||||||
|
int &p_exitCode,
|
||||||
|
QByteArray &p_out,
|
||||||
|
QByteArray &p_err);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VPROCESSUTILS_H
|
#endif // VPROCESSUTILS_H
|
||||||
|
@ -35,6 +35,17 @@ VEditor::~VEditor()
|
|||||||
if (m_completer->widget() == m_editor) {
|
if (m_completer->widget() == m_editor) {
|
||||||
m_completer->setWidget(NULL);
|
m_completer->setWidget(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::cleanUp()
|
||||||
|
{
|
||||||
|
for (auto const & file : m_tempFiles) {
|
||||||
|
VUtils::deleteFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_tempFiles.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditor::init()
|
void VEditor::init()
|
||||||
|
@ -254,6 +254,8 @@ protected:
|
|||||||
|
|
||||||
virtual int lineNumberAreaWidth() const = 0;
|
virtual int lineNumberAreaWidth() const = 0;
|
||||||
|
|
||||||
|
void addTempFile(const QString &p_file);
|
||||||
|
|
||||||
QWidget *m_editor;
|
QWidget *m_editor;
|
||||||
|
|
||||||
VEditorObject *m_object;
|
VEditorObject *m_object;
|
||||||
@ -309,6 +311,8 @@ private:
|
|||||||
|
|
||||||
QStringList generateCompletionCandidates() const;
|
QStringList generateCompletionCandidates() const;
|
||||||
|
|
||||||
|
void cleanUp();
|
||||||
|
|
||||||
QLabel *m_wrapLabel;
|
QLabel *m_wrapLabel;
|
||||||
QTimer *m_labelTimer;
|
QTimer *m_labelTimer;
|
||||||
|
|
||||||
@ -352,6 +356,9 @@ private:
|
|||||||
|
|
||||||
QSharedPointer<VTextEditCompleter> m_completer;
|
QSharedPointer<VTextEditCompleter> m_completer;
|
||||||
|
|
||||||
|
// Temp files needed to be delete.
|
||||||
|
QStringList m_tempFiles;
|
||||||
|
|
||||||
// Functions for private slots.
|
// Functions for private slots.
|
||||||
private:
|
private:
|
||||||
void labelTimerTimeout();
|
void labelTimerTimeout();
|
||||||
@ -456,4 +463,8 @@ inline QWidget *VEditor::getEditor() const
|
|||||||
return m_editor;
|
return m_editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void VEditor::addTempFile(const QString &p_file)
|
||||||
|
{
|
||||||
|
m_tempFiles.append(p_file);
|
||||||
|
}
|
||||||
#endif // VEDITOR_H
|
#endif // VEDITOR_H
|
||||||
|
@ -119,3 +119,26 @@ bool VGraphvizHelper::testGraphviz(const QString &p_dot, QString &p_msg)
|
|||||||
|
|
||||||
return ret == 0 && exitCode == 0;
|
return ret == 0 && exitCode == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray VGraphvizHelper::process(const QString &p_format, const QString &p_text)
|
||||||
|
{
|
||||||
|
VGraphvizHelper inst;
|
||||||
|
|
||||||
|
int exitCode = -1;
|
||||||
|
QByteArray out, err;
|
||||||
|
|
||||||
|
QStringList args(inst.m_args);
|
||||||
|
args << ("-T" + p_format);
|
||||||
|
int ret = VProcessUtils::startProcess(inst.m_program,
|
||||||
|
args,
|
||||||
|
p_text.toUtf8(),
|
||||||
|
exitCode,
|
||||||
|
out,
|
||||||
|
err);
|
||||||
|
|
||||||
|
if (ret != 0 || exitCode < 0) {
|
||||||
|
qWarning() << "Graphviz fail" << ret << exitCode << QString::fromLocal8Bit(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
@ -16,10 +16,10 @@ public:
|
|||||||
|
|
||||||
void processAsync(int p_id, TimeStamp p_timeStamp, const QString &p_format, const QString &p_text);
|
void processAsync(int p_id, TimeStamp p_timeStamp, const QString &p_format, const QString &p_text);
|
||||||
|
|
||||||
void prepareCommand(QString &p_cmd, QStringList &p_args) const;
|
|
||||||
|
|
||||||
static bool testGraphviz(const QString &p_dot, QString &p_msg);
|
static bool testGraphviz(const QString &p_dot, QString &p_msg);
|
||||||
|
|
||||||
|
static QByteArray process(const QString &p_format, const QString &p_text);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void resultReady(int p_id, TimeStamp p_timeStamp, const QString &p_format, const QString &p_result);
|
void resultReady(int p_id, TimeStamp p_timeStamp, const QString &p_format, const QString &p_result);
|
||||||
|
|
||||||
@ -27,6 +27,8 @@ private slots:
|
|||||||
void handleProcessFinished(int p_exitCode, QProcess::ExitStatus p_exitStatus);
|
void handleProcessFinished(int p_exitCode, QProcess::ExitStatus p_exitStatus);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void prepareCommand(QString &p_cmd, QStringList &p_args) const;
|
||||||
|
|
||||||
QString m_program;
|
QString m_program;
|
||||||
QStringList m_args;
|
QStringList m_args;
|
||||||
};
|
};
|
||||||
|
@ -8,10 +8,10 @@
|
|||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vgraphvizhelper.h"
|
#include "vgraphvizhelper.h"
|
||||||
#include "vplantumlhelper.h"
|
#include "vplantumlhelper.h"
|
||||||
#include "vcodeblockhighlighthelper.h"
|
|
||||||
#include "vmainwindow.h"
|
#include "vmainwindow.h"
|
||||||
#include "veditarea.h"
|
#include "veditarea.h"
|
||||||
#include "vmathjaxpreviewhelper.h"
|
#include "vmathjaxpreviewhelper.h"
|
||||||
|
#include "utils/veditutils.h"
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
@ -249,14 +249,6 @@ void VLivePreviewHelper::handleCursorPositionChanged()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString removeFence(const QString &p_text)
|
|
||||||
{
|
|
||||||
QString text = VCodeBlockHighlightHelper::unindentCodeBlock(p_text);
|
|
||||||
Q_ASSERT(text.startsWith("```") && text.endsWith("```"));
|
|
||||||
int idx = text.indexOf('\n') + 1;
|
|
||||||
return text.mid(idx, text.size() - idx - 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VLivePreviewHelper::updateLivePreview()
|
void VLivePreviewHelper::updateLivePreview()
|
||||||
{
|
{
|
||||||
if (m_cbIndex < 0) {
|
if (m_cbIndex < 0) {
|
||||||
@ -277,7 +269,7 @@ void VLivePreviewHelper::updateLivePreview()
|
|||||||
m_graphvizHelper->processAsync(m_cbIndex | LANG_PREFIX_GRAPHVIZ | TYPE_LIVE_PREVIEW,
|
m_graphvizHelper->processAsync(m_cbIndex | LANG_PREFIX_GRAPHVIZ | TYPE_LIVE_PREVIEW,
|
||||||
m_timeStamp,
|
m_timeStamp,
|
||||||
"svg",
|
"svg",
|
||||||
removeFence(vcb.m_text));
|
VEditUtils::removeCodeBlockFence(vcb.m_text));
|
||||||
} else {
|
} else {
|
||||||
m_document->setPreviewContent(vcb.m_lang, cb.imageData());
|
m_document->setPreviewContent(vcb.m_lang, cb.imageData());
|
||||||
}
|
}
|
||||||
@ -292,7 +284,7 @@ void VLivePreviewHelper::updateLivePreview()
|
|||||||
m_plantUMLHelper->processAsync(m_cbIndex | LANG_PREFIX_PLANTUML | TYPE_LIVE_PREVIEW,
|
m_plantUMLHelper->processAsync(m_cbIndex | LANG_PREFIX_PLANTUML | TYPE_LIVE_PREVIEW,
|
||||||
m_timeStamp,
|
m_timeStamp,
|
||||||
"svg",
|
"svg",
|
||||||
removeFence(vcb.m_text));
|
VEditUtils::removeCodeBlockFence(vcb.m_text));
|
||||||
} else {
|
} else {
|
||||||
m_document->setPreviewContent(vcb.m_lang, cb.imageData());
|
m_document->setPreviewContent(vcb.m_lang, cb.imageData());
|
||||||
}
|
}
|
||||||
@ -300,7 +292,7 @@ void VLivePreviewHelper::updateLivePreview()
|
|||||||
// No need to live preview MathJax.
|
// No need to live preview MathJax.
|
||||||
m_document->previewCodeBlock(m_cbIndex,
|
m_document->previewCodeBlock(m_cbIndex,
|
||||||
vcb.m_lang,
|
vcb.m_lang,
|
||||||
removeFence(vcb.m_text),
|
VEditUtils::removeCodeBlockFence(vcb.m_text),
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,7 +410,7 @@ void VLivePreviewHelper::processForInplacePreview(int p_idx)
|
|||||||
m_graphvizHelper->processAsync(p_idx | LANG_PREFIX_GRAPHVIZ | TYPE_INPLACE_PREVIEW,
|
m_graphvizHelper->processAsync(p_idx | LANG_PREFIX_GRAPHVIZ | TYPE_INPLACE_PREVIEW,
|
||||||
m_timeStamp,
|
m_timeStamp,
|
||||||
"svg",
|
"svg",
|
||||||
removeFence(vcb.m_text));
|
VEditUtils::removeCodeBlockFence(vcb.m_text));
|
||||||
} else if (vcb.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) {
|
} else if (vcb.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) {
|
||||||
if (!m_plantUMLHelper) {
|
if (!m_plantUMLHelper) {
|
||||||
m_plantUMLHelper = new VPlantUMLHelper(this);
|
m_plantUMLHelper = new VPlantUMLHelper(this);
|
||||||
@ -429,19 +421,19 @@ void VLivePreviewHelper::processForInplacePreview(int p_idx)
|
|||||||
m_plantUMLHelper->processAsync(p_idx | LANG_PREFIX_PLANTUML | TYPE_INPLACE_PREVIEW,
|
m_plantUMLHelper->processAsync(p_idx | LANG_PREFIX_PLANTUML | TYPE_INPLACE_PREVIEW,
|
||||||
m_timeStamp,
|
m_timeStamp,
|
||||||
"svg",
|
"svg",
|
||||||
removeFence(vcb.m_text));
|
VEditUtils::removeCodeBlockFence(vcb.m_text));
|
||||||
} else if (vcb.m_lang == "flow"
|
} else if (vcb.m_lang == "flow"
|
||||||
|| vcb.m_lang == "flowchart") {
|
|| vcb.m_lang == "flowchart") {
|
||||||
m_mathJaxHelper->previewDiagram(m_mathJaxID,
|
m_mathJaxHelper->previewDiagram(m_mathJaxID,
|
||||||
p_idx,
|
p_idx,
|
||||||
m_timeStamp,
|
m_timeStamp,
|
||||||
vcb.m_lang,
|
vcb.m_lang,
|
||||||
removeFence(vcb.m_text));
|
VEditUtils::removeCodeBlockFence(vcb.m_text));
|
||||||
} else if (vcb.m_lang == "mathjax") {
|
} else if (vcb.m_lang == "mathjax") {
|
||||||
m_mathJaxHelper->previewMathJax(m_mathJaxID,
|
m_mathJaxHelper->previewMathJax(m_mathJaxID,
|
||||||
p_idx,
|
p_idx,
|
||||||
m_timeStamp,
|
m_timeStamp,
|
||||||
removeFence(vcb.m_text));
|
VEditUtils::removeCodeBlockFence(vcb.m_text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#include "utils/vwebutils.h"
|
#include "utils/vwebutils.h"
|
||||||
#include "dialog/vinsertlinkdialog.h"
|
#include "dialog/vinsertlinkdialog.h"
|
||||||
#include "utils/vclipboardutils.h"
|
#include "utils/vclipboardutils.h"
|
||||||
|
#include "vplantumlhelper.h"
|
||||||
|
#include "vgraphvizhelper.h"
|
||||||
|
|
||||||
extern VWebUtils *g_webUtils;
|
extern VWebUtils *g_webUtils;
|
||||||
|
|
||||||
@ -1604,7 +1606,16 @@ void VMdEditor::initLinkAndPreviewMenu(QAction *p_before, QMenu *p_menu, const Q
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool needSeparator = false;
|
||||||
if (initInPlacePreviewMenu(p_before, p_menu, block, pos)) {
|
if (initInPlacePreviewMenu(p_before, p_menu, block, pos)) {
|
||||||
|
needSeparator = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initExportAndCopyMenu(p_before, p_menu, block, pos)) {
|
||||||
|
needSeparator = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needSeparator) {
|
||||||
p_menu->insertSeparator(p_before ? p_before : NULL);
|
p_menu->insertSeparator(p_before ? p_before : NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1665,3 +1676,117 @@ bool VMdEditor::initInPlacePreviewMenu(QAction *p_before,
|
|||||||
p_menu->insertAction(p_before, copyImageAct);
|
p_menu->insertAction(p_before, copyImageAct);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VMdEditor::initExportAndCopyMenu(QAction *p_before,
|
||||||
|
QMenu *p_menu,
|
||||||
|
const QTextBlock &p_block,
|
||||||
|
int p_pos)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_pos);
|
||||||
|
int state = p_block.userState();
|
||||||
|
if (state != HighlightBlockState::CodeBlockStart
|
||||||
|
&& state != HighlightBlockState::CodeBlock
|
||||||
|
&& state != HighlightBlockState::CodeBlockEnd) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockNum = p_block.blockNumber();
|
||||||
|
const QVector<VCodeBlock> &cbs = m_pegHighlighter->getCodeBlocks();
|
||||||
|
int idx = 0;
|
||||||
|
for (idx = 0; idx < cbs.size(); ++idx) {
|
||||||
|
if (cbs[idx].m_startBlock <= blockNum
|
||||||
|
&& cbs[idx].m_endBlock >= blockNum) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx >= cbs.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VCodeBlock &cb = cbs[idx];
|
||||||
|
if (cb.m_lang != "puml" && cb.m_lang != "dot") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu *subMenu = new QMenu(tr("Copy Diagram"), p_menu);
|
||||||
|
subMenu->setToolTipsVisible(true);
|
||||||
|
|
||||||
|
QAction *pngAct = new QAction(tr("PNG"), subMenu);
|
||||||
|
pngAct->setToolTip(tr("Export diagram as PNG to a temporary file and copy"));
|
||||||
|
connect(pngAct, &QAction::triggered,
|
||||||
|
this, [this, lang = cb.m_lang, text = cb.m_text]() {
|
||||||
|
exportDiagramAndCopy(lang, text, "png");
|
||||||
|
});
|
||||||
|
subMenu->addAction(pngAct);
|
||||||
|
|
||||||
|
QAction *svgAct = new QAction(tr("SVG"), subMenu);
|
||||||
|
svgAct->setToolTip(tr("Export diagram as SVG to a temporary file and copy"));
|
||||||
|
connect(svgAct, &QAction::triggered,
|
||||||
|
this, [this, lang = cb.m_lang, text = cb.m_text]() {
|
||||||
|
exportDiagramAndCopy(lang, text, "svg");
|
||||||
|
});
|
||||||
|
subMenu->addAction(svgAct);
|
||||||
|
|
||||||
|
p_menu->insertMenu(p_before, subMenu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::exportDiagramAndCopy(const QString &p_lang,
|
||||||
|
const QString &p_text,
|
||||||
|
const QString &p_format)
|
||||||
|
{
|
||||||
|
m_exportTempFile.reset(new QTemporaryFile(QDir::tempPath()
|
||||||
|
+ QDir::separator()
|
||||||
|
+ "XXXXXX." + p_format));
|
||||||
|
if (!m_exportTempFile->open()) {
|
||||||
|
VUtils::showMessage(QMessageBox::Warning,
|
||||||
|
tr("Warning"),
|
||||||
|
tr("Fail to open a temporary file for export."),
|
||||||
|
"",
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok,
|
||||||
|
this);
|
||||||
|
m_exportTempFile.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit m_object->statusMessage(tr("Exporting diagram"));
|
||||||
|
|
||||||
|
QString filePath(m_exportTempFile->fileName());
|
||||||
|
QByteArray out;
|
||||||
|
if (p_lang == "puml") {
|
||||||
|
out = VPlantUMLHelper::process(p_format,
|
||||||
|
VEditUtils::removeCodeBlockFence(p_text));
|
||||||
|
} else if (p_lang == "dot") {
|
||||||
|
out = VGraphvizHelper::process(p_format,
|
||||||
|
VEditUtils::removeCodeBlockFence(p_text));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out.isEmpty() || m_exportTempFile->write(out) == -1) {
|
||||||
|
VUtils::showMessage(QMessageBox::Warning,
|
||||||
|
tr("Warning"),
|
||||||
|
tr("Fail to export diagram."),
|
||||||
|
"",
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok,
|
||||||
|
this);
|
||||||
|
} else {
|
||||||
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
clipboard->clear();
|
||||||
|
QImage img;
|
||||||
|
img.loadFromData(out, p_format.toLocal8Bit().data());
|
||||||
|
if (!img.isNull()) {
|
||||||
|
VClipboardUtils::setImageAndLinkToClipboard(clipboard,
|
||||||
|
img,
|
||||||
|
filePath,
|
||||||
|
QClipboard::Clipboard);
|
||||||
|
emit m_object->statusMessage(tr("Diagram exported and copied"));
|
||||||
|
} else {
|
||||||
|
emit m_object->statusMessage(tr("Fail to read exported image: %1").arg(filePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_exportTempFile->close();
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include "vtextedit.h"
|
#include "vtextedit.h"
|
||||||
#include "veditor.h"
|
#include "veditor.h"
|
||||||
@ -284,12 +286,21 @@ private:
|
|||||||
const QTextBlock &p_block,
|
const QTextBlock &p_block,
|
||||||
int p_pos);
|
int p_pos);
|
||||||
|
|
||||||
|
bool initExportAndCopyMenu(QAction *p_before,
|
||||||
|
QMenu *p_menu,
|
||||||
|
const QTextBlock &p_block,
|
||||||
|
int p_pos);
|
||||||
|
|
||||||
void insertImageLink(const QString &p_text, const QString &p_url);
|
void insertImageLink(const QString &p_text, const QString &p_url);
|
||||||
|
|
||||||
void setFontPointSizeByStyleSheet(int p_ptSize);
|
void setFontPointSizeByStyleSheet(int p_ptSize);
|
||||||
|
|
||||||
void setFontAndPaletteByStyleSheet(const QFont &p_font, const QPalette &p_palette);
|
void setFontAndPaletteByStyleSheet(const QFont &p_font, const QPalette &p_palette);
|
||||||
|
|
||||||
|
void exportDiagramAndCopy(const QString &p_lang,
|
||||||
|
const QString &p_text,
|
||||||
|
const QString &p_format);
|
||||||
|
|
||||||
PegMarkdownHighlighter *m_pegHighlighter;
|
PegMarkdownHighlighter *m_pegHighlighter;
|
||||||
|
|
||||||
VCodeBlockHighlightHelper *m_cbHighlighter;
|
VCodeBlockHighlightHelper *m_cbHighlighter;
|
||||||
@ -314,6 +325,9 @@ private:
|
|||||||
VEditTab *m_editTab;
|
VEditTab *m_editTab;
|
||||||
|
|
||||||
int m_copyTimeStamp;
|
int m_copyTimeStamp;
|
||||||
|
|
||||||
|
// Temp file used for ExportAndCopy.
|
||||||
|
QSharedPointer<QTemporaryFile> m_exportTempFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline PegMarkdownHighlighter *VMdEditor::getMarkdownHighlighter() const
|
inline PegMarkdownHighlighter *VMdEditor::getMarkdownHighlighter() const
|
||||||
|
@ -15,7 +15,19 @@ extern VConfigManager *g_config;
|
|||||||
VPlantUMLHelper::VPlantUMLHelper(QObject *p_parent)
|
VPlantUMLHelper::VPlantUMLHelper(QObject *p_parent)
|
||||||
: QObject(p_parent)
|
: QObject(p_parent)
|
||||||
{
|
{
|
||||||
prepareCommand(m_customCmd, m_program, m_args);
|
m_customCmd = g_config->getPlantUMLCmd();
|
||||||
|
if (m_customCmd.isEmpty()) {
|
||||||
|
prepareCommand(m_program, m_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VPlantUMLHelper::VPlantUMLHelper(const QString &p_jar, QObject *p_parent)
|
||||||
|
: QObject(p_parent)
|
||||||
|
{
|
||||||
|
m_customCmd = g_config->getPlantUMLCmd();
|
||||||
|
if (m_customCmd.isEmpty()) {
|
||||||
|
prepareCommand(m_program, m_args, p_jar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPlantUMLHelper::processAsync(int p_id,
|
void VPlantUMLHelper::processAsync(int p_id,
|
||||||
@ -49,18 +61,13 @@ void VPlantUMLHelper::processAsync(int p_id,
|
|||||||
process->closeWriteChannel();
|
process->closeWriteChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPlantUMLHelper::prepareCommand(QString &p_customCmd,
|
void VPlantUMLHelper::prepareCommand(QString &p_program,
|
||||||
QString &p_program,
|
QStringList &p_args,
|
||||||
QStringList &p_args) const
|
const QString &p_jar) const
|
||||||
{
|
{
|
||||||
p_customCmd = g_config->getPlantUMLCmd();
|
|
||||||
if (!p_customCmd.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
p_program = "java";
|
p_program = "java";
|
||||||
|
|
||||||
p_args << "-jar" << g_config->getPlantUMLJar();
|
p_args << "-jar" << (p_jar.isEmpty() ? g_config->getPlantUMLJar() : p_jar);
|
||||||
p_args << "-charset" << "UTF-8";
|
p_args << "-charset" << "UTF-8";
|
||||||
|
|
||||||
int nbthread = QThread::idealThreadCount();
|
int nbthread = QThread::idealThreadCount();
|
||||||
@ -124,28 +131,23 @@ void VPlantUMLHelper::handleProcessFinished(int p_exitCode, QProcess::ExitStatus
|
|||||||
|
|
||||||
bool VPlantUMLHelper::testPlantUMLJar(const QString &p_jar, QString &p_msg)
|
bool VPlantUMLHelper::testPlantUMLJar(const QString &p_jar, QString &p_msg)
|
||||||
{
|
{
|
||||||
QString program("java");
|
VPlantUMLHelper inst(p_jar);
|
||||||
QStringList args;
|
QStringList args(inst.m_args);
|
||||||
args << "-jar" << p_jar;
|
|
||||||
args << "-charset" << "UTF-8";
|
|
||||||
|
|
||||||
const QString &dot = g_config->getGraphvizDot();
|
|
||||||
if (!dot.isEmpty()) {
|
|
||||||
args << "-graphvizdot";
|
|
||||||
args << dot;
|
|
||||||
}
|
|
||||||
|
|
||||||
args << "-pipe";
|
|
||||||
args << "-tsvg";
|
args << "-tsvg";
|
||||||
|
|
||||||
QString testGraph("VNote->Markdown : hello");
|
QString testGraph("VNote->Markdown : hello");
|
||||||
|
|
||||||
int exitCode = -1;
|
int exitCode = -1;
|
||||||
QByteArray out, err;
|
QByteArray out, err;
|
||||||
int ret = VProcessUtils::startProcess(program, args, testGraph.toUtf8(), exitCode, out, err);
|
int ret = VProcessUtils::startProcess(inst.m_program,
|
||||||
|
args,
|
||||||
|
testGraph.toUtf8(),
|
||||||
|
exitCode,
|
||||||
|
out,
|
||||||
|
err);
|
||||||
|
|
||||||
p_msg = QString("Command: %1 %2\nExitCode: %3\nOutput: %4\nError: %5")
|
p_msg = QString("Command: %1 %2\nExitCode: %3\nOutput: %4\nError: %5")
|
||||||
.arg(program)
|
.arg(inst.m_program)
|
||||||
.arg(args.join(' '))
|
.arg(args.join(' '))
|
||||||
.arg(exitCode)
|
.arg(exitCode)
|
||||||
.arg(QString::fromLocal8Bit(out))
|
.arg(QString::fromLocal8Bit(out))
|
||||||
@ -153,3 +155,36 @@ bool VPlantUMLHelper::testPlantUMLJar(const QString &p_jar, QString &p_msg)
|
|||||||
|
|
||||||
return ret == 0 && exitCode == 0;
|
return ret == 0 && exitCode == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray VPlantUMLHelper::process(const QString &p_format, const QString &p_text)
|
||||||
|
{
|
||||||
|
VPlantUMLHelper inst;
|
||||||
|
|
||||||
|
int exitCode = -1;
|
||||||
|
QByteArray out, err;
|
||||||
|
int ret = -1;
|
||||||
|
if (inst.m_customCmd.isEmpty()) {
|
||||||
|
QStringList args(inst.m_args);
|
||||||
|
args << ("-t" + p_format);
|
||||||
|
ret = VProcessUtils::startProcess(inst.m_program,
|
||||||
|
args,
|
||||||
|
p_text.toUtf8(),
|
||||||
|
exitCode,
|
||||||
|
out,
|
||||||
|
err);
|
||||||
|
} else {
|
||||||
|
QString cmd(inst.m_customCmd);
|
||||||
|
cmd.replace("%0", p_format);
|
||||||
|
ret = VProcessUtils::startProcess(cmd,
|
||||||
|
p_text.toUtf8(),
|
||||||
|
exitCode,
|
||||||
|
out,
|
||||||
|
err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0 || exitCode < 0) {
|
||||||
|
qWarning() << "PlantUML fail" << ret << exitCode << QString::fromLocal8Bit(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
@ -19,17 +19,24 @@ public:
|
|||||||
const QString &p_format,
|
const QString &p_format,
|
||||||
const QString &p_text);
|
const QString &p_text);
|
||||||
|
|
||||||
void prepareCommand(QString &p_customCmd, QString &p_cmd, QStringList &p_args) const;
|
|
||||||
|
|
||||||
static bool testPlantUMLJar(const QString &p_jar, QString &p_msg);
|
static bool testPlantUMLJar(const QString &p_jar, QString &p_msg);
|
||||||
|
|
||||||
|
static QByteArray process(const QString &p_format, const QString &p_text);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void resultReady(int p_id, TimeStamp p_timeStamp, const QString &p_format, const QString &p_result);
|
void resultReady(int p_id,
|
||||||
|
TimeStamp p_timeStamp,
|
||||||
|
const QString &p_format,
|
||||||
|
const QString &p_result);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleProcessFinished(int p_exitCode, QProcess::ExitStatus p_exitStatus);
|
void handleProcessFinished(int p_exitCode, QProcess::ExitStatus p_exitStatus);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
VPlantUMLHelper(const QString &p_jar, QObject *p_parent = nullptr);
|
||||||
|
|
||||||
|
void prepareCommand(QString &p_cmd, QStringList &p_args, const QString &p_jar= QString()) const;
|
||||||
|
|
||||||
QString m_program;
|
QString m_program;
|
||||||
|
|
||||||
QStringList m_args;
|
QStringList m_args;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user