novelWriter 2.6.3__py3-none-any.whl → 2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. novelwriter/__init__.py +98 -75
  2. novelwriter/assets/i18n/nw_cs_CZ.qm +0 -0
  3. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  4. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  5. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  6. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  7. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  8. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  9. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  10. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  11. novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
  12. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  13. novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
  14. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  15. novelwriter/assets/i18n/project_de_DE.json +3 -1
  16. novelwriter/assets/i18n/project_en_GB.json +2 -0
  17. novelwriter/assets/i18n/project_en_US.json +2 -0
  18. novelwriter/assets/i18n/project_it_IT.json +2 -0
  19. novelwriter/assets/i18n/project_ja_JP.json +2 -0
  20. novelwriter/assets/i18n/project_nb_NO.json +2 -0
  21. novelwriter/assets/i18n/project_nn_NO.json +5 -0
  22. novelwriter/assets/i18n/project_pl_PL.json +2 -0
  23. novelwriter/assets/i18n/project_pt_BR.json +2 -0
  24. novelwriter/assets/i18n/project_ru_RU.json +2 -0
  25. novelwriter/assets/i18n/project_zh_CN.json +2 -0
  26. novelwriter/assets/icons/font_awesome.icons +109 -0
  27. novelwriter/assets/icons/material_filled_bold.icons +109 -0
  28. novelwriter/assets/icons/material_filled_normal.icons +109 -0
  29. novelwriter/assets/icons/material_filled_thin.icons +109 -0
  30. novelwriter/assets/icons/material_rounded_bold.icons +109 -0
  31. novelwriter/assets/icons/material_rounded_normal.icons +109 -0
  32. novelwriter/assets/icons/material_rounded_thin.icons +109 -0
  33. novelwriter/assets/icons/remix_filled.icons +109 -0
  34. novelwriter/assets/icons/remix_outline.icons +109 -0
  35. novelwriter/assets/images/splash.png +0 -0
  36. novelwriter/assets/manual.pdf +0 -0
  37. novelwriter/assets/{manual_fr_FR.pdf → manual_fr.pdf} +0 -0
  38. novelwriter/assets/sample.zip +0 -0
  39. novelwriter/assets/syntax/cyberpunk_night.conf +1 -1
  40. novelwriter/assets/syntax/snazzy.conf +3 -3
  41. novelwriter/assets/text/credits_en.htm +12 -6
  42. novelwriter/assets/themes/cyberpunk_night.conf +23 -7
  43. novelwriter/assets/themes/default_dark.conf +20 -4
  44. novelwriter/assets/themes/default_light.conf +21 -5
  45. novelwriter/assets/themes/dracula.conf +20 -4
  46. novelwriter/assets/themes/snazzy.conf +48 -0
  47. novelwriter/assets/themes/solarized_dark.conf +24 -8
  48. novelwriter/assets/themes/solarized_light.conf +22 -6
  49. novelwriter/common.py +43 -27
  50. novelwriter/config.py +201 -139
  51. novelwriter/constants.py +67 -36
  52. novelwriter/core/buildsettings.py +26 -17
  53. novelwriter/core/coretools.py +52 -41
  54. novelwriter/core/docbuild.py +20 -13
  55. novelwriter/core/document.py +2 -2
  56. novelwriter/core/index.py +166 -432
  57. novelwriter/core/indexdata.py +406 -0
  58. novelwriter/core/item.py +50 -32
  59. novelwriter/core/itemmodel.py +43 -38
  60. novelwriter/core/novelmodel.py +225 -0
  61. novelwriter/core/options.py +1 -1
  62. novelwriter/core/project.py +24 -25
  63. novelwriter/core/projectdata.py +47 -29
  64. novelwriter/core/projectxml.py +18 -8
  65. novelwriter/core/sessions.py +32 -15
  66. novelwriter/core/spellcheck.py +4 -3
  67. novelwriter/core/status.py +12 -15
  68. novelwriter/core/storage.py +1 -1
  69. novelwriter/core/tree.py +55 -13
  70. novelwriter/dialogs/about.py +19 -22
  71. novelwriter/dialogs/docmerge.py +23 -24
  72. novelwriter/dialogs/docsplit.py +26 -26
  73. novelwriter/dialogs/editlabel.py +19 -20
  74. novelwriter/dialogs/preferences.py +143 -69
  75. novelwriter/dialogs/projectsettings.py +51 -54
  76. novelwriter/dialogs/quotes.py +14 -19
  77. novelwriter/dialogs/wordlist.py +25 -30
  78. novelwriter/enum.py +8 -0
  79. novelwriter/error.py +16 -16
  80. novelwriter/extensions/configlayout.py +24 -20
  81. novelwriter/extensions/eventfilters.py +9 -5
  82. novelwriter/extensions/modified.py +34 -15
  83. novelwriter/extensions/novelselector.py +18 -5
  84. novelwriter/extensions/pagedsidebar.py +39 -49
  85. novelwriter/extensions/progressbars.py +10 -8
  86. novelwriter/extensions/statusled.py +6 -13
  87. novelwriter/extensions/switch.py +31 -41
  88. novelwriter/extensions/switchbox.py +8 -3
  89. novelwriter/extensions/versioninfo.py +4 -4
  90. novelwriter/formats/shared.py +1 -1
  91. novelwriter/formats/todocx.py +14 -10
  92. novelwriter/formats/tohtml.py +7 -5
  93. novelwriter/formats/tokenizer.py +37 -33
  94. novelwriter/formats/tomarkdown.py +6 -2
  95. novelwriter/formats/toodt.py +27 -22
  96. novelwriter/formats/toqdoc.py +19 -14
  97. novelwriter/formats/toraw.py +6 -2
  98. novelwriter/gui/doceditor.py +314 -305
  99. novelwriter/gui/dochighlight.py +46 -45
  100. novelwriter/gui/docviewer.py +102 -104
  101. novelwriter/gui/docviewerpanel.py +47 -51
  102. novelwriter/gui/editordocument.py +8 -5
  103. novelwriter/gui/itemdetails.py +11 -14
  104. novelwriter/gui/mainmenu.py +146 -145
  105. novelwriter/gui/noveltree.py +246 -406
  106. novelwriter/gui/outline.py +141 -72
  107. novelwriter/gui/projtree.py +139 -132
  108. novelwriter/gui/search.py +34 -31
  109. novelwriter/gui/sidebar.py +13 -18
  110. novelwriter/gui/statusbar.py +27 -21
  111. novelwriter/gui/theme.py +554 -436
  112. novelwriter/guimain.py +56 -38
  113. novelwriter/shared.py +32 -23
  114. novelwriter/splash.py +74 -0
  115. novelwriter/text/comments.py +70 -0
  116. novelwriter/text/patterns.py +4 -4
  117. novelwriter/tools/dictionaries.py +20 -29
  118. novelwriter/tools/lipsum.py +18 -18
  119. novelwriter/tools/manusbuild.py +39 -42
  120. novelwriter/tools/manuscript.py +102 -115
  121. novelwriter/tools/manussettings.py +129 -102
  122. novelwriter/tools/noveldetails.py +60 -72
  123. novelwriter/tools/welcome.py +57 -75
  124. novelwriter/tools/writingstats.py +112 -102
  125. novelwriter/types.py +5 -7
  126. {novelWriter-2.6.3.dist-info → novelwriter-2.7.dist-info}/METADATA +6 -6
  127. novelwriter-2.7.dist-info/RECORD +162 -0
  128. {novelWriter-2.6.3.dist-info → novelwriter-2.7.dist-info}/WHEEL +1 -1
  129. novelWriter-2.6.3.dist-info/RECORD +0 -363
  130. novelwriter/assets/icons/typicons_dark/README.md +0 -29
  131. novelwriter/assets/icons/typicons_dark/icons.conf +0 -134
  132. novelwriter/assets/icons/typicons_dark/mixed_copy.svg +0 -4
  133. novelwriter/assets/icons/typicons_dark/mixed_document-chapter.svg +0 -12
  134. novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +0 -6
  135. novelwriter/assets/icons/typicons_dark/mixed_document-note.svg +0 -12
  136. novelwriter/assets/icons/typicons_dark/mixed_document-scene.svg +0 -12
  137. novelwriter/assets/icons/typicons_dark/mixed_document-section.svg +0 -12
  138. novelwriter/assets/icons/typicons_dark/mixed_document-title.svg +0 -12
  139. novelwriter/assets/icons/typicons_dark/mixed_edit.svg +0 -4
  140. novelwriter/assets/icons/typicons_dark/mixed_import.svg +0 -5
  141. novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg +0 -5
  142. novelwriter/assets/icons/typicons_dark/mixed_input-none.svg +0 -5
  143. novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg +0 -5
  144. novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +0 -6
  145. novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +0 -6
  146. novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +0 -6
  147. novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +0 -6
  148. novelwriter/assets/icons/typicons_dark/mixed_search-replace.svg +0 -6
  149. novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +0 -6
  150. novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +0 -6
  151. novelwriter/assets/icons/typicons_dark/nw_deco-h0.svg +0 -4
  152. novelwriter/assets/icons/typicons_dark/nw_deco-h1.svg +0 -4
  153. novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +0 -4
  154. novelwriter/assets/icons/typicons_dark/nw_deco-h2.svg +0 -4
  155. novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +0 -4
  156. novelwriter/assets/icons/typicons_dark/nw_deco-h3.svg +0 -4
  157. novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +0 -4
  158. novelwriter/assets/icons/typicons_dark/nw_deco-h4.svg +0 -4
  159. novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +0 -4
  160. novelwriter/assets/icons/typicons_dark/nw_deco-noveltree-more.svg +0 -4
  161. novelwriter/assets/icons/typicons_dark/nw_font.svg +0 -4
  162. novelwriter/assets/icons/typicons_dark/nw_panel.svg +0 -4
  163. novelwriter/assets/icons/typicons_dark/nw_quote.svg +0 -4
  164. novelwriter/assets/icons/typicons_dark/nw_search-case.svg +0 -4
  165. novelwriter/assets/icons/typicons_dark/nw_search-preserve.svg +0 -4
  166. novelwriter/assets/icons/typicons_dark/nw_search-regex.svg +0 -4
  167. novelwriter/assets/icons/typicons_dark/nw_search-word.svg +0 -4
  168. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +0 -4
  169. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +0 -6
  170. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +0 -4
  171. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +0 -6
  172. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +0 -7
  173. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +0 -4
  174. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +0 -6
  175. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +0 -7
  176. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +0 -7
  177. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +0 -7
  178. novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +0 -5
  179. novelwriter/assets/icons/typicons_dark/typ_arrow-down-thick-grey.svg +0 -4
  180. novelwriter/assets/icons/typicons_dark/typ_arrow-forward.svg +0 -4
  181. novelwriter/assets/icons/typicons_dark/typ_arrow-maximise.svg +0 -4
  182. novelwriter/assets/icons/typicons_dark/typ_arrow-minimise.svg +0 -4
  183. novelwriter/assets/icons/typicons_dark/typ_arrow-repeat-grey.svg +0 -4
  184. novelwriter/assets/icons/typicons_dark/typ_book-grey.svg +0 -4
  185. novelwriter/assets/icons/typicons_dark/typ_book.svg +0 -6
  186. novelwriter/assets/icons/typicons_dark/typ_bookmark.svg +0 -4
  187. novelwriter/assets/icons/typicons_dark/typ_calendar.svg +0 -4
  188. novelwriter/assets/icons/typicons_dark/typ_cancel-grey.svg +0 -4
  189. novelwriter/assets/icons/typicons_dark/typ_cancel.svg +0 -4
  190. novelwriter/assets/icons/typicons_dark/typ_chart-bar-grey.svg +0 -4
  191. novelwriter/assets/icons/typicons_dark/typ_chevron-down.svg +0 -4
  192. novelwriter/assets/icons/typicons_dark/typ_chevron-left.svg +0 -4
  193. novelwriter/assets/icons/typicons_dark/typ_chevron-right.svg +0 -4
  194. novelwriter/assets/icons/typicons_dark/typ_chevron-up.svg +0 -4
  195. novelwriter/assets/icons/typicons_dark/typ_cog.svg +0 -4
  196. novelwriter/assets/icons/typicons_dark/typ_delete-full.svg +0 -4
  197. novelwriter/assets/icons/typicons_dark/typ_delete.svg +0 -4
  198. novelwriter/assets/icons/typicons_dark/typ_directions-full.svg +0 -4
  199. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +0 -4
  200. novelwriter/assets/icons/typicons_dark/typ_document-text.svg +0 -8
  201. novelwriter/assets/icons/typicons_dark/typ_document.svg +0 -4
  202. novelwriter/assets/icons/typicons_dark/typ_export-grey.svg +0 -4
  203. novelwriter/assets/icons/typicons_dark/typ_export.svg +0 -4
  204. novelwriter/assets/icons/typicons_dark/typ_eye.svg +0 -4
  205. novelwriter/assets/icons/typicons_dark/typ_flag.svg +0 -4
  206. novelwriter/assets/icons/typicons_dark/typ_folder-open.svg +0 -4
  207. novelwriter/assets/icons/typicons_dark/typ_folder.svg +0 -5
  208. novelwriter/assets/icons/typicons_dark/typ_globe-grey.svg +0 -4
  209. novelwriter/assets/icons/typicons_dark/typ_key.svg +0 -4
  210. novelwriter/assets/icons/typicons_dark/typ_lightbulb-full.svg +0 -4
  211. novelwriter/assets/icons/typicons_dark/typ_location.svg +0 -4
  212. novelwriter/assets/icons/typicons_dark/typ_media-pause-grey.svg +0 -4
  213. novelwriter/assets/icons/typicons_dark/typ_media-record-outline.svg +0 -4
  214. novelwriter/assets/icons/typicons_dark/typ_media-record.svg +0 -4
  215. novelwriter/assets/icons/typicons_dark/typ_minus.svg +0 -4
  216. novelwriter/assets/icons/typicons_dark/typ_pencil.svg +0 -5
  217. novelwriter/assets/icons/typicons_dark/typ_pin-outline.svg +0 -4
  218. novelwriter/assets/icons/typicons_dark/typ_pin.svg +0 -4
  219. novelwriter/assets/icons/typicons_dark/typ_plus.svg +0 -4
  220. novelwriter/assets/icons/typicons_dark/typ_puzzle-outline.svg +0 -4
  221. novelwriter/assets/icons/typicons_dark/typ_puzzle.svg +0 -4
  222. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +0 -4
  223. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +0 -4
  224. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +0 -4
  225. novelwriter/assets/icons/typicons_dark/typ_search.svg +0 -4
  226. novelwriter/assets/icons/typicons_dark/typ_star.svg +0 -4
  227. novelwriter/assets/icons/typicons_dark/typ_stopwatch-grey.svg +0 -4
  228. novelwriter/assets/icons/typicons_dark/typ_th-dot-menu.svg +0 -4
  229. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +0 -4
  230. novelwriter/assets/icons/typicons_dark/typ_th-list-grey.svg +0 -4
  231. novelwriter/assets/icons/typicons_dark/typ_th-list.svg +0 -9
  232. novelwriter/assets/icons/typicons_dark/typ_times.svg +0 -4
  233. novelwriter/assets/icons/typicons_dark/typ_trash.svg +0 -5
  234. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +0 -4
  235. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +0 -4
  236. novelwriter/assets/icons/typicons_dark/typ_user.svg +0 -5
  237. novelwriter/assets/icons/typicons_dark/typ_warning-full.svg +0 -4
  238. novelwriter/assets/icons/typicons_light/README.md +0 -29
  239. novelwriter/assets/icons/typicons_light/icons.conf +0 -134
  240. novelwriter/assets/icons/typicons_light/mixed_copy.svg +0 -4
  241. novelwriter/assets/icons/typicons_light/mixed_document-chapter.svg +0 -12
  242. novelwriter/assets/icons/typicons_light/mixed_document-new.svg +0 -6
  243. novelwriter/assets/icons/typicons_light/mixed_document-note.svg +0 -12
  244. novelwriter/assets/icons/typicons_light/mixed_document-scene.svg +0 -12
  245. novelwriter/assets/icons/typicons_light/mixed_document-section.svg +0 -12
  246. novelwriter/assets/icons/typicons_light/mixed_document-title.svg +0 -12
  247. novelwriter/assets/icons/typicons_light/mixed_edit.svg +0 -4
  248. novelwriter/assets/icons/typicons_light/mixed_import.svg +0 -5
  249. novelwriter/assets/icons/typicons_light/mixed_input-checked.svg +0 -5
  250. novelwriter/assets/icons/typicons_light/mixed_input-none.svg +0 -5
  251. novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg +0 -5
  252. novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +0 -6
  253. novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +0 -6
  254. novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +0 -6
  255. novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +0 -6
  256. novelwriter/assets/icons/typicons_light/mixed_search-replace.svg +0 -6
  257. novelwriter/assets/icons/typicons_light/mixed_size-height.svg +0 -6
  258. novelwriter/assets/icons/typicons_light/mixed_size-width.svg +0 -6
  259. novelwriter/assets/icons/typicons_light/nw_deco-h0.svg +0 -4
  260. novelwriter/assets/icons/typicons_light/nw_deco-h1.svg +0 -4
  261. novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +0 -4
  262. novelwriter/assets/icons/typicons_light/nw_deco-h2.svg +0 -4
  263. novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +0 -4
  264. novelwriter/assets/icons/typicons_light/nw_deco-h3.svg +0 -4
  265. novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +0 -4
  266. novelwriter/assets/icons/typicons_light/nw_deco-h4.svg +0 -4
  267. novelwriter/assets/icons/typicons_light/nw_deco-note.svg +0 -4
  268. novelwriter/assets/icons/typicons_light/nw_deco-noveltree-more.svg +0 -4
  269. novelwriter/assets/icons/typicons_light/nw_font.svg +0 -4
  270. novelwriter/assets/icons/typicons_light/nw_panel.svg +0 -4
  271. novelwriter/assets/icons/typicons_light/nw_quote.svg +0 -4
  272. novelwriter/assets/icons/typicons_light/nw_search-case.svg +0 -4
  273. novelwriter/assets/icons/typicons_light/nw_search-preserve.svg +0 -4
  274. novelwriter/assets/icons/typicons_light/nw_search-regex.svg +0 -4
  275. novelwriter/assets/icons/typicons_light/nw_search-word.svg +0 -4
  276. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +0 -4
  277. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +0 -6
  278. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +0 -4
  279. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +0 -6
  280. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +0 -7
  281. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +0 -4
  282. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +0 -6
  283. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +0 -7
  284. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +0 -7
  285. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +0 -7
  286. novelwriter/assets/icons/typicons_light/nw_toolbar.svg +0 -5
  287. novelwriter/assets/icons/typicons_light/typ_arrow-down-thick-grey.svg +0 -4
  288. novelwriter/assets/icons/typicons_light/typ_arrow-forward.svg +0 -4
  289. novelwriter/assets/icons/typicons_light/typ_arrow-maximise.svg +0 -4
  290. novelwriter/assets/icons/typicons_light/typ_arrow-minimise.svg +0 -4
  291. novelwriter/assets/icons/typicons_light/typ_arrow-repeat-grey.svg +0 -4
  292. novelwriter/assets/icons/typicons_light/typ_book-grey.svg +0 -4
  293. novelwriter/assets/icons/typicons_light/typ_book.svg +0 -6
  294. novelwriter/assets/icons/typicons_light/typ_bookmark.svg +0 -4
  295. novelwriter/assets/icons/typicons_light/typ_calendar.svg +0 -4
  296. novelwriter/assets/icons/typicons_light/typ_cancel-grey.svg +0 -4
  297. novelwriter/assets/icons/typicons_light/typ_cancel.svg +0 -4
  298. novelwriter/assets/icons/typicons_light/typ_chart-bar-grey.svg +0 -4
  299. novelwriter/assets/icons/typicons_light/typ_chevron-down.svg +0 -4
  300. novelwriter/assets/icons/typicons_light/typ_chevron-left.svg +0 -4
  301. novelwriter/assets/icons/typicons_light/typ_chevron-right.svg +0 -4
  302. novelwriter/assets/icons/typicons_light/typ_chevron-up.svg +0 -4
  303. novelwriter/assets/icons/typicons_light/typ_cog.svg +0 -4
  304. novelwriter/assets/icons/typicons_light/typ_delete-full.svg +0 -4
  305. novelwriter/assets/icons/typicons_light/typ_delete.svg +0 -4
  306. novelwriter/assets/icons/typicons_light/typ_directions-full.svg +0 -4
  307. novelwriter/assets/icons/typicons_light/typ_document-add.svg +0 -4
  308. novelwriter/assets/icons/typicons_light/typ_document-text.svg +0 -5
  309. novelwriter/assets/icons/typicons_light/typ_document.svg +0 -4
  310. novelwriter/assets/icons/typicons_light/typ_export-grey.svg +0 -4
  311. novelwriter/assets/icons/typicons_light/typ_export.svg +0 -4
  312. novelwriter/assets/icons/typicons_light/typ_eye.svg +0 -4
  313. novelwriter/assets/icons/typicons_light/typ_flag.svg +0 -4
  314. novelwriter/assets/icons/typicons_light/typ_folder-open.svg +0 -4
  315. novelwriter/assets/icons/typicons_light/typ_folder.svg +0 -5
  316. novelwriter/assets/icons/typicons_light/typ_globe-grey.svg +0 -4
  317. novelwriter/assets/icons/typicons_light/typ_key.svg +0 -4
  318. novelwriter/assets/icons/typicons_light/typ_lightbulb-full.svg +0 -4
  319. novelwriter/assets/icons/typicons_light/typ_location.svg +0 -4
  320. novelwriter/assets/icons/typicons_light/typ_media-pause-grey.svg +0 -4
  321. novelwriter/assets/icons/typicons_light/typ_media-record-outline.svg +0 -4
  322. novelwriter/assets/icons/typicons_light/typ_media-record.svg +0 -4
  323. novelwriter/assets/icons/typicons_light/typ_minus.svg +0 -4
  324. novelwriter/assets/icons/typicons_light/typ_pencil.svg +0 -5
  325. novelwriter/assets/icons/typicons_light/typ_pin-outline.svg +0 -4
  326. novelwriter/assets/icons/typicons_light/typ_pin.svg +0 -4
  327. novelwriter/assets/icons/typicons_light/typ_plus.svg +0 -4
  328. novelwriter/assets/icons/typicons_light/typ_puzzle-outline.svg +0 -4
  329. novelwriter/assets/icons/typicons_light/typ_puzzle.svg +0 -4
  330. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +0 -4
  331. novelwriter/assets/icons/typicons_light/typ_refresh.svg +0 -4
  332. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +0 -4
  333. novelwriter/assets/icons/typicons_light/typ_search.svg +0 -4
  334. novelwriter/assets/icons/typicons_light/typ_star.svg +0 -4
  335. novelwriter/assets/icons/typicons_light/typ_stopwatch-grey.svg +0 -4
  336. novelwriter/assets/icons/typicons_light/typ_th-dot-menu.svg +0 -4
  337. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +0 -4
  338. novelwriter/assets/icons/typicons_light/typ_th-list-grey.svg +0 -4
  339. novelwriter/assets/icons/typicons_light/typ_th-list.svg +0 -9
  340. novelwriter/assets/icons/typicons_light/typ_times.svg +0 -4
  341. novelwriter/assets/icons/typicons_light/typ_trash.svg +0 -5
  342. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +0 -4
  343. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +0 -4
  344. novelwriter/assets/icons/typicons_light/typ_user.svg +0 -5
  345. novelwriter/assets/icons/typicons_light/typ_warning-full.svg +0 -4
  346. {novelWriter-2.6.3.dist-info → novelwriter-2.7.dist-info}/entry_points.txt +0 -0
  347. {novelWriter-2.6.3.dist-info → novelwriter-2.7.dist-info/licenses}/LICENSE.md +0 -0
  348. {novelWriter-2.6.3.dist-info → novelwriter-2.7.dist-info}/top_level.txt +0 -0
