diff --git a/src/resources/hoedown.js b/src/resources/hoedown.js
index e111ae63..408e0569 100644
--- a/src/resources/hoedown.js
+++ b/src/resources/hoedown.js
@@ -41,6 +41,7 @@ var updateHtml = function(html) {
}
}
+ addClassToCodeBlock();
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to
diff --git a/src/resources/markdown-it.js b/src/resources/markdown-it.js
index aac79d6a..51962184 100644
--- a/src/resources/markdown-it.js
+++ b/src/resources/markdown-it.js
@@ -97,6 +97,7 @@ var updateText = function(text) {
insertImageCaption();
renderMermaid('lang-mermaid');
renderFlowchart('lang-flowchart');
+ addClassToCodeBlock();
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to
diff --git a/src/resources/markdown_template.html b/src/resources/markdown_template.html
index 5ed8d112..e06c9626 100644
--- a/src/resources/markdown_template.html
+++ b/src/resources/markdown_template.html
@@ -22,7 +22,7 @@
-
+
diff --git a/src/resources/markdown_template.js b/src/resources/markdown_template.js
index 59f7662c..7d6ae805 100644
--- a/src/resources/markdown_template.js
+++ b/src/resources/markdown_template.js
@@ -833,3 +833,14 @@ var renderCodeBlockLineNumber = function() {
}
}
};
+
+var addClassToCodeBlock = function() {
+ var hljsClass = 'hljs';
+ var codes = document.getElementsByTagName('code');
+ for (var i = 0; i < codes.length; ++i) {
+ var code = codes[i];
+ if (code.parentElement.tagName.toLowerCase() == 'pre') {
+ code.classList.add(hljsClass);
+ }
+ }
+};
diff --git a/src/resources/marked.js b/src/resources/marked.js
index 50e5f7b7..32a1162e 100644
--- a/src/resources/marked.js
+++ b/src/resources/marked.js
@@ -49,6 +49,7 @@ var updateText = function(text) {
insertImageCaption();
renderMermaid('lang-mermaid');
renderFlowchart('lang-flowchart');
+ addClassToCodeBlock();
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to
diff --git a/src/resources/showdown.js b/src/resources/showdown.js
index 348dc3c1..f51d0675 100644
--- a/src/resources/showdown.js
+++ b/src/resources/showdown.js
@@ -76,6 +76,7 @@ var updateText = function(text) {
highlightCodeBlocks(document, VEnableMermaid, VEnableFlowchart);
renderMermaid('language-mermaid');
renderFlowchart('language-flowchart');
+ addClassToCodeBlock();
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to
diff --git a/src/resources/styles/default.css b/src/resources/styles/default.css
index 464a36f8..19ef55d6 100644
--- a/src/resources/styles/default.css
+++ b/src/resources/styles/default.css
@@ -77,7 +77,6 @@ p, ul, ol {
}
pre {
- padding: 0px 24px;
background-color: #f8f8f8;
border-radius: 3px;
border: 1px solid #cccccc;
@@ -92,12 +91,12 @@ code {
}
pre code {
- margin: 0;
- padding: 0;
- border: none;
- background: transparent;
- overflow: auto;
- color: #363636;
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: transparent;
+ overflow: auto;
+ color: #363636;
}
aside {
diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini
index 9907d03d..564adf03 100644
--- a/src/resources/vnote.ini
+++ b/src/resources/vnote.ini
@@ -1,6 +1,16 @@
[global]
welcome_page_path=:/resources/welcome.html
+
+; CSS style for Markdown template
template_css=default
+
+; Code block CSS style for Markdown template
+template_code_block_css=vnote
+
+; Code block CSS style file URL for Markdown template
+; If not empty, VNote will ignore template_code_block_css
+template_code_block_css_url=
+
editor_style=default
current_notebook=0
tab_stop_width=4
diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp
index a085f820..688504c1 100644
--- a/src/vconfigmanager.cpp
+++ b/src/vconfigmanager.cpp
@@ -18,7 +18,9 @@ const QString VConfigManager::c_obsoleteDirConfigFile = QString(".vnote.json");
const QString VConfigManager::c_dirConfigFile = QString("_vnote.json");
const QString VConfigManager::defaultConfigFilePath = QString(":/resources/vnote.ini");
const QString VConfigManager::c_styleConfigFolder = QString("styles");
+const QString VConfigManager::c_codeBlockStyleConfigFolder = QString("codeblock_styles");
const QString VConfigManager::c_defaultCssFile = QString(":/resources/styles/default.css");
+const QString VConfigManager::c_defaultCodeBlockCssFile = QString(":/utils/highlightjs/styles/vnote.css");
const QString VConfigManager::c_defaultMdhlFile = QString(":/resources/styles/default.mdhl");
const QString VConfigManager::c_solarizedDarkMdhlFile = QString(":/resources/styles/solarized-dark.mdhl");
const QString VConfigManager::c_solarizedLightMdhlFile = QString(":/resources/styles/solarized-light.mdhl");
@@ -55,6 +57,7 @@ void VConfigManager::initialize()
// Override the default css styles on start up.
outputDefaultCssStyle();
+ outputDefaultCodeBlockCssStyle();
outputDefaultEditorStyle();
m_defaultEditPalette = QTextEdit().palette();
@@ -63,6 +66,8 @@ void VConfigManager::initialize()
welcomePagePath = getConfigFromSettings("global", "welcome_page_path").toString();
m_templateCss = getConfigFromSettings("global", "template_css").toString();
+ m_templateCodeBlockCss = getConfigFromSettings("global", "template_code_block_css").toString();
+ m_templateCodeBlockCssUrl = getConfigFromSettings("global", "template_code_block_css_url").toString();
curNotebookIndex = getConfigFromSettings("global", "current_notebook").toInt();
markdownExtensions = hoedown_extensions(HOEDOWN_EXT_TABLES | HOEDOWN_EXT_FENCED_CODE |
@@ -606,6 +611,11 @@ QString VConfigManager::getStyleConfigFolder() const
return getConfigFolder() + QDir::separator() + c_styleConfigFolder;
}
+QString VConfigManager::getCodeBlockStyleConfigFolder() const
+{
+ return getStyleConfigFolder() + QDir::separator() + c_codeBlockStyleConfigFolder;
+}
+
QVector VConfigManager::getCssStyles() const
{
QVector res;
@@ -627,6 +637,27 @@ QVector VConfigManager::getCssStyles() const
return res;
}
+QVector VConfigManager::getCodeBlockCssStyles() const
+{
+ QVector res;
+ QDir dir(getCodeBlockStyleConfigFolder());
+ if (!dir.exists()) {
+ // Output pre-defined CSS styles to this folder.
+ outputDefaultCodeBlockCssStyle();
+ }
+
+ // Get all the .css files in the folder.
+ dir.setFilter(QDir::Files | QDir::NoSymLinks);
+ dir.setNameFilters(QStringList("*.css"));
+ QStringList files = dir.entryList();
+ res.reserve(files.size());
+ for (auto const &item : files) {
+ res.push_back(item.left(item.size() - 4));
+ }
+
+ return res;
+}
+
QVector VConfigManager::getEditorStyles() const
{
QVector res;
@@ -651,15 +682,46 @@ QVector VConfigManager::getEditorStyles() const
bool VConfigManager::outputDefaultCssStyle() const
{
// Make sure the styles folder exists.
- QDir dir(getConfigFolder());
- if (!dir.exists(c_styleConfigFolder)) {
- if (!dir.mkdir(c_styleConfigFolder)) {
+ QString folderPath = getStyleConfigFolder();
+ QDir dir(folderPath);
+ if (!dir.exists()) {
+ if (!dir.mkpath(folderPath)) {
return false;
}
}
QString srcPath = c_defaultCssFile;
- QString destPath = getStyleConfigFolder() + QDir::separator() + QFileInfo(srcPath).fileName();
+ QString destPath = folderPath + QDir::separator() + QFileInfo(srcPath).fileName();
+
+ if (QFileInfo::exists(destPath)) {
+ QString bakPath = destPath + ".bak";
+ // We only keep one bak file.
+ if (!QFileInfo::exists(bakPath)) {
+ QFile::rename(destPath, bakPath);
+ } else {
+ // Just delete the default style.
+ QFile file(destPath);
+ file.setPermissions(QFile::ReadUser | QFile::WriteUser);
+ file.remove();
+ }
+ }
+
+ return VUtils::copyFile(srcPath, destPath, false);
+}
+
+bool VConfigManager::outputDefaultCodeBlockCssStyle() const
+{
+ // Make sure the styles folder exists.
+ QString folderPath = getCodeBlockStyleConfigFolder();
+ QDir dir(folderPath);
+ if (!dir.exists()) {
+ if (!dir.mkpath(folderPath)) {
+ return false;
+ }
+ }
+
+ QString srcPath = c_defaultCodeBlockCssFile;
+ QString destPath = folderPath + QDir::separator() + QFileInfo(srcPath).fileName();
if (QFileInfo::exists(destPath)) {
QString bakPath = destPath + ".bak";
@@ -753,6 +815,36 @@ QString VConfigManager::getTemplateCssUrl()
return cssPath;
}
+// The URL will be used in the Web page.
+QString VConfigManager::getTemplateCodeBlockCssUrl()
+{
+ if (!m_templateCodeBlockCssUrl.isEmpty()) {
+ return m_templateCodeBlockCssUrl;
+ }
+
+ QString cssPath = getCodeBlockStyleConfigFolder() +
+ QDir::separator() +
+ m_templateCodeBlockCss + ".css";
+ QUrl cssUrl = QUrl::fromLocalFile(cssPath);
+ cssPath = cssUrl.toString();
+ if (!QFile::exists(cssUrl.toLocalFile())) {
+ // Specified css not exists.
+ if (m_templateCss == "vnote") {
+ bool ret = outputDefaultCodeBlockCssStyle();
+ if (!ret) {
+ // Use embedded file.
+ cssPath = "qrc" + c_defaultCodeBlockCssFile;
+ }
+ } else {
+ setTemplateCodeBlockCss("vnote");
+ return getTemplateCodeBlockCssUrl();
+ }
+ }
+
+ qDebug() << "use template code block css:" << cssPath;
+ return cssPath;
+}
+
QString VConfigManager::getEditorStyleUrl()
{
QString mdhlPath = getStyleConfigFolder() + QDir::separator() + m_editorStyle + ".mdhl";
@@ -775,20 +867,6 @@ QString VConfigManager::getEditorStyleUrl()
}
-const QString &VConfigManager::getTemplateCss() const
-{
- return m_templateCss;
-}
-
-void VConfigManager::setTemplateCss(const QString &p_css)
-{
- if (m_templateCss == p_css) {
- return;
- }
- m_templateCss = p_css;
- setConfigToSettings("global", "template_css", m_templateCss);
-}
-
const QString &VConfigManager::getEditorStyle() const
{
return m_editorStyle;
diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h
index a767c956..5212c4a0 100644
--- a/src/vconfigmanager.h
+++ b/src/vconfigmanager.h
@@ -87,11 +87,16 @@ public:
QString getTemplateCssUrl();
+ QString getTemplateCodeBlockCssUrl();
+
QString getEditorStyleUrl();
const QString &getTemplateCss() const;
void setTemplateCss(const QString &p_css);
+ const QString &getTemplateCodeBlockCss() const;
+ void setTemplateCodeBlockCss(const QString &p_css);
+
const QString &getEditorStyle() const;
void setEditorStyle(const QString &p_style);
@@ -283,6 +288,9 @@ public:
bool getDoubleClickCloseTab() const;
+ // Whether user specify template_code_block_css_url directly.
+ bool getUserSpecifyTemplateCodeBlockCssUrl() const;
+
// Return the configured key sequence of @p_operation.
// Return empty if there is no corresponding config.
QString getShortcutKeySequence(const QString &p_operation) const;
@@ -299,6 +307,12 @@ public:
// Read all available css files in c_styleConfigFolder.
QVector getCssStyles() const;
+ // Get the folder c_codeBlockStyleConfigFolder in the config folder.
+ QString getCodeBlockStyleConfigFolder() const;
+
+ // Read all available css files in c_codeBlockStyleConfigFolder.
+ QVector getCodeBlockCssStyles() const;
+
// Read all available mdhl files in c_styleConfigFolder.
QVector getEditorStyles() const;
@@ -326,7 +340,11 @@ private:
// This is for the change of org name.
void migrateIniFile();
+ // Output pre-defined CSS styles to style folder.
bool outputDefaultCssStyle() const;
+
+ bool outputDefaultCodeBlockCssStyle() const;
+
bool outputDefaultEditorStyle() const;
// See if the old c_obsoleteDirConfigFile exists. If so, rename it to
@@ -363,7 +381,17 @@ private:
QHash m_codeBlockStyles;
QString welcomePagePath;
+
+ // CSS style for Markdown template.
QString m_templateCss;
+
+ // Code block CSS style for Markdown template.
+ QString m_templateCodeBlockCss;
+
+ // Code block CSS style file URL for Markdown template.
+ // If not empty, VNote will ignore m_templateCodeBlockCss.
+ QString m_templateCodeBlockCssUrl;
+
QString m_editorStyle;
int curNotebookIndex;
@@ -585,10 +613,19 @@ private:
QSettings *userSettings;
// Qsettings for @defaultConfigFileName
QSettings *defaultSettings;
+
// The folder name of style files.
static const QString c_styleConfigFolder;
+
+ // The folder name of code block style files.
+ static const QString c_codeBlockStyleConfigFolder;
+
+ // Default CSS file in resource system.
static const QString c_defaultCssFile;
+ // Default code block CSS file in resource system.
+ static const QString c_defaultCodeBlockCssFile;
+
// MDHL files for editor styles.
static const QString c_defaultMdhlFile;
static const QString c_solarizedDarkMdhlFile;
@@ -1498,4 +1535,39 @@ inline bool VConfigManager::getDoubleClickCloseTab() const
return m_doubleClickCloseTab;
}
+inline const QString &VConfigManager::getTemplateCss() const
+{
+ return m_templateCss;
+}
+
+inline void VConfigManager::setTemplateCss(const QString &p_css)
+{
+ if (m_templateCss == p_css) {
+ return;
+ }
+
+ m_templateCss = p_css;
+ setConfigToSettings("global", "template_css", m_templateCss);
+}
+
+inline const QString &VConfigManager::getTemplateCodeBlockCss() const
+{
+ return m_templateCodeBlockCss;
+}
+
+inline void VConfigManager::setTemplateCodeBlockCss(const QString &p_css)
+{
+ if (m_templateCodeBlockCss == p_css) {
+ return;
+ }
+
+ m_templateCodeBlockCss = p_css;
+ setConfigToSettings("global", "template_code_block_css", m_templateCodeBlockCss);
+}
+
+inline bool VConfigManager::getUserSpecifyTemplateCodeBlockCssUrl() const
+{
+ return !m_templateCodeBlockCssUrl.isEmpty();
+}
+
#endif // VCONFIGMANAGER_H
diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp
index 2622a6bb..fe1a0eec 100644
--- a/src/vmainwindow.cpp
+++ b/src/vmainwindow.cpp
@@ -579,6 +579,8 @@ void VMainWindow::initMarkdownMenu()
initRenderBackgroundMenu(markdownMenu);
+ initCodeBlockStyleMenu(markdownMenu);
+
QAction *constrainImageAct = new QAction(tr("Constrain The Width of Images"), this);
constrainImageAct->setToolTip(tr("Constrain the width of images to the window in read mode (re-open current tabs to make it work)"));
constrainImageAct->setCheckable(true);
@@ -1319,6 +1321,7 @@ void VMainWindow::updateRenderStyleMenu()
}
// Update the menu actions with styles.
+ QString curStyle = g_config->getTemplateCss();
QVector styles = g_config->getCssStyles();
for (auto const &style : styles) {
QAction *act = new QAction(style, m_renderStyleActs);
@@ -1329,7 +1332,7 @@ void VMainWindow::updateRenderStyleMenu()
// Add it to the menu.
menu->addAction(act);
- if (g_config->getTemplateCss() == style) {
+ if (curStyle == style) {
act->setChecked(true);
}
}
@@ -1347,14 +1350,73 @@ void VMainWindow::initRenderStyleMenu(QMenu *p_menu)
this, &VMainWindow::setRenderStyle);
QAction *addAct = newAction(QIcon(":/resources/icons/add_style.svg"),
- tr("&Add Style"), m_renderStyleActs);
- addAct->setToolTip(tr("Open the folder to add your custom CSS style files"));
+ tr("&Add Style"),
+ m_renderStyleActs);
+ addAct->setToolTip(tr("Open the folder to add your custom CSS style files "
+ "for Markdown rendering"));
addAct->setCheckable(true);
addAct->setData("AddStyle");
styleMenu->addAction(addAct);
}
+void VMainWindow::updateCodeBlockStyleMenu()
+{
+ QMenu *menu = dynamic_cast(sender());
+ V_ASSERT(menu);
+
+ QList actions = menu->actions();
+ // Remove all other actions except the first one.
+ for (int i = 1; i < actions.size(); ++i) {
+ menu->removeAction(actions[i]);
+ m_codeBlockStyleActs->removeAction(actions[i]);
+ delete actions[i];
+ }
+
+ // Update the menu actions with styles.
+ QString curStyle = g_config->getTemplateCodeBlockCss();
+ QVector styles = g_config->getCodeBlockCssStyles();
+ for (auto const &style : styles) {
+ QAction *act = new QAction(style, m_codeBlockStyleActs);
+ act->setToolTip(tr("Set as the code block CSS style for Markdown rendering"));
+ act->setCheckable(true);
+ act->setData(style);
+
+ // Add it to the menu.
+ menu->addAction(act);
+
+ if (curStyle == style) {
+ act->setChecked(true);
+ }
+ }
+}
+
+void VMainWindow::initCodeBlockStyleMenu(QMenu *p_menu)
+{
+ QMenu *styleMenu = p_menu->addMenu(tr("Code Block Style"));
+ styleMenu->setToolTipsVisible(true);
+ connect(styleMenu, &QMenu::aboutToShow,
+ this, &VMainWindow::updateCodeBlockStyleMenu);
+
+ m_codeBlockStyleActs = new QActionGroup(this);
+ connect(m_codeBlockStyleActs, &QActionGroup::triggered,
+ this, &VMainWindow::setCodeBlockStyle);
+
+ QAction *addAct = newAction(QIcon(":/resources/icons/add_style.svg"),
+ tr("&Add Style"),
+ m_codeBlockStyleActs);
+ addAct->setToolTip(tr("Open the folder to add your custom CSS style files "
+ "for Markdown code block rendering"));
+ addAct->setCheckable(true);
+ addAct->setData("AddStyle");
+
+ styleMenu->addAction(addAct);
+
+ if (g_config->getUserSpecifyTemplateCodeBlockCssUrl()) {
+ styleMenu->setEnabled(false);
+ }
+}
+
void VMainWindow::initEditorBackgroundMenu(QMenu *menu)
{
QMenu *backgroundColorMenu = menu->addMenu(tr("&Background Color"));
@@ -1537,6 +1599,23 @@ void VMainWindow::setEditorStyle(QAction *p_action)
}
}
+void VMainWindow::setCodeBlockStyle(QAction *p_action)
+{
+ if (!p_action) {
+ return;
+ }
+
+ QString data = p_action->data().toString();
+ if (data == "AddStyle") {
+ // Add custom style.
+ QUrl url = QUrl::fromLocalFile(g_config->getCodeBlockStyleConfigFolder());
+ QDesktopServices::openUrl(url);
+ } else {
+ g_config->setTemplateCodeBlockCss(data);
+ vnote->updateTemplate();
+ }
+}
+
void VMainWindow::updateActionStateFromTabStatusChange(const VFile *p_file,
bool p_editMode)
{
diff --git a/src/vmainwindow.h b/src/vmainwindow.h
index 8b755200..a0e1bc61 100644
--- a/src/vmainwindow.h
+++ b/src/vmainwindow.h
@@ -83,10 +83,22 @@ private slots:
void setTabStopWidth(QAction *action);
void setEditorBackgroundColor(QAction *action);
void setRenderBackgroundColor(QAction *action);
+
void setRenderStyle(QAction *p_action);
+
void setEditorStyle(QAction *p_action);
+
+ // Set code block render style.
+ void setCodeBlockStyle(QAction *p_action);
+
+ // Update the render styles menu according to existing files.
void updateRenderStyleMenu();
+
void updateEditorStyleMenu();
+
+ // Update the code block styles menu according to existing files.
+ void updateCodeBlockStyleMenu();
+
void changeHighlightCursorLine(bool p_checked);
void changeHighlightSelectedWord(bool p_checked);
void changeHighlightSearchedWord(bool p_checked);
@@ -161,7 +173,11 @@ private:
void initAvatar();
void initPredefinedColorPixmaps();
void initRenderBackgroundMenu(QMenu *menu);
+
void initRenderStyleMenu(QMenu *p_menu);
+
+ void initCodeBlockStyleMenu(QMenu *p_menu);
+
void initConverterMenu(QMenu *p_menu);
void initMarkdownitOptionMenu(QMenu *p_menu);
void initEditorBackgroundMenu(QMenu *menu);
@@ -245,9 +261,14 @@ private:
QAction *m_autoIndentAct;
+ // Act group for render styles.
QActionGroup *m_renderStyleActs;
+
QActionGroup *m_editorStyleActs;
+ // Act group for code block render styles.
+ QActionGroup *m_codeBlockStyleActs;
+
QShortcut *m_closeNoteShortcut;
// Menus
diff --git a/src/vnote.cpp b/src/vnote.cpp
index 8a025351..1c318527 100644
--- a/src/vnote.cpp
+++ b/src/vnote.cpp
@@ -180,6 +180,7 @@ void VNote::updateTemplate()
}
}
}
+
QString cssStyle;
if (!rgb.isEmpty()) {
cssStyle += "body { background-color: #" + rgb + " !important; }\n";
@@ -192,8 +193,12 @@ void VNote::updateTemplate()
const QString styleHolder("");
const QString cssHolder("CSS_PLACE_HOLDER");
+ const QString codeBlockCssHolder("HIGHLIGHTJS_CSS_PLACE_HOLDER");
s_markdownTemplate = VUtils::readFileFromDisk(c_markdownTemplatePath);
+
+ // Must replace the code block holder first.
+ s_markdownTemplate.replace(codeBlockCssHolder, g_config->getTemplateCodeBlockCssUrl());
s_markdownTemplate.replace(cssHolder, g_config->getTemplateCssUrl());
s_markdownTemplatePDF = s_markdownTemplate;