vnote.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #include <QSettings>
  2. #include <QDebug>
  3. #include <QJsonObject>
  4. #include <QJsonArray>
  5. #include <QDir>
  6. #include <QFont>
  7. #include <QFontMetrics>
  8. #include <QStringList>
  9. #include <QFontDatabase>
  10. #include "vnote.h"
  11. #include "utils/vutils.h"
  12. #include "vconfigmanager.h"
  13. #include "vorphanfile.h"
  14. #include "vnotefile.h"
  15. #include "vpalette.h"
  16. extern VConfigManager *g_config;
  17. extern VPalette *g_palette;
  18. // Meta word manager.
  19. VMetaWordManager *g_mwMgr;
  20. QString VNote::s_simpleHtmlTemplate;
  21. QString VNote::s_markdownTemplate;
  22. QString VNote::s_sloganTemplate = "<div class=\"typewriter\"><h3>Hi Markdown, I'm VNote</h3></div>";
  23. const QString VNote::c_hoedownJsFile = ":/resources/hoedown.js";
  24. const QString VNote::c_markedJsFile = ":/resources/marked.js";
  25. const QString VNote::c_markedExtraFile = ":/utils/marked/marked.min.js";
  26. const QString VNote::c_markdownitJsFile = ":/resources/markdown-it.js";
  27. const QString VNote::c_markdownitExtraFile = ":/utils/markdown-it/markdown-it.min.js";
  28. const QString VNote::c_markdownitAnchorExtraFile = ":/utils/markdown-it/markdown-it-headinganchor.js";
  29. const QString VNote::c_markdownitTaskListExtraFile = ":/utils/markdown-it/markdown-it-task-lists.min.js";
  30. const QString VNote::c_markdownitSubExtraFile = ":/utils/markdown-it/markdown-it-sub.min.js";
  31. const QString VNote::c_markdownitSupExtraFile = ":/utils/markdown-it/markdown-it-sup.min.js";
  32. const QString VNote::c_markdownitFootnoteExtraFile = ":/utils/markdown-it/markdown-it-footnote.min.js";
  33. const QString VNote::c_showdownJsFile = ":/resources/showdown.js";
  34. const QString VNote::c_showdownExtraFile = ":/utils/showdown/showdown.min.js";
  35. const QString VNote::c_showdownAnchorExtraFile = ":/utils/showdown/showdown-headinganchor.js";
  36. const QString VNote::c_mermaidApiJsFile = ":/utils/mermaid/mermaidAPI.min.js";
  37. const QString VNote::c_mermaidForestCssFile = ":/utils/mermaid/mermaid.forest.css";
  38. const QString VNote::c_flowchartJsFile = ":/utils/flowchart.js/flowchart.min.js";
  39. const QString VNote::c_raphaelJsFile = ":/utils/flowchart.js/raphael.min.js";
  40. const QString VNote::c_highlightjsLineNumberExtraFile = ":/utils/highlightjs/highlightjs-line-numbers.min.js";
  41. const QString VNote::c_docFileFolder = ":/resources/docs";
  42. const QString VNote::c_shortcutsDocFile = "shortcuts.md";
  43. const QString VNote::c_markdownGuideDocFile = "markdown_guide.md";
  44. VNote::VNote(QObject *parent)
  45. : QObject(parent)
  46. {
  47. initTemplate();
  48. g_config->getNotebooks(m_notebooks, this);
  49. m_metaWordMgr.init();
  50. g_mwMgr = &m_metaWordMgr;
  51. }
  52. void VNote::initTemplate()
  53. {
  54. if (s_markdownTemplate.isEmpty()) {
  55. updateSimpletHtmlTemplate();
  56. updateTemplate();
  57. }
  58. }
  59. void VNote::updateSimpletHtmlTemplate()
  60. {
  61. const QString c_simpleHtmlTemplatePath(":/resources/simple_template.html");
  62. const QString cssHolder("CSS_PLACE_HOLDER");
  63. s_simpleHtmlTemplate = VUtils::readFileFromDisk(c_simpleHtmlTemplatePath);
  64. g_palette->fillStyle(s_simpleHtmlTemplate);
  65. s_simpleHtmlTemplate.replace(cssHolder, g_config->getCssStyleUrl());
  66. }
  67. QString VNote::generateHtmlTemplate(const QString &p_renderBg,
  68. const QString &p_renderStyleUrl,
  69. const QString &p_codeBlockStyleUrl,
  70. bool p_isPDF)
  71. {
  72. const QString c_markdownTemplatePath(":/resources/markdown_template.html");
  73. QString cssStyle;
  74. if (!p_renderBg.isEmpty()) {
  75. cssStyle += "body { background-color: " + p_renderBg + " !important; }\n";
  76. }
  77. if (g_config->getEnableImageConstraint()) {
  78. // Constain the image width.
  79. cssStyle += "img { max-width: 100% !important; height: auto !important; }\n";
  80. }
  81. const QString styleHolder("/* BACKGROUND_PLACE_HOLDER */");
  82. const QString cssHolder("CSS_PLACE_HOLDER");
  83. const QString codeBlockCssHolder("HIGHLIGHTJS_CSS_PLACE_HOLDER");
  84. QString templ = VUtils::readFileFromDisk(c_markdownTemplatePath);
  85. g_palette->fillStyle(templ);
  86. // Must replace the code block holder first.
  87. templ.replace(codeBlockCssHolder, p_codeBlockStyleUrl);
  88. templ.replace(cssHolder, p_renderStyleUrl);
  89. if (p_isPDF) {
  90. // Shoudl not display scrollbar in PDF.
  91. cssStyle += "pre { white-space: pre-wrap !important; "
  92. "word-break: break-all !important; }\n"
  93. "pre code { white-space: pre-wrap !important; "
  94. "word-break: break-all !important; }\n"
  95. "code { word-break: break-all !important; }\n";
  96. if (!g_config->getEnableImageConstraint()) {
  97. // Constain the image width by force in PDF, otherwise, the PDF will
  98. // be cut off.
  99. cssStyle += "img { max-width: 100% !important; height: auto !important; }\n";
  100. }
  101. }
  102. if (!cssStyle.isEmpty()) {
  103. templ.replace(styleHolder, cssStyle);
  104. }
  105. return templ;
  106. }
  107. QString VNote::generateExportHtmlTemplate(const QString &p_renderBg)
  108. {
  109. const QString c_exportTemplatePath(":/resources/export_template.html");
  110. QString cssStyle;
  111. if (!p_renderBg.isEmpty()) {
  112. cssStyle += "body { background-color: " + p_renderBg + " !important; }\n";
  113. }
  114. if (g_config->getEnableImageConstraint()) {
  115. // Constain the image width.
  116. cssStyle += "img { max-width: 100% !important; height: auto !important; }\n";
  117. }
  118. const QString styleHolder("/* BACKGROUND_PLACE_HOLDER */");
  119. QString templ = VUtils::readFileFromDisk(c_exportTemplatePath);
  120. g_palette->fillStyle(templ);
  121. if (!cssStyle.isEmpty()) {
  122. templ.replace(styleHolder, cssStyle);
  123. }
  124. return templ;
  125. }
  126. void VNote::updateTemplate()
  127. {
  128. QString renderBg = g_config->getRenderBackgroundColor(g_config->getCurRenderBackgroundColor());
  129. s_markdownTemplate = generateHtmlTemplate(renderBg,
  130. g_config->getCssStyleUrl(),
  131. g_config->getCodeBlockCssStyleUrl(),
  132. false);
  133. }
  134. const QVector<VNotebook *> &VNote::getNotebooks() const
  135. {
  136. return m_notebooks;
  137. }
  138. QVector<VNotebook *> &VNote::getNotebooks()
  139. {
  140. return m_notebooks;
  141. }
  142. QString VNote::getNavigationLabelStyle(const QString &p_str) const
  143. {
  144. static int lastLen = -1;
  145. static int pxWidth = 24;
  146. const int fontPt = 15;
  147. QString fontFamily = getMonospacedFont();
  148. if (p_str.size() != lastLen) {
  149. QFont font(fontFamily, fontPt);
  150. font.setBold(true);
  151. QFontMetrics fm(font);
  152. pxWidth = fm.width(p_str);
  153. lastLen = p_str.size();
  154. }
  155. QColor bg(g_palette->color("navigation_label_bg"));
  156. bg.setAlpha(200);
  157. return QString("background-color: %1;"
  158. "color: %2;"
  159. "font-size: %3pt;"
  160. "font: bold;"
  161. "font-family: %4;"
  162. "border-radius: 3px;"
  163. "min-width: %5px;"
  164. "max-width: %5px;")
  165. .arg(bg.name(QColor::HexArgb))
  166. .arg(g_palette->color("navigation_label_fg"))
  167. .arg(fontPt)
  168. .arg(fontFamily)
  169. .arg(pxWidth);
  170. }
  171. const QString &VNote::getMonospacedFont() const
  172. {
  173. static QString font;
  174. if (font.isNull()) {
  175. QStringList candidates;
  176. candidates << "Consolas" << "Monaco" << "Andale Mono" << "Monospace" << "Courier New";
  177. QStringList availFamilies = QFontDatabase().families();
  178. for (int i = 0; i < candidates.size(); ++i) {
  179. QString family = candidates[i].trimmed().toLower();
  180. for (int j = 0; j < availFamilies.size(); ++j) {
  181. QString availFamily = availFamilies[j];
  182. availFamily.remove(QRegExp("\\[.*\\]"));
  183. if (family == availFamily.trimmed().toLower()) {
  184. font = availFamily;
  185. return font;
  186. }
  187. }
  188. }
  189. // Fallback to current font.
  190. font = QFont().family();
  191. }
  192. return font;
  193. }
  194. VOrphanFile *VNote::getOrphanFile(const QString &p_path, bool p_modifiable, bool p_systemFile)
  195. {
  196. if (p_path.isEmpty()) {
  197. return NULL;
  198. }
  199. QString path = QDir::cleanPath(p_path);
  200. // See if the file has already been opened before.
  201. for (auto const &file : m_externalFiles) {
  202. if (VUtils::equalPath(QDir::cleanPath(file->fetchPath()), path)) {
  203. Q_ASSERT(file->isModifiable() == p_modifiable);
  204. Q_ASSERT(file->isSystemFile() == p_systemFile);
  205. return file;
  206. }
  207. }
  208. freeOrphanFiles();
  209. // Create a VOrphanFile for path.
  210. VOrphanFile *file = new VOrphanFile(this, path, p_modifiable, p_systemFile);
  211. m_externalFiles.append(file);
  212. return file;
  213. }
  214. VNoteFile *VNote::getInternalFile(const QString &p_path)
  215. {
  216. VNoteFile *file = NULL;
  217. for (auto & nb : m_notebooks) {
  218. file = nb->tryLoadFile(p_path);
  219. if (file) {
  220. break;
  221. }
  222. }
  223. return file;
  224. }
  225. VFile *VNote::getFile(const QString &p_path)
  226. {
  227. VFile *file = getInternalFile(p_path);
  228. if (!file) {
  229. QFileInfo fi(p_path);
  230. if (fi.isNativePath()) {
  231. file = getOrphanFile(p_path, true, false);
  232. } else {
  233. // File in Qt resource system.
  234. file = getOrphanFile(p_path, false, true);
  235. }
  236. }
  237. return file;
  238. }
  239. VDirectory *VNote::getInternalDirectory(const QString &p_path)
  240. {
  241. VDirectory *dir = NULL;
  242. for (auto & nb : m_notebooks) {
  243. dir = nb->tryLoadDirectory(p_path);
  244. if (dir) {
  245. break;
  246. }
  247. }
  248. return dir;
  249. }
  250. void VNote::freeOrphanFiles()
  251. {
  252. for (int i = 0; i < m_externalFiles.size();) {
  253. VOrphanFile *file = m_externalFiles[i];
  254. if (!file->isOpened()) {
  255. qDebug() << "release orphan file" << file;
  256. m_externalFiles.removeAt(i);
  257. delete file;
  258. } else {
  259. ++i;
  260. }
  261. }
  262. }