novelwriter/constants.py CHANGED
@@ -23,7 +23,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
23
23
  """
24
24
  from __future__ import annotations
25
25
 
26
- from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
26
+ from typing import Final
27
+
28
+ from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
27
29
 
28
30
  from novelwriter.enum import (
29
31
  nwBuildFmt, nwComment, nwItemClass, nwItemLayout, nwOutline, nwStatusShape
@@ -99,7 +101,7 @@ class nwShortcode:
99
101
  FOOTNOTE_B = "[footnote:"
100
102
  FIELD_B = "[field:"
101
103
 
102
- COMMENT_STYLES = {
104
+ COMMENT_STYLES: Final[dict[nwComment, str]] = {
103
105
  nwComment.FOOTNOTE: "[footnote:{0}]",
104
106
  nwComment.COMMENT: "[comment:{0}]",
105
107
  }
@@ -110,13 +112,13 @@ class nwShortcode:
110
112
  class nwStyles:
111
113
 
112
114
  H_VALID = ("H0", "H1", "H2", "H3", "H4")
113
- H_LEVEL = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
114
- H_SIZES = {0: 2.50, 1: 2.00, 2: 1.75, 3: 1.50, 4: 1.25}
115
+ H_LEVEL: Final[dict[str, int]] = {"H0": 0, "H1": 1, "H2": 2, "H3": 3, "H4": 4}
116
+ H_SIZES: Final[dict[int, float]] = {0: 2.50, 1: 2.00, 2: 1.75, 3: 1.50, 4: 1.25}
115
117
 
116
118
  T_NORMAL = 1.0
117
119
  T_SMALL = 0.8
118
120
 
119
- T_LABEL = {
121
+ T_LABEL: Final[dict[str, str]] = {
120
122
  "H0": QT_TRANSLATE_NOOP("Constant", "Title"),
121
123
  "H1": QT_TRANSLATE_NOOP("Constant", "Heading 1 (Partition)"),
122
124
  "H2": QT_TRANSLATE_NOOP("Constant", "Heading 2 (Chapter)"),
@@ -125,7 +127,7 @@ class nwStyles:
125
127
  "TT": QT_TRANSLATE_NOOP("Constant", "Text Paragraph"),
126
128
  "SP": QT_TRANSLATE_NOOP("Constant", "Scene Separator"),
127
129
  }
128
- T_MARGIN = {
130
+ T_MARGIN: Final[dict[str, tuple[float, float]]] = {
129
131
  "H0": (1.50, 0.60), # Title margins (top, bottom)
130
132
  "H1": (1.50, 0.60), # Heading 1 margins (top, bottom)
131
133
  "H2": (1.50, 0.60), # Heading 2 margins (top, bottom)
@@ -174,20 +176,20 @@ class nwKeyWords:
174
176
  MENTION_KEY = "@mention"
175
177
 
176
178
  # Note: The order here affects the order of menu entries
177
- ALL_KEYS = [
179
+ ALL_KEYS: Final[list[str]] = [
178
180
  TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
179
181
  OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY,
180
182
  ]
181
- CAN_CREATE = [
183
+ CAN_CREATE: Final[list[str]] = [
182
184
  POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
183
185
  OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY,
184
186
  ]
185
187
 
186
188
  # Set of Valid Keys
187
- VALID_KEYS = set(ALL_KEYS)
189
+ VALID_KEYS: Final[set[str]] = set(ALL_KEYS)
188
190
 
189
191
  # Map from Keys to Item Class
190
- KEY_CLASS = {
192
+ KEY_CLASS: Final[dict[str, nwItemClass]] = {
191
193
  POV_KEY: nwItemClass.CHARACTER,
192
194
  FOCUS_KEY: nwItemClass.CHARACTER,
193
195
  CHAR_KEY: nwItemClass.CHARACTER,
@@ -203,7 +205,7 @@ class nwKeyWords:
203
205
 
204
206
  class nwLists:
205
207
 
206
- USER_CLASSES = [
208
+ USER_CLASSES: Final[list[nwItemClass]] = [
207
209
  nwItemClass.CHARACTER,
208
210
  nwItemClass.PLOT,
209
211
  nwItemClass.WORLD,
@@ -229,7 +231,7 @@ class nwStats:
229
231
  WORDS_TITLE = "titleWords"
230
232
 
231
233
  # Note: The order here affects the order of menu entries
232
- ALL_FIELDS = [
234
+ ALL_FIELDS: Final[list[str]] = [
233
235
  WORDS, WORDS_TEXT, WORDS_TITLE,
234
236
  CHARS, CHARS_TEXT, CHARS_TITLE,
235
237
  WCHARS_ALL, WCHARS_TEXT, WCHARS_TITLE,
@@ -239,7 +241,7 @@ class nwStats:
239
241
 
240
242
  class nwLabels:
241
243
 
242
- CLASS_NAME = {
244
+ CLASS_NAME: Final[dict[nwItemClass, str]] = {
243
245
  nwItemClass.NO_CLASS: QT_TRANSLATE_NOOP("Constant", "None"),
244
246
  nwItemClass.NOVEL: QT_TRANSLATE_NOOP("Constant", "Novel"),
245
247
  nwItemClass.PLOT: QT_TRANSLATE_NOOP("Constant", "Plot"),
@@ -253,7 +255,7 @@ class nwLabels:
253
255
  nwItemClass.TEMPLATE: QT_TRANSLATE_NOOP("Constant", "Templates"),
254
256
  nwItemClass.TRASH: QT_TRANSLATE_NOOP("Constant", "Trash"),
255
257
  }
256
- CLASS_ICON = {
258
+ CLASS_ICON: Final[dict[nwItemClass, str]] = {
257
259
  nwItemClass.NO_CLASS: "cls_none",
258
260
  nwItemClass.NOVEL: "cls_novel",
259
261
  nwItemClass.PLOT: "cls_plot",
@@ -267,12 +269,12 @@ class nwLabels:
267
269
  nwItemClass.TEMPLATE: "cls_template",
268
270
  nwItemClass.TRASH: "cls_trash",
269
271
  }
270
- LAYOUT_NAME = {
272
+ LAYOUT_NAME: Final[dict[nwItemLayout, str]] = {
271
273
  nwItemLayout.NO_LAYOUT: QT_TRANSLATE_NOOP("Constant", "None"),
272
274
  nwItemLayout.DOCUMENT: QT_TRANSLATE_NOOP("Constant", "Novel Document"),
273
275
  nwItemLayout.NOTE: QT_TRANSLATE_NOOP("Constant", "Project Note"),
274
276
  }
275
- ITEM_DESCRIPTION = {
277
+ ITEM_DESCRIPTION: Final[dict[str, str]] = {
276
278
  "none": QT_TRANSLATE_NOOP("Constant", "None"),
277
279
  "root": QT_TRANSLATE_NOOP("Constant", "Root Folder"),
278
280
  "folder": QT_TRANSLATE_NOOP("Constant", "Folder"),
@@ -283,11 +285,11 @@ class nwLabels:
283
285
  "doc_h4": QT_TRANSLATE_NOOP("Constant", "Novel Section"),
284
286
  "note": QT_TRANSLATE_NOOP("Constant", "Project Note"),
285
287
  }
286
- ACTIVE_NAME = {
288
+ ACTIVE_NAME: Final[dict[str, str]] = {
287
289
  "checked": QT_TRANSLATE_NOOP("Constant", "Active"),
288
290
  "unchecked": QT_TRANSLATE_NOOP("Constant", "Inactive"),
289
291
  }
290
- KEY_NAME = {
292
+ KEY_NAME: Final[dict[str, str]] = {
291
293
  nwKeyWords.TAG_KEY: QT_TRANSLATE_NOOP("Constant", "Tag"),
292
294
  nwKeyWords.POV_KEY: QT_TRANSLATE_NOOP("Constant", "Point of View"),
293
295
  nwKeyWords.FOCUS_KEY: QT_TRANSLATE_NOOP("Constant", "Focus"),
@@ -301,7 +303,7 @@ class nwLabels:
301
303
  nwKeyWords.STORY_KEY: QT_TRANSLATE_NOOP("Constant", "Story"),
302
304
  nwKeyWords.MENTION_KEY: QT_TRANSLATE_NOOP("Constant", "Mentions"),
303
305
  }
304
- KEY_SHORTCUT = {
306
+ KEY_SHORTCUT: Final[dict[str, str]] = {
305
307
  nwKeyWords.TAG_KEY: "Ctrl+K, G",
306
308
  nwKeyWords.POV_KEY: "Ctrl+K, V",
307
309
  nwKeyWords.FOCUS_KEY: "Ctrl+K, F",
@@ -315,7 +317,7 @@ class nwLabels:
315
317
  nwKeyWords.STORY_KEY: "Ctrl+K, N",
316
318
  nwKeyWords.MENTION_KEY: "Ctrl+K, M",
317
319
  }
318
- OUTLINE_COLS = {
320
+ OUTLINE_COLS: Final[dict[nwOutline, str]] = {
319
321
  nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"),
320
322
  nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"),
321
323
  nwOutline.LABEL: QT_TRANSLATE_NOOP("Constant", "Document"),
@@ -337,7 +339,7 @@ class nwLabels:
337
339
  nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY],
338
340
  nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
339
341
  }
340
- STATS_NAME = {
342
+ STATS_NAME: Final[dict[str, str]] = {
341
343
  nwStats.CHARS: QT_TRANSLATE_NOOP("Stats", "Characters"),
342
344
  nwStats.CHARS_TEXT: QT_TRANSLATE_NOOP("Stats", "Characters in Text"),
343
345
  nwStats.CHARS_TITLE: QT_TRANSLATE_NOOP("Stats", "Characters in Headings"),
@@ -350,7 +352,11 @@ class nwLabels:
350
352
  nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
351
353
  nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"),
352
354
  }
353
- BUILD_FMT = {
355
+ STATS_DISPLAY: Final[dict[str, str]] = {
356
+ nwStats.CHARS: QT_TRANSLATE_NOOP("Stats", "Characters: {0} ({1})"),
357
+ nwStats.WORDS: QT_TRANSLATE_NOOP("Stats", "Words: {0} ({1})"),
358
+ }
359
+ BUILD_FMT: Final[dict[nwBuildFmt, str]] = {
354
360
  nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
355
361
  nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
356
362
  nwBuildFmt.DOCX: QT_TRANSLATE_NOOP("Constant", "Microsoft Word Document (.docx)"),
@@ -362,7 +368,7 @@ class nwLabels:
362
368
  nwBuildFmt.J_HTML: QT_TRANSLATE_NOOP("Constant", "JSON + HTML 5 (.json)"),
363
369
  nwBuildFmt.J_NWD: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markup (.json)"),
364
370
  }
365
- BUILD_EXT = {
371
+ BUILD_EXT: Final[dict[nwBuildFmt, str]] = {
366
372
  nwBuildFmt.ODT: ".odt",
367
373
  nwBuildFmt.FODT: ".fodt",
368
374
  nwBuildFmt.DOCX: ".docx",
@@ -374,7 +380,7 @@ class nwLabels:
374
380
  nwBuildFmt.J_HTML: ".json",
375
381
  nwBuildFmt.J_NWD: ".json",
376
382
  }
377
- SHAPES_PLAIN = {
383
+ SHAPES_PLAIN: Final[dict[nwStatusShape, str]] = {
378
384
  nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
379
385
  nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
380
386
  nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
@@ -384,42 +390,42 @@ class nwLabels:
384
390
  nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
385
391
  nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
386
392
  }
387
- SHAPES_CIRCLE = {
393
+ SHAPES_CIRCLE: Final[dict[nwStatusShape, str]] = {
388
394
  nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "1/4 Circle"),
389
395
  nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Half Circle"),
390
396
  nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "3/4 Circle"),
391
397
  nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Full Circle"),
392
398
  }
393
- SHAPES_BARS = {
399
+ SHAPES_BARS: Final[dict[nwStatusShape, str]] = {
394
400
  nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
395
401
  nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
396
402
  nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
397
403
  nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Constant", "4 Bars"),
398
404
  }
399
- SHAPES_BLOCKS = {
405
+ SHAPES_BLOCKS: Final[dict[nwStatusShape, str]] = {
400
406
  nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Constant", "1 Block"),
401
407
  nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Constant", "2 Blocks"),
402
408
  nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Constant", "3 Blocks"),
403
409
  nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Constant", "4 Blocks"),
404
410
  }
405
- FILE_FILTERS = {
411
+ FILE_FILTERS: Final[dict[str, str]] = {
406
412
  "*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
407
413
  "*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
408
414
  "*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
409
415
  "*.csv": QT_TRANSLATE_NOOP("Constant", "CSV files"),
410
416
  "*": QT_TRANSLATE_NOOP("Constant", "All files"),
411
417
  }
412
- UNIT_NAME = {
418
+ UNIT_NAME: Final[dict[str, str]] = {
413
419
  "mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"),
414
420
  "cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"),
415
421
  "in": QT_TRANSLATE_NOOP("Constant", "Inches"),
416
422
  }
417
- UNIT_SCALE = {
423
+ UNIT_SCALE: Final[dict[str, float]] = {
418
424
  "mm": 1.0,
419
425
  "cm": 10.0,
420
426
  "in": 25.4,
421
427
  }
422
- PAPER_NAME = {
428
+ PAPER_NAME: Final[dict[str, str]] = {
423
429
  "A4": QT_TRANSLATE_NOOP("Constant", "A4"),
424
430
  "A5": QT_TRANSLATE_NOOP("Constant", "A5"),
425
431
  "A6": QT_TRANSLATE_NOOP("Constant", "A6"),
@@ -427,7 +433,7 @@ class nwLabels:
427
433
  "Letter": QT_TRANSLATE_NOOP("Constant", "US Letter"),
428
434
  "Custom": QT_TRANSLATE_NOOP("Constant", "Custom"),
429
435
  }
430
- PAPER_SIZE = {
436
+ PAPER_SIZE: Final[dict[str, tuple[float, float]]] = {
431
437
  "A4": (210.0, 297.0),
432
438
  "A5": (148.0, 210.0),
433
439
  "A6": (105.0, 148.0),
@@ -435,6 +441,18 @@ class nwLabels:
435
441
  "Letter": (215.9, 279.4),
436
442
  "Custom": (-1.0, -1.0),
437
443
  }
444
+ THEME_COLORS: Final[dict[str, str]] = {
445
+ "theme": QT_TRANSLATE_NOOP("Constant", "Theme Colours"),
446
+ "default": QT_TRANSLATE_NOOP("Constant", "Foreground Colour"),
447
+ "faded": QT_TRANSLATE_NOOP("Constant", "Faded Colour"),
448
+ "red": QT_TRANSLATE_NOOP("Constant", "Red"),
449
+ "orange": QT_TRANSLATE_NOOP("Constant", "Orange"),
450
+ "yellow": QT_TRANSLATE_NOOP("Constant", "Yellow"),
451
+ "green": QT_TRANSLATE_NOOP("Constant", "Green"),
452
+ "aqua": QT_TRANSLATE_NOOP("Constant", "Aqua"),
453
+ "blue": QT_TRANSLATE_NOOP("Constant", "Blue"),
454
+ "purple": QT_TRANSLATE_NOOP("Constant", "Purple"),
455
+ }
438
456
 
439
457
 
440
458
  class nwHeadFmt:
@@ -450,7 +468,7 @@ class nwHeadFmt:
450
468
  CHAR_POV = "{Char:POV}"
451
469
  CHAR_FOCUS = "{Char:Focus}"
452
470
 
453
- PAGE_HEADERS = [
471
+ PAGE_HEADERS: Final[list[str]] = [
454
472
  TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS,
455
473
  CHAR_POV, CHAR_FOCUS
456
474
  ]
@@ -466,7 +484,7 @@ class nwQuotes:
466
484
  """Allowed quotation marks.
