vnote.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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 "vmainwindow.h"
  14. #include "vorphanfile.h"
  15. #include "vnotefile.h"
  16. extern VConfigManager *g_config;
  17. // Meta word manager.
  18. VMetaWordManager *g_mwMgr;
  19. QString VNote::s_markdownTemplate;
  20. QString VNote::s_markdownTemplatePDF;
  21. const QString VNote::c_hoedownJsFile = ":/resources/hoedown.js";
  22. const QString VNote::c_markedJsFile = ":/resources/marked.js";
  23. const QString VNote::c_markedExtraFile = ":/utils/marked/marked.min.js";
  24. const QString VNote::c_markdownitJsFile = ":/resources/markdown-it.js";
  25. const QString VNote::c_markdownitExtraFile = ":/utils/markdown-it/markdown-it.min.js";
  26. const QString VNote::c_markdownitAnchorExtraFile = ":/utils/markdown-it/markdown-it-headinganchor.js";
  27. const QString VNote::c_markdownitTaskListExtraFile = ":/utils/markdown-it/markdown-it-task-lists.min.js";
  28. const QString VNote::c_markdownitSubExtraFile = ":/utils/markdown-it/markdown-it-sub.min.js";
  29. const QString VNote::c_markdownitSupExtraFile = ":/utils/markdown-it/markdown-it-sup.min.js";
  30. const QString VNote::c_markdownitFootnoteExtraFile = ":/utils/markdown-it/markdown-it-footnote.min.js";
  31. const QString VNote::c_showdownJsFile = ":/resources/showdown.js";
  32. const QString VNote::c_showdownExtraFile = ":/utils/showdown/showdown.min.js";
  33. const QString VNote::c_showdownAnchorExtraFile = ":/utils/showdown/showdown-headinganchor.js";
  34. const QString VNote::c_mermaidApiJsFile = ":/utils/mermaid/mermaidAPI.min.js";
  35. const QString VNote::c_mermaidCssFile = ":/utils/mermaid/mermaid.css";
  36. const QString VNote::c_mermaidDarkCssFile = ":/utils/mermaid/mermaid.dark.css";
  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_shortcutsDocFile_en = ":/resources/docs/shortcuts_en.md";
  42. const QString VNote::c_shortcutsDocFile_zh = ":/resources/docs/shortcuts_zh.md";
  43. const QString VNote::c_markdownGuideDocFile_en = ":/resources/docs/markdown_guide_en.md";
  44. const QString VNote::c_markdownGuideDocFile_zh = ":/resources/docs/markdown_guide_zh.md";
  45. VNote::VNote(QObject *parent)
  46. : QObject(parent)
  47. {
  48. initTemplate();
  49. g_config->getNotebooks(m_notebooks, this);
  50. m_metaWordMgr.init();
  51. g_mwMgr = &m_metaWordMgr;
  52. }
  53. void VNote::initPalette(QPalette palette)
  54. {
  55. m_palette.clear();
  56. m_palette.append(QPair<QString, QString>("base-background",
  57. palette.background().color().name()));
  58. m_palette.append(QPair<QString, QString>("base-foreground",
  59. palette.background().color().name()));
  60. m_palette.append(QPair<QString, QString>("hover-color", "#42A5F5"));
  61. m_palette.append(QPair<QString, QString>("base-color", "#BDBDBD"));
  62. m_palette.append(QPair<QString, QString>("focus-color", "#75C5B5"));
  63. m_palette.append(QPair<QString, QString>("logo-base", "#D6EACE"));
  64. m_palette.append(QPair<QString, QString>("logo-max", "#15AE67"));
  65. m_palette.append(QPair<QString, QString>("logo-min", "#75C5B5"));
  66. // Material Design Colors
  67. m_palette.append(QPair<QString, QString>("Teal0", "#E0F2F1"));
  68. m_palette.append(QPair<QString, QString>("Teal1", "#B2DFDB"));
  69. m_palette.append(QPair<QString, QString>("Teal2", "#80CBC4"));
  70. m_palette.append(QPair<QString, QString>("Teal3", "#4DB6AC"));
  71. m_palette.append(QPair<QString, QString>("Teal4", "#26A69A"));
  72. m_palette.append(QPair<QString, QString>("Teal5", "#009688"));
  73. m_palette.append(QPair<QString, QString>("Teal6", "#00897B"));
  74. m_palette.append(QPair<QString, QString>("Teal7", "#00796B"));
  75. m_palette.append(QPair<QString, QString>("Teal8", "#00695C"));
  76. m_palette.append(QPair<QString, QString>("Teal9", "#004D40"));
  77. m_palette.append(QPair<QString, QString>("Indigo0", "#E8EAF6"));
  78. m_palette.append(QPair<QString, QString>("Indigo1", "#C5CAE9"));
  79. m_palette.append(QPair<QString, QString>("Indigo2", "#9FA8DA"));
  80. m_palette.append(QPair<QString, QString>("Indigo3", "#7986CB"));
  81. m_palette.append(QPair<QString, QString>("Indigo4", "#5C6BC0"));
  82. m_palette.append(QPair<QString, QString>("Grey0", "#FAFAFA"));
  83. m_palette.append(QPair<QString, QString>("Grey1", "#F5F5F5"));
  84. m_palette.append(QPair<QString, QString>("Grey2", "#EEEEEE"));
  85. m_palette.append(QPair<QString, QString>("Grey3", "#E0E0E0"));
  86. m_palette.append(QPair<QString, QString>("Grey4", "#BDBDBD"));
  87. m_palette.append(QPair<QString, QString>("Grey5", "#9E9E9E"));
  88. m_palette.append(QPair<QString, QString>("Grey6", "#757575"));
  89. m_palette.append(QPair<QString, QString>("Grey7", "#616161"));
  90. m_palette.append(QPair<QString, QString>("Grey8", "#424242"));
  91. m_palette.append(QPair<QString, QString>("Green0", "#E8F5E9"));
  92. m_palette.append(QPair<QString, QString>("Green1", "#C8E6C9"));
  93. m_palette.append(QPair<QString, QString>("Green2", "#A5D6A7"));
  94. m_palette.append(QPair<QString, QString>("Green3", "#81C784"));
  95. m_palette.append(QPair<QString, QString>("Green4", "#66BB6A"));
  96. m_palette.append(QPair<QString, QString>("Green5", "#4CAF50"));
  97. m_palette.append(QPair<QString, QString>("Green6", "#43A047"));
  98. m_palette.append(QPair<QString, QString>("Green7", "#388E3C"));
  99. m_palette.append(QPair<QString, QString>("Green8", "#2E7D32"));
  100. m_palette.append(QPair<QString, QString>("Green9", "#1B5E20"));
  101. m_palette.append(QPair<QString, QString>("DeepPurple0", "#EDE7F6"));
  102. m_palette.append(QPair<QString, QString>("DeepPurple1", "#D1C4E9"));
  103. m_palette.append(QPair<QString, QString>("DeepPurple2", "#B39DDB"));
  104. m_palette.append(QPair<QString, QString>("DeepPurple3", "#9575CD"));
  105. m_palette.append(QPair<QString, QString>("DeepPurple4", "#7E57C2"));
  106. m_palette.append(QPair<QString, QString>("DeepPurple5", "#673AB7"));
  107. m_palette.append(QPair<QString, QString>("DeepPurple6", "#5E35B1"));
  108. m_palette.append(QPair<QString, QString>("DeepPurple7", "#512DA8"));
  109. m_palette.append(QPair<QString, QString>("DeepPurple8", "#4527A0"));
  110. m_palette.append(QPair<QString, QString>("DeepPurple9", "#311B92"));
  111. m_palette.append(QPair<QString, QString>("Purple0", "#F3E5F5"));
  112. m_palette.append(QPair<QString, QString>("Purple1", "#E1BEE7"));
  113. m_palette.append(QPair<QString, QString>("Purple2", "#CE93D8"));
  114. m_palette.append(QPair<QString, QString>("Purple3", "#BA68C8"));
  115. m_palette.append(QPair<QString, QString>("Purple4", "#AB47BC"));
  116. m_palette.append(QPair<QString, QString>("Purple5", "#9C27B0"));
  117. m_palette.append(QPair<QString, QString>("Purple6", "#8E24AA"));
  118. m_palette.append(QPair<QString, QString>("Purple7", "#7B1FA2"));
  119. m_palette.append(QPair<QString, QString>("Purple8", "#6A1B9A"));
  120. m_palette.append(QPair<QString, QString>("Purple9", "#4A148C"));
  121. m_palette.append(QPair<QString, QString>("Red0", "#FFEBEE"));
  122. m_palette.append(QPair<QString, QString>("Red1", "#FFCDD2"));
  123. m_palette.append(QPair<QString, QString>("Red2", "#EF9A9A"));
  124. m_palette.append(QPair<QString, QString>("Red3", "#E57373"));
  125. m_palette.append(QPair<QString, QString>("Red4", "#EF5350"));
  126. m_palette.append(QPair<QString, QString>("Red5", "#F44336"));
  127. m_palette.append(QPair<QString, QString>("Red6", "#E53935"));
  128. m_palette.append(QPair<QString, QString>("Red7", "#D32F2F"));
  129. m_palette.append(QPair<QString, QString>("Red8", "#C62828"));
  130. m_palette.append(QPair<QString, QString>("Red9", "#B71C1C"));
  131. }
  132. QString VNote::getColorFromPalette(const QString &p_name) const
  133. {
  134. for (int i = 0; i < m_palette.size(); ++i) {
  135. if (m_palette[i].first == p_name) {
  136. return m_palette[i].second;
  137. }
  138. }
  139. return "White";
  140. }
  141. void VNote::initTemplate()
  142. {
  143. if (s_markdownTemplate.isEmpty()) {
  144. updateTemplate();
  145. }
  146. }
  147. void VNote::updateTemplate()
  148. {
  149. const QString c_markdownTemplatePath(":/resources/markdown_template.html");
  150. // Get background color
  151. QString rgb;
  152. const QString &curRenderBg = g_config->getCurRenderBackgroundColor();
  153. const QVector<VColor> &predefinedColors = g_config->getPredefinedColors();
  154. if (curRenderBg != "System") {
  155. for (int i = 0; i < predefinedColors.size(); ++i) {
  156. if (predefinedColors[i].name == curRenderBg) {
  157. rgb = predefinedColors[i].rgb;
  158. break;
  159. }
  160. }
  161. }
  162. QString cssStyle;
  163. if (!rgb.isEmpty()) {
  164. cssStyle += "body { background-color: #" + rgb + " !important; }\n";
  165. }
  166. if (g_config->getEnableImageConstraint()) {
  167. // Constain the image width.
  168. cssStyle += "img { max-width: 100% !important; height: auto !important; }\n";
  169. }
  170. const QString styleHolder("<!-- BACKGROUND_PLACE_HOLDER -->");
  171. const QString cssHolder("CSS_PLACE_HOLDER");
  172. const QString codeBlockCssHolder("HIGHLIGHTJS_CSS_PLACE_HOLDER");
  173. s_markdownTemplate = VUtils::readFileFromDisk(c_markdownTemplatePath);
  174. // Must replace the code block holder first.
  175. s_markdownTemplate.replace(codeBlockCssHolder, g_config->getTemplateCodeBlockCssUrl());
  176. s_markdownTemplate.replace(cssHolder, g_config->getTemplateCssUrl());
  177. s_markdownTemplatePDF = s_markdownTemplate;
  178. if (!cssStyle.isEmpty()) {
  179. s_markdownTemplate.replace(styleHolder, cssStyle);
  180. }
  181. // Shoudl not display scrollbar in PDF.
  182. cssStyle += "pre code { white-space: pre-wrap !important; "
  183. "word-break: break-all !important; }\n";
  184. if (!g_config->getEnableImageConstraint()) {
  185. // Constain the image width by force in PDF, otherwise, the PDF will
  186. // be cut off.
  187. cssStyle += "img { max-width: 100% !important; height: auto !important; }\n";
  188. }
  189. s_markdownTemplatePDF.replace(styleHolder, cssStyle);
  190. }
  191. const QVector<VNotebook *> &VNote::getNotebooks() const
  192. {
  193. return m_notebooks;
  194. }
  195. QVector<VNotebook *> &VNote::getNotebooks()
  196. {
  197. return m_notebooks;
  198. }
  199. QString VNote::getNavigationLabelStyle(const QString &p_str) const
  200. {
  201. static int lastLen = -1;
  202. static int pxWidth = 24;
  203. int fontPt = 15;
  204. QString fontFamily = getMonospacedFont();
  205. if (p_str.size() != lastLen) {
  206. QFont font(fontFamily, fontPt);
  207. font.setBold(true);
  208. QFontMetrics fm(font);
  209. pxWidth = fm.width(p_str);
  210. lastLen = p_str.size();
  211. }
  212. return QString("background-color: %1;"
  213. "color: %2;"
  214. "font-size: %3pt;"
  215. "font: bold;"
  216. "font-family: %4;"
  217. "border-radius: 3px;"
  218. "min-width: %5px;"
  219. "max-width: %5px;")
  220. .arg(getColorFromPalette("logo-base"))
  221. .arg(getColorFromPalette("logo-max"))
  222. .arg(fontPt)
  223. .arg(fontFamily)
  224. .arg(pxWidth);
  225. }
  226. const QString &VNote::getMonospacedFont() const
  227. {
  228. static QString font;
  229. if (font.isNull()) {
  230. QStringList candidates;
  231. candidates << "Consolas" << "Monaco" << "Andale Mono" << "Monospace" << "Courier New";
  232. QStringList availFamilies = QFontDatabase().families();
  233. for (int i = 0; i < candidates.size(); ++i) {
  234. QString family = candidates[i].trimmed().toLower();
  235. for (int j = 0; j < availFamilies.size(); ++j) {
  236. QString availFamily = availFamilies[j];
  237. availFamily.remove(QRegExp("\\[.*\\]"));
  238. if (family == availFamily.trimmed().toLower()) {
  239. font = availFamily;
  240. return font;
  241. }
  242. }
  243. }
  244. // Fallback to current font.
  245. font = QFont().family();
  246. }
  247. return font;
  248. }
  249. VOrphanFile *VNote::getOrphanFile(const QString &p_path, bool p_modifiable, bool p_systemFile)
  250. {
  251. if (p_path.isEmpty()) {
  252. return NULL;
  253. }
  254. QString path = QDir::cleanPath(p_path);
  255. // See if the file has already been opened before.
  256. for (auto const &file : m_externalFiles) {
  257. if (VUtils::equalPath(QDir::cleanPath(file->fetchPath()), path)) {
  258. Q_ASSERT(file->isModifiable() == p_modifiable);
  259. Q_ASSERT(file->isSystemFile() == p_systemFile);
  260. return file;
  261. }
  262. }
  263. for (int i = 0; i < m_externalFiles.size(); ++i) {
  264. VOrphanFile *file = m_externalFiles[i];
  265. if (!file->isOpened()) {
  266. qDebug() << "release orphan file" << file;
  267. m_externalFiles.removeAt(i);
  268. delete file;
  269. --i;
  270. }
  271. }
  272. // Create a VOrphanFile for path.
  273. VOrphanFile *file = new VOrphanFile(this, path, p_modifiable, p_systemFile);
  274. m_externalFiles.append(file);
  275. return file;
  276. }
  277. VNoteFile *VNote::getInternalFile(const QString &p_path)
  278. {
  279. VNoteFile *file = NULL;
  280. for (auto & nb : m_notebooks) {
  281. file = nb->tryLoadFile(p_path);
  282. if (file) {
  283. break;
  284. }
  285. }
  286. return file;
  287. }
  288. VFile *VNote::getFile(const QString &p_path)
  289. {
  290. VFile *file = getInternalFile(p_path);
  291. if (!file) {
  292. file = getOrphanFile(p_path, true, false);
  293. }
  294. return file;
  295. }
  296. VDirectory *VNote::getInternalDirectory(const QString &p_path)
  297. {
  298. VDirectory *dir = NULL;
  299. for (auto & nb : m_notebooks) {
  300. dir = nb->tryLoadDirectory(p_path);
  301. if (dir) {
  302. break;
  303. }
  304. }
  305. return dir;
  306. }