support PlantUML

This commit is contained in:
Le Tan 2018-04-04 20:30:36 +08:00
parent a6087d98a6
commit 9850d9a2a2
28 changed files with 560 additions and 25 deletions

View File

@ -899,10 +899,32 @@ VMarkdownTab::VMarkdownTab(QWidget *p_parent)
QLabel *colorColumnLabel = new QLabel(tr("Color column:"));
colorColumnLabel->setToolTip(m_colorColumnEdit->toolTip());
// PlantUML.
m_plantUMLModeCombo = VUtils::getComboBox();
m_plantUMLModeCombo->setToolTip(tr("Enable PlantUML support in Markdown"));
m_plantUMLModeCombo->addItem(tr("Disabled"), PlantUMLMode::DisablePlantUML);
m_plantUMLModeCombo->addItem(tr("Online Service"), PlantUMLMode::OnlinePlantUML);
m_plantUMLModeCombo->addItem(tr("Local JAR"), PlantUMLMode::LocalPlantUML);
m_plantUMLServerEdit = new VLineEdit();
m_plantUMLServerEdit->setToolTip(tr("Server address for online PlantUML"));
m_plantUMLJarEdit = new VLineEdit();
m_plantUMLJarEdit->setToolTip(tr("Location to the PlantUML JAR executable for local PlantUML"));
m_plantUMLDotEdit = new VLineEdit();
m_plantUMLDotEdit->setPlaceholderText(tr("Empty to detect automatically"));
m_plantUMLDotEdit->setToolTip(tr("Location to the GraphViz executable for local PlantUML "
"(empty to let PlantUML detect it automatically)"));
QFormLayout *mainLayout = new QFormLayout();
mainLayout->addRow(tr("Note open mode:"), m_openModeCombo);
mainLayout->addRow(tr("Heading sequence:"), headingSequenceLayout);
mainLayout->addRow(colorColumnLabel, m_colorColumnEdit);
mainLayout->addRow(tr("PlantUML:"), m_plantUMLModeCombo);
mainLayout->addRow(tr("PlantUML server:"), m_plantUMLServerEdit);
mainLayout->addRow(tr("PlantUML JAR:"), m_plantUMLJarEdit);
mainLayout->addRow(tr("Graphviz executable:"), m_plantUMLDotEdit);
setLayout(mainLayout);
}
@ -921,6 +943,10 @@ bool VMarkdownTab::loadConfiguration()
return false;
}
if (!loadPlantUML()) {
return false;
}
return true;
}
@ -938,6 +964,10 @@ bool VMarkdownTab::saveConfiguration()
return false;
}
if (!savePlantUML()) {
return false;
}
return true;
}
@ -1009,3 +1039,20 @@ bool VMarkdownTab::saveColorColumn()
return true;
}
bool VMarkdownTab::loadPlantUML()
{
m_plantUMLModeCombo->setCurrentIndex(m_plantUMLModeCombo->findData(g_config->getPlantUMLMode()));
m_plantUMLServerEdit->setText(g_config->getPlantUMLServer());
m_plantUMLJarEdit->setText(g_config->getPlantUMLJar());
m_plantUMLDotEdit->setText(g_config->getPlantUMLDot());
return true;
}
bool VMarkdownTab::savePlantUML()
{
g_config->setPlantUMLMode(m_plantUMLModeCombo->currentData().toInt());
g_config->setPlantUMLServer(m_plantUMLServerEdit->text());
g_config->setPlantUMLJar(m_plantUMLJarEdit->text());
g_config->setPlantUMLDot(m_plantUMLDotEdit->text());
return true;
}

View File

@ -157,6 +157,9 @@ private:
bool loadColorColumn();
bool saveColorColumn();
bool loadPlantUML();
bool savePlantUML();
// Default note open mode for markdown.
QComboBox *m_openModeCombo;
@ -166,6 +169,12 @@ private:
// Color column in code block.
VLineEdit *m_colorColumnEdit;
// PlantUML.
QComboBox *m_plantUMLModeCombo;
VLineEdit *m_plantUMLServerEdit;
VLineEdit *m_plantUMLJarEdit;
VLineEdit *m_plantUMLDotEdit;
};
class VSettingsDialog : public QDialog

View File

