mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support PlantUML
This commit is contained in:
parent
a6087d98a6
commit
9850d9a2a2
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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`的代码块来实现块公式。使用代码块的一个好处是大多数情况下无需转义字符。
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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 \
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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 §ion, 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
|
||||
|
@ -152,6 +152,13 @@ enum MarkdownConverterType
|
||||
Showdown
|
||||
};
|
||||
|
||||
enum PlantUMLMode
|
||||
{
|
||||
DisablePlantUML = 0,
|
||||
OnlinePlantUML = 1,
|
||||
LocalPlantUML = 2
|
||||
};
|
||||
|
||||
|
||||
struct MarkdownitOption
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"));
|
||||
|
@ -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";
|
||||
|
@ -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
90
src/vplantumlhelper.cpp
Normal 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
30
src/vplantumlhelper.h
Normal 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
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user