467
485
  Source: https://en.wikipedia.org/wiki/Quotation_mark
468
486
  """
469
- SYMBOLS = {
487
+ SYMBOLS: Final[dict[str, str]] = {
470
488
  "\u0027": QT_TRANSLATE_NOOP("Constant", "Straight single quotation mark"),
471
489
  "\u0022": QT_TRANSLATE_NOOP("Constant", "Straight double quotation mark"),
472
490
 
@@ -491,6 +509,19 @@ class nwQuotes:
491
509
  "\u300f": QT_TRANSLATE_NOOP("Constant", "Right white corner bracket"),
492
510
  }
493
511
 
512
+ DASHES: Final[dict[str, str]] = {
513
+ "": QT_TRANSLATE_NOOP("Constant", "None"),
514
+ "\u2013": QT_TRANSLATE_NOOP("Constant", "Short dash"),
515
+ "\u2014": QT_TRANSLATE_NOOP("Constant", "Long dash"),
516
+ "\u2015": QT_TRANSLATE_NOOP("Constant", "Horizontal bar"),
517
+ }
518
+
519
+ ALLOWED: Final[list[str]] = [
520
+ "\u0027", "\u0022", "\u2018", "\u2019", "\u201a", "\u201b", "\u201c", "\u201d", "\u201e",
521
+ "\u201f", "\u2e42", "\u2039", "\u203a", "\u00ab", "\u00bb", "\u300c", "\u300d", "\u300e",
522
+ "\u300f", "\u2013", "\u2014", "\u2015",
523
+ ]
524
+
494
525
 
495
526
  class nwUnicode:
496
527
  """Supported unicode character constants and their HTML equivalents."""
@@ -631,9 +662,9 @@ class nwUnicode:
631
662
  H_LTRIS = "&#9666;"
632
663
 
633
664
 
634
- class nwHtmlUnicode():
665
+ class nwHtmlUnicode:
635
666
 
636
- U_TO_H = {
667
+ U_TO_H: Final[dict[str, str]] = {
637
668
  # Quotes
638
669
  nwUnicode.U_QUOT: nwUnicode.H_QUOT,
639
670
  nwUnicode.U_APOS: nwUnicode.H_APOS,
@@ -28,11 +28,11 @@ import json
28
28
  import logging
29
29
  import uuid
30
30
 
31
- from collections.abc import Iterable
32
31
  from enum import Enum
33
32
  from pathlib import Path
33
+ from typing import TYPE_CHECKING
34
34
 
35
- from PyQt5.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
35
+ from PyQt6.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
36
36
 
37
37
  from novelwriter import CONFIG
38
38
  from novelwriter.common import checkUuid, isHandle, jsonEncode
@@ -41,13 +41,18 @@ from novelwriter.core.project import NWProject
41
41
  from novelwriter.enum import nwBuildFmt
42
42
  from novelwriter.error import logException
43
43
 
44
+ if TYPE_CHECKING:
45
+ from collections.abc import Iterable
46
+
44
47
  logger = logging.getLogger(__name__)
45
48
 
49
+ T_BuildValue = str | int | float | bool
50
+
46
51
  # The Settings Template
47
52
  # =====================
48
53
  # Each entry contains a tuple on the form: (type, default)
49
54
 
50
- SETTINGS_TEMPLATE: dict[str, tuple[type, str | int | float | bool]] = {
55
+ SETTINGS_TEMPLATE: dict[str, tuple[type, T_BuildValue]] = {
51
56
  "filter.includeNovel": (bool, True),
52
57
  "filter.includeNotes": (bool, False),
53
58
  "filter.includeInactive": (bool, False),
@@ -73,6 +78,8 @@ SETTINGS_TEMPLATE: dict[str, tuple[type, str | int | float | bool]] = {
73
78
  "headings.breakScene": (bool, False),
74
79
  "text.includeSynopsis": (bool, False),
75
80
  "text.includeComments": (bool, False),
81
+ "text.includeStory": (bool, False),
82
+ "text.includeNotes": (bool, False),
76
83
  "text.includeKeywords": (bool, False),
77
84
  "text.includeBodyText": (bool, True),
78
85
  "text.ignoredKeywords": (str, ""),
@@ -139,6 +146,8 @@ SETTINGS_LABELS = {
139
146
  "text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"),
140
147
  "text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"),
141
148
  "text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"),
149
+ "text.includeStory": QT_TRANSLATE_NOOP("Builds", "Include Story Structure"),
150
+ "text.includeNotes": QT_TRANSLATE_NOOP("Builds", "Include Manuscript Notes"),
142
151
  "text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"),
143
152
  "text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"),
144
153
  "text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"),
@@ -185,7 +194,6 @@ SETTINGS_LABELS = {
185
194
  }
186
195
 
187
196
  RENAMED = {
188
- "odt.addColours": "doc.addColours",
189
197
  "odt.pageHeader": "doc.pageHeader",
190
198
  "odt.pageCountOffset": "doc.pageCountOffset",
191
199
  }
@@ -226,9 +234,9 @@ class BuildSettings:
226
234
  @classmethod
227
235
  def fromDict(cls, data: dict) -> BuildSettings:
228
236
  """Create a build settings object from a dict."""
229
- cls = BuildSettings()
230
- cls.unpack(data)
231
- return cls
237
+ new = cls()
238
+ new.unpack(data)
239
+ return new
232
240
 
233
241
  ##
234
242
  # Properties
@@ -293,12 +301,12 @@ class BuildSettings:
293
301
  def getInt(self, key: str) -> int:
294
302
  """Type safe value access for integers."""
295
303
  value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None))[1])
296
- return int(value) if isinstance(value, (int, float)) else 0
304
+ return int(value) if isinstance(value, int | float) else 0
297
305
 
298
306
  def getFloat(self, key: str) -> float:
299
307
  """Type safe value access for floats."""
300
308
  value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None))[1])
301
- return float(value) if isinstance(value, (int, float)) else 0.0
309
+ return float(value) if isinstance(value, int | float) else 0.0
302
310
 
303
311
  ##
304
312
  # Setters
@@ -379,10 +387,10 @@ class BuildSettings:
379
387
  self._changed = True
380
388
  return
381
389
 
382
- def setValue(self, key: str, value: str | int | float | bool) -> None:
390
+ def setValue(self, key: str, value: T_BuildValue) -> None:
383
391
  """Set a specific value for a build setting."""
384
392
  if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
385
- self._changed = value != self._settings[key]
393
+ self._changed |= (value != self._settings[key])
386
394
  self._settings[key] = value
387
395
  return
388
396
 
@@ -503,7 +511,8 @@ class BuildSettings:
503
511
  self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()}
504
512
  if isinstance(settings, dict):
505
513
  for key, value in settings.items():
506
- self.setValue(RENAMED.get(key, key), value)
514
+ if isinstance(key, str) and isinstance(value, T_BuildValue):
515
+ self.setValue(RENAMED.get(key, key), value)
507
516
 
508
517
  self._changed = False
509
518
 
@@ -512,11 +521,11 @@ class BuildSettings:
512
521
  @classmethod
513
522
  def duplicate(cls, source: BuildSettings) -> BuildSettings:
514
523
  """Make a copy of another build."""
515
- cls = BuildSettings()
516
- cls.unpack(source.pack())
517
- cls._uuid = str(uuid.uuid4())
518
- cls._name = f"{source.name} 2"
519
- return cls
524
+ new = cls()
525
+ new.unpack(source.pack())
526
+ new._uuid = str(uuid.uuid4())
527
+ new._name = f"{source.name} 2"
528
+ return new
520
529
 
521
530
 
522
531
  class BuildCollection:
@@ -30,20 +30,24 @@ import logging
30
30
  import re
31
31
  import shutil
32
32
 
33
- from collections.abc import Iterable
34
33
  from functools import partial
35
34
  from pathlib import Path
35
+ from typing import TYPE_CHECKING
36
36
  from zipfile import ZipFile, is_zipfile
37
37
 
38
- from PyQt5.QtCore import QCoreApplication
38
+ from PyQt6.QtCore import QCoreApplication
39
39
 
40
40
  from novelwriter import CONFIG, SHARED
41
41
  from novelwriter.common import isHandle, minmax, simplified
42
42
  from novelwriter.constants import nwConst, nwFiles, nwItemClass, nwStats
43
- from novelwriter.core.item import NWItem
44
43
  from novelwriter.core.project import NWProject
45
44
  from novelwriter.core.storage import NWStorageCreate
46
45
 
46
+ if TYPE_CHECKING:
47
+ from collections.abc import Iterable
48
+
49
+ from novelwriter.core.item import NWItem
50
+
47
51
  logger = logging.getLogger(__name__)
48
52
 
49
53
 
@@ -284,7 +288,7 @@ class DocDuplicator:
284
288
  class DocSearch:
285
289
 
286
290
  def __init__(self) -> None:
287
- self._regEx = re.compile("")
291
+ self._regEx = re.compile(r"")
288
292
  self._opts = re.UNICODE | re.IGNORECASE
289
293
  self._words = False
290
294
  self._escape = True
@@ -379,10 +383,12 @@ class ProjectBuilder:
379
383
  """Build or copy a project from a data dictionary."""
380
384
  if isinstance(data, dict):
381
385
  path = data.get("path", None) or None
382
- if isinstance(path, (str, Path)):
386
+ if author := data.get("author"):
387
+ CONFIG.setLastAuthor(author)
388
+ if isinstance(path, str | Path):
383
389
  self._path = Path(path).resolve()
384
- if data.get("sample", False):
385
- return self._extractSampleProject(self._path)
390
+ if data.get("sample"):
391
+ return self._extractSampleProject(self._path, data)
386
392
  elif data.get("template"):
387
393
  return self._copyProject(self._path, data)
388
394
  else:
@@ -412,20 +418,21 @@ class ProjectBuilder:
412
418
 
413
419
  self._path = project.storage.storagePath
414
420
 
415
- lblNewProject = self.tr("New Project")
416
- lblTitlePage = self.tr("Title Page")
421
+ trName = self.tr("New Project")
422
+ trAuthor = self.tr("Author Name")
423
+ trTitlePage = self.tr("Title Page")
417
424
 
418
425
  # Settings
419
426
  project.data.setUuid(None)
420
- project.data.setName(data.get("name", lblNewProject))
421
- project.data.setAuthor(data.get("author", ""))
427
+ project.data.setName(data.get("name", trName))
428
+ project.data.setAuthor(data.get("author", trAuthor))
422
429
  project.data.setLanguage(CONFIG.guiLocale)
423
430
  project.setDefaultStatusImport()
424
431
  project.session.startSession()
425
432
 
426
433
  # Add Root Folders
427
434
  hNovelRoot = project.newRoot(nwItemClass.NOVEL)
428
- hTitlePage = project.newFile(lblTitlePage, hNovelRoot)
435
+ hTitlePage = project.newFile(trTitlePage, hNovelRoot)
429
436
 
430
437
  # Generate Title Page
431
438
  aDoc = project.storage.getDocument(hTitlePage)
@@ -442,25 +449,21 @@ class ProjectBuilder:
442
449
  "\n"
443
450
  ">> {count}: [field:{field}] <<\n"
444
451
  ).format(
445
- author=project.data.author or "None",
446
- address=self.tr("Address"),
447
- title=project.data.name or "None",
452
+ author=project.data.author or trAuthor,
453
+ address=self.tr("Address Line"),
454
+ title=project.data.name or trName,
448
455
  by=self.tr("By"),
449
456
  count=self.tr("Word Count"),
450
457
  field=nwStats.WORDS_TEXT,
451
458
  ))
452
459
 
453
- # Create a project structure based on selected root folders
454
- # and a number of chapters and scenes selected in the
455
- # wizard's custom page.
456
-
457
460
  # Create chapters and scenes
458
461
  numChapters = data.get("chapters", 0)
459
462
  numScenes = data.get("scenes", 0)
460
463
 
461
- chSynop = self.tr("Summary of the chapter.")
462
- scSynop = self.tr("Summary of the scene.")
463
- bfNote = self.tr("A short description.")
464
+ trChSynop = self.tr("Summary of the chapter.")
465
+ trScSynop = self.tr("Summary of the scene.")
466
+ trNoteDesc = self.tr("A short description.")
464
467
 
465
468
  # Create chapters
466
469
  if numChapters > 0:
@@ -468,7 +471,7 @@ class ProjectBuilder:
468
471
  chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
469
472
  cHandle = project.newFile(chTitle, hNovelRoot)
470
473
  aDoc = project.storage.getDocument(cHandle)
471
- aDoc.writeDocument(f"## {chTitle}\n\n%Synopsis: {chSynop}\n\n")
474
+ aDoc.writeDocument(f"## {chTitle}\n\n%Synopsis: {trChSynop}\n\n")
472
475
 
473
476
  # Create chapter scenes
474
477
  if numScenes > 0 and cHandle:
@@ -476,7 +479,7 @@ class ProjectBuilder:
476
479
  scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
477
480
  sHandle = project.newFile(scTitle, cHandle)
478
481
  aDoc = project.storage.getDocument(sHandle)
479
- aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
482
+ aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {trScSynop}\n\n")
480
483
 
481
484
  # Create scenes (no chapters)
482
485
  elif numScenes > 0:
@@ -484,7 +487,7 @@ class ProjectBuilder:
484
487
  scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
485
488
  sHandle = project.newFile(scTitle, hNovelRoot)
486
489
  aDoc = project.storage.getDocument(sHandle)
487
- aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {scSynop}\n\n")
490
+ aDoc.writeDocument(f"### {scTitle}\n\n%Synopsis: {trScSynop}\n\n")
488
491
 
489
492
  # Create notes folders
490
493
  noteTitles = {
@@ -504,12 +507,12 @@ class ProjectBuilder:
504
507
  aDoc.writeDocument(
505
508
  f"# {noteTitles[newRoot]}\n\n"
506
509
  f"@tag: {ntTag}\n\n"
507
- f"%Short: {bfNote}\n\n"
510
+ f"%Short: {trNoteDesc}\n\n"
508
511
  )
509
512
 
510
513
  # Also add the archive and trash folders
511
514
  project.newRoot(nwItemClass.ARCHIVE)
512
- project.tree.trash # Triggers the creation of Trash
515
+ _ = project.tree.trash # Triggers the creation of Trash
513
516
 
514
517
  project.saveProject()
515
518
  project.closeProject()
@@ -559,23 +562,11 @@ class ProjectBuilder:
559
562
  return False
560
563
 
561
564
  # Open the copied project and update settings
562
- project = NWProject()
563
- project.openProject(dstPath)
564
- project.data.setUuid("") # Creates a fresh uuid
565
- project.data.setName(data.get("name", "None"))
566
- project.data.setAuthor(data.get("author", ""))
567
- project.data.setSpellCheck(True)
568
- project.data.setSpellLang(None)
569
- project.data.setDoBackup(True)
570
- project.data.setSaveCount(0)
571
- project.data.setAutoCount(0)
572
- project.data.setEditTime(0)
573
- project.saveProject()
574
- project.closeProject()
565
+ self._resetProject(dstPath, data)
575
566
 
576
567
  return True
577
568
 
578
- def _extractSampleProject(self, path: Path) -> bool:
569
+ def _extractSampleProject(self, path: Path, data: dict) -> bool:
579
570
  """Make a copy of the sample project by extracting the