@ -108,7 +108,10 @@ As VNote suggests:
- `lang` is optional to specify the language of the code;
### Diagrams
VNote supports [Flowchart.js](http://flowchart.js.org/) and [Mermaid](https://mermaidjs.github.io/) to draw diagrams such as *flowchart* and *sequence diagram*. You should use `flowchart` and `mermaid` specified as the language of the fenced code block and write the definition of your diagram within it.
> You need to enable Flowchart.js or Mermaid in the `Markdown` menu.
VNote supports [Flowchart.js](http://flowchart.js.org/) and [Mermaid](https://mermaidjs.github.io/) to draw diagrams such as *flowchart* and *sequence diagram*. You should use `flow` or `flowchart` and `mermaid` specified as the language of the fenced code block and write the definition of your diagram within it.
```flowchart
st=>start: Start:>http://www.google.com[blank]
@ -124,7 +127,22 @@ VNote supports [Flowchart.js](http://flowchart.js.org/) and [Mermaid](https://me
cond(no)->sub1(right)->op1
```
#### UML
> You need to enable PlantUML in the settings. Pay attention to the privacy issue if you use online PlantUML server. You may need to prepare Java runtime, PlantUML, and Graphviz if you choose local PlantUML.
VNote supports [PlantUML](http://plantuml.com/) to draw UML diagrams. You should use `puml` specified as the language of the fenced code block and write the definition of your diagram within it.
```puml
@startuml
Bob -> Alice : hello
@enduml
```
### Math Formulas
> You need to enable MathJax in the `Markdown` menu.
VNote supports math formulas via [MathJax](https://www.mathjax.org/). The default math delimiters are `$$...$$` and `\[...\]` for **displayed mathematics**, and `$...$` for **inline mathematics**. Sometimes you may need to *escape* some characters via `\`.
VNote also supports displayed mathematics via fenced code block with language `mathjax` specified. The benifit of using code block is you do not have to escape most characters.

View File

@ -109,7 +109,10 @@ As VNote suggests:
- `lang`用于指定代码块的代码语言,可选;
### 图表
VNote支持 [Flowchart.js](http://flowchart.js.org/) 和 [Mermaid](https://mermaidjs.github.io/) 来实现诸如*流程图*和*序列图*等图表。您需要使用代码块,并标明语言为`flowchart``mermaid`,然后在代码块里面定义图表。
> 需要在`Markdown`菜单中启用Flowchart.js或Mermaid。
VNote支持 [Flowchart.js](http://flowchart.js.org/) 和 [Mermaid](https://mermaidjs.github.io/) 来实现诸如*流程图*和*序列图*等图表。您需要使用代码块,并标明语言为`flow``flowchart``mermaid`,然后在代码块里面定义图表。
```flowchart
st=>start: Start:>http://www.google.com[blank]
@ -125,7 +128,22 @@ VNote支持 [Flowchart.js](http://flowchart.js.org/) 和 [Mermaid](https://merma
cond(no)->sub1(right)->op1
```
#### UML
> 需要在设置中启用PlantUML。如果使用在线的PlantUML服务器请注意隐私问题如果使用本地PlantUML可能需要安装Java运行时、PlantUML以及Graphviz。
VNote支持 [PlantUML](http://plantuml.com/) 来实现UML图表。您需要使用代码块并标明语言为`puml`,然后在代码块里面定义图表。
```puml
@startuml
Bob -> Alice : hello
@enduml
```
### 数学公式
> 需要在`Markdown`菜单中启用MathJax。
VNote通过 [MathJax](https://www.mathjax.org/) 来支持数学公式。默认的**块公式**的分隔符是`$$...$$$``\[...\]`**行内公式**的分隔符是`$...$`。有时候,您需要使用`\`来*转义*某些字符。
VNote也可以使用标明语言`mathjax`的代码块来实现块公式。使用代码块的一个好处是大多数情况下无需转义字符。

View File

@ -16,12 +16,15 @@ marked.setOptions({
});
var updateHtml = function(html) {
asyncJobsCount = 0;
placeholder.innerHTML = html;
insertImageCaption();
var codes = document.getElementsByTagName('code');
mermaidIdx = 0;
plantUMLIdx = 0;
for (var i = 0; i < codes.length; ++i) {
var code = codes[i];
if (code.parentElement.tagName.toLowerCase() == 'pre') {
@ -43,6 +46,19 @@ var updateHtml = function(html) {
}
} else if (VEnableMathjax && code.classList.contains('language-mathjax')) {
// Mathjax code block.
continue;
} else if (VPlantUMLMode != 0
&& code.classList.contains('language-puml')) {
// PlantUML code block.
if (VPlantUMLMode == 1) {
if (renderPlantUMLOneOnline(code)) {
// replaceChild() will decrease codes.length.
--i;
}
} else {
renderPlantUMLOneLocal(code);
}
continue;
}

View File

@ -106,6 +106,8 @@ var updateText = function(text) {
text = "[TOC]\n\n" + text;
}
asyncJobsCount = 0;
var needToc = mdHasTocSection(text);
var html = markdownToHtml(text, needToc);
placeholder.innerHTML = html;
@ -113,6 +115,7 @@ var updateText = function(text) {
insertImageCaption();
renderMermaid('lang-mermaid');
renderFlowchart(['lang-flowchart', 'lang-flow']);
renderPlantUML('lang-puml');
addClassToCodeBlock();
renderCodeBlockLineNumber();

View File

@ -8,6 +8,8 @@ var pendingKeys = [];
var VMermaidDivClass = 'mermaid-diagram';
var VFlowchartDivClass = 'flowchart-diagram';
var VPlantUMLDivClass = 'plantuml-diagram';
if (typeof VEnableMermaid == 'undefined') {
VEnableMermaid = false;
} else if (VEnableMermaid) {
@ -32,6 +34,17 @@ if (typeof VStylesToInline == 'undefined') {
VStylesToInline = '';
}
// 0 - disable PlantUML;
// 1 - Use online PlantUML processor;
// 2 - Use local PlantUML processor;
if (typeof VPlantUMLMode == 'undefined') {
VPlantUMLMode = 0;
}
if (typeof VPlantUMLServer == 'undefined') {
VPlantUMLServer = 'http://www.plantuml.com/plantuml';
}
// Add a caption (using alt text) under the image.
var VImageCenterClass = 'img-center';
var VImageCaptionClass = 'img-caption';
@ -133,6 +146,8 @@ new QWebChannel(qt.webChannelTransport,
if (typeof htmlContent == "function") {
content.requestHtmlContent.connect(htmlContent);
}
content.plantUMLResultReady.connect(handlePlantUMLResult);
});
var VHighlightedAnchorClass = 'highlighted-anchor';
@ -605,6 +620,72 @@ var renderFlowchartOne = function(code) {
return true;
};
var plantUMLIdx = 0;
var plantUMLCodeClass = 'plantuml_code_';
// @className, the class name of the PlantUML code block, such as 'lang-puml'.
var renderPlantUML = function(className) {
if (VPlantUMLMode == 0) {
return;
}
plantUMLIdx = 0;
var codes = document.getElementsByTagName('code');
for (var i = 0; i < codes.length; ++i) {
var code = codes[i];
if (code.classList.contains(className)) {
if (VPlantUMLMode == 1) {
if (renderPlantUMLOneOnline(code)) {
// replaceChild() will decrease codes.length.
--i;
}
} else {
renderPlantUMLOneLocal(code);
}
}
}
};
// Render @code as PlantUML graph.
// Returns true if succeeded.
var renderPlantUMLOneOnline = function(code) {
var s = unescape(encodeURIComponent(code.textContent));
var arr = [];
for (var i = 0; i < s.length; i++) {
arr.push(s.charCodeAt(i));
}
var compressor = new Zopfli.RawDeflate(arr);
var compressed = compressor.compress();
var url = VPlantUMLServer + "/" + VPlantUMLFormat + "/" + encode64_(compressed);
var obj = null;
if (VPlantUMLFormat == 'svg') {
var svgObj = document.createElement('object');
svgObj.type = 'image/svg+xml';
svgObj.data = url;
obj = document.createElement('div');
obj.classList.add(VPlantUMLDivClass);
obj.appendChild(svgObj);
} else {
obj = document.createElement('img');
obj.src = url;
}
var preNode = code.parentNode;
preNode.parentNode.replaceChild(obj, preNode);
return true;
};
var renderPlantUMLOneLocal = function(code) {
++asyncJobsCount;
code.classList.add(plantUMLCodeClass + plantUMLIdx);
content.processPlantUML(plantUMLIdx, VPlantUMLFormat, code.textContent);
plantUMLIdx++;
};
var isImageBlock = function(img) {
var pn = img.parentNode;
return (pn.children.length == 1) && (pn.textContent == '');
@ -704,12 +785,20 @@ var insertImageCaption = function() {
}
}
var asyncJobsCount = 0;
var finishOneAsyncJob = function() {
--asyncJobsCount;
finishLogics();
};
// The renderer specific code should call this function once thay have finished
// markdown-specifi handle logics, such as Mermaid, MathJax.
var finishLogics = function() {
content.finishLogics();
calculateWordCount();
if (asyncJobsCount <= 0) {
content.finishLogics();
calculateWordCount();
}
};
// Escape @text to Html.
@ -1170,5 +1259,26 @@ var calculateWordCount = function() {
var specialCodeBlock = function(lang) {
return (VEnableMathjax && lang == 'mathjax')
|| (VEnableMermaid && lang == 'mermaid')
|| (VEnableFlowchart && (lang == 'flowchart' || lang == 'flow'));
|| (VEnableFlowchart && (lang == 'flowchart' || lang == 'flow'))
|| (VPlantUMLMode != 0 && lang == 'puml');
};
var handlePlantUMLResult = function(id, format, result) {
var code = document.getElementsByClassName(plantUMLCodeClass + id)[0];
if (code && result.length > 0) {
var obj = null;
if (format == 'svg') {
obj = document.createElement('div');
obj.classList.add(VPlantUMLDivClass);
obj.innerHTML = result;
} else {
obj = document.createElement('img');
obj.src = "data:image/" + format + ";base64, " + result;
}
var preNode = code.parentNode;
preNode.parentNode.replaceChild(obj, preNode);
}
finishOneAsyncJob();
};

View File

@ -50,6 +50,8 @@ var updateText = function(text) {
text = "[TOC]\n\n" + text;
}
asyncJobsCount = 0;
var needToc = mdHasTocSection(text);
var html = markdownToHtml(text, needToc);
placeholder.innerHTML = html;
@ -57,6 +59,7 @@ var updateText = function(text) {
insertImageCaption();
renderMermaid('lang-mermaid');
renderFlowchart(['lang-flowchart', 'lang-flow']);
renderPlantUML('lang-puml');
addClassToCodeBlock();
renderCodeBlockLineNumber();

View File

@ -49,7 +49,7 @@ var mdHasTocSection = function(markdown) {
return n != -1;
};
var highlightCodeBlocks = function(doc, enableMermaid, enableFlowchart, enableMathJax) {
var highlightCodeBlocks = function(doc, enableMermaid, enableFlowchart, enableMathJax, enablePlantUML) {
var codes = doc.getElementsByTagName('code');
for (var i = 0; i < codes.length; ++i) {
var code = codes[i];
@ -65,6 +65,9 @@ var highlightCodeBlocks = function(doc, enableMermaid, enableFlowchart, enableMa
} else if (enableMathJax && code.classList.contains('language-mathjax')) {
// MathJax code block.
continue;
} else if (enablePlantUML && code.classList.contains('language-puml')) {
// PlantUML code block.
continue;
}
if (listContainsRegex(code.classList, /language-.*/)) {
@ -79,14 +82,17 @@ var updateText = function(text) {
text = "[TOC]\n\n" + text;
}
asyncJobsCount = 0;
var needToc = mdHasTocSection(text);
var html = markdownToHtml(text, needToc);
placeholder.innerHTML = html;
handleToc(needToc);
insertImageCaption();
highlightCodeBlocks(document, VEnableMermaid, VEnableFlowchart, VEnableMathjax);
highlightCodeBlocks(document, VEnableMermaid, VEnableFlowchart, VEnableMathjax, VPlantUMLMode != 0);
renderMermaid('language-mermaid');
renderFlowchart(['language-flowchart', 'language-flow']);
renderPlantUML('language-puml');
addClassToCodeBlock();
renderCodeBlockLineNumber();

View File

@ -164,13 +164,22 @@ table tr th :last-child, table tr td :last-child {
}
div.mermaid-diagram {
overflow-y: hidden;
width: fit-content;
overflow: hidden;
background: #B0BEC5;
color: #6C6C6C;
}
div.flowchart-diagram {
overflow-y: hidden;
width: fit-content;
overflow: hidden;
background: #B0BEC5;
color: #6C6C6C;
}
div.plantuml-diagram {
width: fit-content;
overflow: hidden;
background: #B0BEC5;
color: #6C6C6C;
}

View File

@ -169,11 +169,18 @@ table tr th :last-child, table tr td :last-child {
}
div.mermaid-diagram {
overflow-y: hidden;
width: fit-content;
overflow: hidden;
}
div.flowchart-diagram {
overflow-y: hidden;
width: fit-content;
overflow: hidden;
}
div.plantuml-diagram {
width: fit-content;
overflow: hidden;
}
.img-package {

View File

@ -170,11 +170,18 @@ table tr th :last-child, table tr td :last-child {
}
div.mermaid-diagram {
overflow-y: hidden;
width: fit-content;
overflow: hidden;
}
div.flowchart-diagram {
overflow-y: hidden;
width: fit-content;
overflow: hidden;
}
div.plantuml-diagram {
width: fit-content;
overflow: hidden;
}
.img-package {

View File

@ -34,13 +34,21 @@ language=System
; 0 - Hoedown, 1 - Marked, 2 - Markdown-it, 3 - Showdown
markdown_converter=2
; Enable Mermaid diagram
enable_mermaid=false
; Enable MathJax
enable_mathjax=true
; Enable flowchart.js
; Enable Flowchart.js
enable_flowchart=false
; Enable PlantUML
; 0 - disable PlantUML
; 1 - online PlantUML
; 2 - local PlantUML
plantuml_mode=0
; -1 - calculate the factor
web_zoom_factor=-1
@ -263,6 +271,15 @@ copy_targets="Without Background"$s:b(mark):c:i:x,Evernote$e:p:b(mark|pre):c(pre
enable_flash_anchor=true
; PlantUML server to convert UML script online
plantuml_server=http://www.plantuml.com/plantuml
; PlantUML JAR location
plantuml_jar=
; PlantUML Graphviz Dot location
plantuml_dot=
[shortcuts]
; Define shortcuts here, with each item in the form "operation=keysequence".
; Leave keysequence empty to disable the shortcut of an operation.

View File

@ -125,7 +125,8 @@ SOURCES += main.cpp\
voutlineue.cpp \
vhelpue.cpp \
vlistfolderue.cpp \
dialog/vfixnotebookdialog.cpp
dialog/vfixnotebookdialog.cpp \
vplantumlhelper.cpp
HEADERS += vmainwindow.h \
vdirectorytree.h \
@ -241,7 +242,8 @@ HEADERS += vmainwindow.h \
voutlineue.h \
vhelpue.h \
vlistfolderue.h \
dialog/vfixnotebookdialog.h
dialog/vfixnotebookdialog.h \
vplantumlhelper.h
RESOURCES += \
vnote.qrc \

View File

@ -618,11 +618,12 @@ QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType,
g_config->getCodeBlockCssStyleUrl(p_renderCodeBlockStyle),
p_isPDF);
return generateHtmlTemplate(templ, p_conType, p_wkhtmltopdf, p_addToc);
return generateHtmlTemplate(templ, p_conType, p_isPDF, p_wkhtmltopdf, p_addToc);
}
QString VUtils::generateHtmlTemplate(const QString &p_template,
MarkdownConverterType p_conType,
bool p_isPDF,
bool p_wkhtmltopdf,
bool p_addToc)
{
@ -721,6 +722,20 @@ QString VUtils::generateHtmlTemplate(const QString &p_template,
}
}
int plantUMLMode = g_config->getPlantUMLMode();
if (plantUMLMode != PlantUMLMode::DisablePlantUML) {
if (plantUMLMode == PlantUMLMode::OnlinePlantUML) {
extraFile += "<script type=\"text/javascript\" src=\"" + VNote::c_plantUMLJsFile + "\"></script>\n" +
"<script type=\"text/javascript\" src=\"" + VNote::c_plantUMLZopfliJsFile + "\"></script>\n" +
"<script>var VPlantUMLServer = '" + g_config->getPlantUMLServer() + "';</script>\n";
}
extraFile += QString("<script>var VPlantUMLMode = %1;</script>\n").arg(plantUMLMode);
QString format = p_isPDF ? "png" : "svg";
extraFile += QString("<script>var VPlantUMLFormat = '%1';</script>\n").arg(format);
}
if (g_config->getEnableImageCaption()) {
extraFile += "<script>var VEnableImageCaption = true;</script>\n";
}

View File

@ -368,6 +368,7 @@ private:
static QString generateHtmlTemplate(const QString &p_template,
MarkdownConverterType p_conType,
bool p_isPDF = false,
bool p_wkhtmltopdf = false,
bool p_addToc = false);

View File

@ -284,6 +284,11 @@ void VConfigManager::initialize()
m_enableFlashAnchor = getConfigFromSettings("web",
"enable_flash_anchor").toBool();
m_plantUMLMode = getConfigFromSettings("global", "plantuml_mode").toInt();
m_plantUMLServer = getConfigFromSettings("web", "plantuml_server").toString();
m_plantUMLJar = getConfigFromSettings("web", "plantuml_jar").toString();
m_plantUMLDot = getConfigFromSettings("web", "plantuml_dot").toString();
}
void VConfigManager::initSettings()

View File

@ -210,6 +210,9 @@ public:
bool getEnableMathjax() const;
void setEnableMathjax(bool p_enabled);
int getPlantUMLMode() const;
void setPlantUMLMode(int p_mode);
qreal getWebZoomFactor() const;
void setWebZoomFactor(qreal p_factor);
bool isCustomWebZoomFactor();
@ -460,6 +463,15 @@ public:
QStringList getSearchOptions() const;
void setSearchOptions(const QStringList &p_opts);
const QString &getPlantUMLServer() const;
void setPlantUMLServer(const QString &p_server);
const QString &getPlantUMLJar() const;
void setPlantUMLJar(const QString &p_jarPath);
const QString &getPlantUMLDot() const;
void setPlantUMLDot(const QString &p_dotPath);
private:
// Look up a config from user and default settings.
QVariant getConfigFromSettings(const QString &section, const QString &key) const;
@ -852,6 +864,15 @@ private:
// Whether flash anchor in read mode.
bool m_enableFlashAnchor;
// PlantUML mode.
int m_plantUMLMode;
QString m_plantUMLServer;
QString m_plantUMLJar;
QString m_plantUMLDot;
// The name of the config file in each directory, obsolete.
// Use c_dirConfigFile instead.
static const QString c_obsoleteDirConfigFile;
@ -1325,10 +1346,26 @@ inline void VConfigManager::setEnableMathjax(bool p_enabled)
if (m_enableMathjax == p_enabled) {
return;
}
m_enableMathjax = p_enabled;
setConfigToSettings("global", "enable_mathjax", m_enableMathjax);
}
inline int VConfigManager::getPlantUMLMode() const
{
return m_plantUMLMode;
}
inline void VConfigManager::setPlantUMLMode(int p_mode)
{
if (m_plantUMLMode == p_mode) {
return;
}
m_plantUMLMode = p_mode;
setConfigToSettings("global", "plantuml_mode", p_mode);
}
inline qreal VConfigManager::getWebZoomFactor() const
{
return m_webZoomFactor;
@ -2149,4 +2186,49 @@ inline void VConfigManager::setSearchOptions(const QStringList &p_opts)
{
setConfigToSettings("global", "search_options", p_opts);
}
inline const QString &VConfigManager::getPlantUMLServer() const
{
return m_plantUMLServer;
}
inline void VConfigManager::setPlantUMLServer(const QString &p_server)
{
if (m_plantUMLServer == p_server) {
return;
}
m_plantUMLServer = p_server;
setConfigToSettings("web", "plantuml_server", p_server);
}
inline const QString &VConfigManager::getPlantUMLJar() const
{
return m_plantUMLJar;
}
inline void VConfigManager::setPlantUMLJar(const QString &p_jarPath)
{
if (m_plantUMLJar == p_jarPath) {
return;
}
m_plantUMLJar = p_jarPath;
setConfigToSettings("web", "plantuml_jar", p_jarPath);
}
inline const QString &VConfigManager::getPlantUMLDot() const
{
return m_plantUMLDot;
}
inline void VConfigManager::setPlantUMLDot(const QString &p_dotPath)
{
if (m_plantUMLDot == p_dotPath) {
return;
}
m_plantUMLDot = p_dotPath;
setConfigToSettings("web", "plantuml_dot", p_dotPath);
}
#endif // VCONFIGMANAGER_H

View File

@ -152,6 +152,13 @@ enum MarkdownConverterType
Showdown
};
enum PlantUMLMode
{
DisablePlantUML = 0,
OnlinePlantUML = 1,
LocalPlantUML = 2
};
struct MarkdownitOption
{

View File

@ -1,11 +1,15 @@
#include "vdocument.h"
#include "vfile.h"
#include <QDebug>
#include "vfile.h"
#include "vplantumlhelper.h"
VDocument::VDocument(const VFile *v_file, QObject *p_parent)
: QObject(p_parent),
m_file(v_file),
m_readyToHighlight(false)
m_readyToHighlight(false),
m_plantUMLHelper(NULL)
{
}
@ -132,3 +136,16 @@ void VDocument::updateWordCountInfo(int p_wordCount,
emit wordCountInfoUpdated();
}
void VDocument::processPlantUML(int p_id, const QString &p_format, const QString &p_text)
{
if (!m_plantUMLHelper) {
m_plantUMLHelper = new VPlantUMLHelper(this);
connect(m_plantUMLHelper, &VPlantUMLHelper::resultReady,
this, [this](int p_id, const QString &p_format, const QString &p_result) {
emit plantUMLResultReady(p_id, p_format, p_result);
});
}
m_plantUMLHelper->processAsync(p_id, p_format, p_text);
}

View File

@ -7,6 +7,7 @@
#include "vwordcountinfo.h"
class VFile;
class VPlantUMLHelper;
class VDocument : public QObject
{
@ -82,6 +83,9 @@ public slots:
int p_charWithoutSpacesCount,
int p_charWithSpacesCount);
// Web-side call this to process PlantUML locally.
void processPlantUML(int p_id, const QString &p_format, const QString &p_text);
signals:
void textChanged(const QString &text);
@ -118,6 +122,8 @@ signals:
void wordCountInfoUpdated();
void plantUMLResultReady(int p_id, const QString &p_format, const QString &p_result);
private:
QString m_toc;
QString m_header;
@ -137,6 +143,8 @@ private:
bool m_readyToTextToHtml;
VWordCountInfo m_wordCountInfo;
VPlantUMLHelper *m_plantUMLHelper;
};
inline bool VDocument::isReadyToHighlight() const

View File

@ -67,7 +67,7 @@ bool VHelpUE::initListWidget()
m_listWidget->addItem(tr("Ctrl+D: Cancel the command"));
m_listWidget->addItem(tr("Ctrl+J: Go to next item"));
m_listWidget->addItem(tr("Ctrl+K: Go to previous item"));
m_listWidget->addItem(tr("Ctrl+R: Go to current item's parent item"));
m_listWidget->addItem(tr("Ctrl+L: Go to current item's parent item"));
m_listWidget->addItem(tr("Ctrl+T: Expand/Collapse current item"));
m_listWidget->addItem(tr("Ctrl+S: Sort items"));
m_listWidget->addItem(tr("Enter: Activate current item"));

View File

@ -50,6 +50,9 @@ const QString VNote::c_mermaidForestCssFile = ":/utils/mermaid/mermaid.forest.cs
const QString VNote::c_flowchartJsFile = ":/utils/flowchart.js/flowchart.min.js";
const QString VNote::c_raphaelJsFile = ":/utils/flowchart.js/raphael.min.js";
const QString VNote::c_plantUMLJsFile = "http://s.plantuml.com/synchro2.js";
const QString VNote::c_plantUMLZopfliJsFile = "http://s.plantuml.com/zopfli.raw.min.js";
const QString VNote::c_highlightjsLineNumberExtraFile = ":/utils/highlightjs/highlightjs-line-numbers.min.js";
const QString VNote::c_docFileFolder = ":/resources/docs";
@ -128,6 +131,7 @@ QString VNote::generateHtmlTemplate(const QString &p_renderBg,
"code { word-break: break-all !important; }\n"
"div.flowchart-diagram { overflow: hidden !important; }\n"
"div.mermaid-diagram { overflow: hidden !important; }\n"
"div.plantuml-diagram { overflow: hidden !important; }\n"
"a { word-break: break-all !important; }\n"
"td.hljs-ln-code { white-space: pre-wrap !important; "
"word-break: break-all !important; }\n";

View File

@ -64,6 +64,10 @@ public:
static const QString c_flowchartJsFile;
static const QString c_raphaelJsFile;
// PlantUML
static const QString c_plantUMLJsFile;
static const QString c_plantUMLZopfliJsFile;
// Highlight.js line number plugin
static const QString c_highlightjsLineNumberExtraFile;

90
src/vplantumlhelper.cpp Normal file
View File

@ -0,0 +1,90 @@
#include "vplantumlhelper.h"
#include <QDebug>
#include <QThread>
#include "vconfigmanager.h"
extern VConfigManager *g_config;
#define TaskIdProperty "PlantUMLTaskId"
#define TaskFormatProperty "PlantUMLTaskFormat"
VPlantUMLHelper::VPlantUMLHelper(QObject *p_parent)
: QObject(p_parent)
{
prepareCommand(m_program, m_args);
}
void VPlantUMLHelper::processAsync(int p_id, const QString &p_format, const QString &p_text)
{
QProcess *process = new QProcess(this);
process->setProperty(TaskIdProperty, p_id);
process->setProperty(TaskFormatProperty, p_format);
connect(process, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(handleProcessFinished(int, QProcess::ExitStatus)));
QStringList args(m_args);
args << ("-t" + p_format);
qDebug() << m_program << args;
process->start(m_program, args);
process->write(p_text.toUtf8());
process->closeWriteChannel();
}
void VPlantUMLHelper::prepareCommand(QString &p_program, QStringList &p_args) const
{
p_program = "java";
p_args << "-jar" << g_config->getPlantUMLJar();
p_args << "-charset" << "UTF-8";
int nbthread = QThread::idealThreadCount();
p_args << "-nbthread" << QString::number(nbthread > 0 ? nbthread : 1);
const QString &dot = g_config->getPlantUMLDot();
if (!dot.isEmpty()) {
p_args << "-graphvizdot";
p_args << dot;
}
p_args << "-pipe";
}
void VPlantUMLHelper::handleProcessFinished(int p_exitCode, QProcess::ExitStatus p_exitStatus)
{
QProcess *process = static_cast<QProcess *>(sender());
int id = process->property(TaskIdProperty).toInt();
QString format = process->property(TaskFormatProperty).toString();
qDebug() << "process finished" << id << format << p_exitCode << p_exitStatus;
bool failed = true;
if (p_exitStatus == QProcess::NormalExit) {
if (p_exitCode < 0) {
qWarning() << "PlantUML fail" << p_exitCode;
} else {
failed = false;
QByteArray outBa = process->readAllStandardOutput();
if (format == "svg") {
emit resultReady(id, format, QString::fromLocal8Bit(outBa));
} else {
emit resultReady(id, format, QString::fromLocal8Bit(outBa.toBase64()));
}
}
} else {
qWarning() << "fail to start PlantUML process" << p_exitCode << p_exitStatus;
}
if (failed) {
QByteArray errBa = process->readAllStandardError();
if (!errBa.isEmpty()) {
QString errStr(QString::fromLocal8Bit(errBa));
qWarning() << "PlantUML stderr:" << errStr;
}
emit resultReady(id, format, "");
}
process->deleteLater();
}

30
src/vplantumlhelper.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef VPLANTUMLHELPER_H
#define VPLANTUMLHELPER_H
#include <QObject>
#include <QStringList>
#include <QProcess>
class VPlantUMLHelper : public QObject
{
Q_OBJECT
public:
explicit VPlantUMLHelper(QObject *p_parent = nullptr);
void processAsync(int p_id, const QString &p_format, const QString &p_text);
void prepareCommand(QString &p_cmd, QStringList &p_args) const;
signals:
void resultReady(int p_id, const QString &p_format, const QString &p_result);
private slots:
void handleProcessFinished(int p_exitCode, QProcess::ExitStatus p_exitStatus);
private:
QString m_program;
QStringList m_args;
};
#endif // VPLANTUMLHELPER_H

View File

@ -16,14 +16,14 @@ bool VPreviewPage::acceptNavigationRequest(const QUrl &p_url,
bool p_isMainFrame)
{
Q_UNUSED(p_type);
Q_UNUSED(p_isMainFrame);
if (p_url.isLocalFile()) {
QString filePath = p_url.toLocalFile();
if (g_mainWin->tryOpenInternalFile(filePath)) {
qDebug() << "internal notes jump" << filePath;
return false;
}
} else if (!p_isMainFrame) {
return true;
}
QDesktopServices::openUrl(p_url);

View File

@ -352,9 +352,9 @@ void VUniversalEntry::keyPressEvent(QKeyEvent *p_event)
break;
case Qt::Key_R:
case Qt::Key_L:
if (VUtils::isControlModifierForVim(modifiers)) {
// Ctrl+R to go up a level.
// Ctrl+L to go up a level.
if (m_lastEntry) {
m_lastEntry->m_entry->selectParentItem(m_lastEntry->m_id);
}