#include #include #include #include #include #include #include #include #include #include "vnote.h" #include "utils/vutils.h" #include "vconfigmanager.h" #include "vorphanfile.h" #include "vnotefile.h" #include "vpalette.h" extern VConfigManager *g_config; extern VPalette *g_palette; // Meta word manager. VMetaWordManager *g_mwMgr; QString VNote::s_simpleHtmlTemplate; QString VNote::s_markdownTemplate; QString VNote::s_sloganTemplate = "

Hi Markdown, I'm VNote

"; const QString VNote::c_hoedownJsFile = ":/resources/hoedown.js"; const QString VNote::c_markedJsFile = ":/resources/marked.js"; const QString VNote::c_markedExtraFile = ":/utils/marked/marked.min.js"; const QString VNote::c_markdownitJsFile = ":/resources/markdown-it.js"; const QString VNote::c_markdownitExtraFile = ":/utils/markdown-it/markdown-it.min.js"; const QString VNote::c_markdownitAnchorExtraFile = ":/utils/markdown-it/markdown-it-headinganchor.js"; const QString VNote::c_markdownitTaskListExtraFile = ":/utils/markdown-it/markdown-it-task-lists.min.js"; const QString VNote::c_markdownitSubExtraFile = ":/utils/markdown-it/markdown-it-sub.min.js"; const QString VNote::c_markdownitSupExtraFile = ":/utils/markdown-it/markdown-it-sup.min.js"; const QString VNote::c_markdownitFootnoteExtraFile = ":/utils/markdown-it/markdown-it-footnote.min.js"; const QString VNote::c_showdownJsFile = ":/resources/showdown.js"; const QString VNote::c_showdownExtraFile = ":/utils/showdown/showdown.min.js"; const QString VNote::c_showdownAnchorExtraFile = ":/utils/showdown/showdown-headinganchor.js"; const QString VNote::c_mermaidApiJsFile = ":/utils/mermaid/mermaidAPI.min.js"; const QString VNote::c_mermaidForestCssFile = ":/utils/mermaid/mermaid.forest.css"; 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_highlightjsLineNumberExtraFile = ":/utils/highlightjs/highlightjs-line-numbers.min.js"; const QString VNote::c_docFileFolder = ":/resources/docs"; const QString VNote::c_shortcutsDocFile = "shortcuts.md"; const QString VNote::c_markdownGuideDocFile = "markdown_guide.md"; VNote::VNote(QObject *parent) : QObject(parent) { initTemplate(); g_config->getNotebooks(m_notebooks, this); m_metaWordMgr.init(); g_mwMgr = &m_metaWordMgr; } void VNote::initTemplate() { if (s_markdownTemplate.isEmpty()) { updateSimpletHtmlTemplate(); updateTemplate(); } } void VNote::updateSimpletHtmlTemplate() { const QString c_simpleHtmlTemplatePath(":/resources/simple_template.html"); const QString cssHolder("CSS_PLACE_HOLDER"); s_simpleHtmlTemplate = VUtils::readFileFromDisk(c_simpleHtmlTemplatePath); g_palette->fillStyle(s_simpleHtmlTemplate); s_simpleHtmlTemplate.replace(cssHolder, g_config->getCssStyleUrl()); } QString VNote::generateHtmlTemplate(const QString &p_renderBg, const QString &p_renderStyleUrl, const QString &p_codeBlockStyleUrl, bool p_isPDF) { const QString c_markdownTemplatePath(":/resources/markdown_template.html"); QString cssStyle; if (!p_renderBg.isEmpty()) { cssStyle += "body { background-color: " + p_renderBg + " !important; }\n"; } if (g_config->getEnableImageConstraint()) { // Constain the image width. cssStyle += "img { max-width: 100% !important; height: auto !important; }\n"; } const QString styleHolder("/* BACKGROUND_PLACE_HOLDER */"); const QString cssHolder("CSS_PLACE_HOLDER"); const QString codeBlockCssHolder("HIGHLIGHTJS_CSS_PLACE_HOLDER"); QString templ = VUtils::readFileFromDisk(c_markdownTemplatePath); g_palette->fillStyle(templ); // Must replace the code block holder first. templ.replace(codeBlockCssHolder, p_codeBlockStyleUrl); templ.replace(cssHolder, p_renderStyleUrl); if (p_isPDF) { // Shoudl not display scrollbar in PDF. cssStyle += "pre { white-space: pre-wrap !important; " "word-break: break-all !important; }\n" "pre code { white-space: pre-wrap !important; " "word-break: break-all !important; }\n" "code { word-break: break-all !important; }\n" "div.flowchart-diagram { overflow: hidden !important; }\n" "div.mermaid-diagram { overflow: hidden !important; }\n" "a { word-break: break-all !important; }\n"; if (!g_config->getEnableImageConstraint()) { // Constain the image width by force in PDF, otherwise, the PDF will // be cut off. cssStyle += "img { max-width: 100% !important; height: auto !important; }\n"; } } if (!cssStyle.isEmpty()) { templ.replace(styleHolder, cssStyle); } return templ; } QString VNote::generateExportHtmlTemplate(const QString &p_renderBg) { const QString c_exportTemplatePath(":/resources/export_template.html"); QString cssStyle; if (!p_renderBg.isEmpty()) { cssStyle += "body { background-color: " + p_renderBg + " !important; }\n"; } if (g_config->getEnableImageConstraint()) { // Constain the image width. cssStyle += "img { max-width: 100% !important; height: auto !important; }\n"; } const QString styleHolder("/* BACKGROUND_PLACE_HOLDER */"); QString templ = VUtils::readFileFromDisk(c_exportTemplatePath); g_palette->fillStyle(templ); if (!cssStyle.isEmpty()) { templ.replace(styleHolder, cssStyle); } return templ; } void VNote::updateTemplate() { QString renderBg = g_config->getRenderBackgroundColor(g_config->getCurRenderBackgroundColor()); s_markdownTemplate = generateHtmlTemplate(renderBg, g_config->getCssStyleUrl(), g_config->getCodeBlockCssStyleUrl(), false); } const QVector &VNote::getNotebooks() const { return m_notebooks; } QVector &VNote::getNotebooks() { return m_notebooks; } QString VNote::getNavigationLabelStyle(const QString &p_str) const { static int lastLen = -1; static int pxWidth = 24; const int fontPt = 15; QString fontFamily = getMonospacedFont(); if (p_str.size() != lastLen) { QFont font(fontFamily, fontPt); font.setBold(true); QFontMetrics fm(font); pxWidth = fm.width(p_str); lastLen = p_str.size(); } QColor bg(g_palette->color("navigation_label_bg")); bg.setAlpha(200); return QString("background-color: %1;" "color: %2;" "font-size: %3pt;" "font: bold;" "font-family: %4;" "border-radius: 3px;" "min-width: %5px;" "max-width: %5px;") .arg(bg.name(QColor::HexArgb)) .arg(g_palette->color("navigation_label_fg")) .arg(fontPt) .arg(fontFamily) .arg(pxWidth); } const QString &VNote::getMonospacedFont() const { static QString font; if (font.isNull()) { QStringList candidates; candidates << "Consolas" << "Monaco" << "Andale Mono" << "Monospace" << "Courier New"; QStringList availFamilies = QFontDatabase().families(); for (int i = 0; i < candidates.size(); ++i) { QString family = candidates[i].trimmed().toLower(); for (int j = 0; j < availFamilies.size(); ++j) { QString availFamily = availFamilies[j]; availFamily.remove(QRegExp("\\[.*\\]")); if (family == availFamily.trimmed().toLower()) { font = availFamily; return font; } } } // Fallback to current font. font = QFont().family(); } return font; } VOrphanFile *VNote::getOrphanFile(const QString &p_path, bool p_modifiable, bool p_systemFile) { if (p_path.isEmpty()) { return NULL; } QString path = QDir::cleanPath(p_path); // See if the file has already been opened before. for (auto const &file : m_externalFiles) { if (VUtils::equalPath(QDir::cleanPath(file->fetchPath()), path)) { Q_ASSERT(file->isModifiable() == p_modifiable); Q_ASSERT(file->isSystemFile() == p_systemFile); return file; } } freeOrphanFiles(); // Create a VOrphanFile for path. VOrphanFile *file = new VOrphanFile(this, path, p_modifiable, p_systemFile); m_externalFiles.append(file); return file; } VNoteFile *VNote::getInternalFile(const QString &p_path) { VNoteFile *file = NULL; for (auto & nb : m_notebooks) { file = nb->tryLoadFile(p_path); if (file) { break; } } return file; } VFile *VNote::getFile(const QString &p_path) { VFile *file = getInternalFile(p_path); if (!file) { QFileInfo fi(p_path); if (fi.isNativePath()) { file = getOrphanFile(p_path, true, false); } else { // File in Qt resource system. file = getOrphanFile(p_path, false, true); } } return file; } VDirectory *VNote::getInternalDirectory(const QString &p_path) { VDirectory *dir = NULL; for (auto & nb : m_notebooks) { dir = nb->tryLoadDirectory(p_path); if (dir) { break; } } return dir; } VNotebook *VNote::getNotebook(const QString &p_path) { for (auto & nb : m_notebooks) { if (VUtils::equalPath(nb->getPath(), p_path)) { return nb; } } return NULL; } void VNote::freeOrphanFiles() { for (int i = 0; i < m_externalFiles.size();) { VOrphanFile *file = m_externalFiles[i]; if (!file->isOpened()) { qDebug() << "release orphan file" << file; m_externalFiles.removeAt(i); delete file; } else { ++i; } } }