580
571
  sample.zip file to the new path.
581
572
  """
@@ -589,6 +580,7 @@ class ProjectBuilder:
589
580
  if (sample := CONFIG.assetPath("sample.zip")).is_file():
590
581
  try:
591
582
  shutil.unpack_archive(sample, path)
583
+ self._resetProject(path, data)
592
584
  except Exception as exc:
593
585
  SHARED.error(self.tr("Failed to create a new example project."), exc=exc)
594
586
  return False
@@ -601,3 +593,22 @@ class ProjectBuilder:
601
593
  return False
602
594
 
603
595
  return True
596
+
597
+ def _resetProject(self, path: Path, data: dict) -> None:
598
+ """Open a project and reset/update its settings."""
599
+ project = NWProject()
600
+ project.openProject(path)
601
+ project.data.setUuid("") # Creates a fresh uuid
602
+ if name := data.get("name", ""):
603
+ project.data.setName(name)
604
+ if author := data.get("author", ""):
605
+ project.data.setAuthor(author)
606
+ project.data.setSpellCheck(True)
607
+ project.data.setSpellLang(None)
608
+ project.data.setDoBackup(True)
609
+ project.data.setSaveCount(0)
610
+ project.data.setAutoCount(0)
611
+ project.data.setEditTime(0)
612
+ project.saveProject()
613
+ project.closeProject()
614
+ return