novelWriter 2.6.2__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 +92 -56
  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 +15 -18
  104. novelwriter/gui/mainmenu.py +147 -146
  105. novelwriter/gui/noveltree.py +246 -406
  106. novelwriter/gui/outline.py +145 -76
  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 +114 -127
  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.2.dist-info → novelwriter-2.7.dist-info}/METADATA +6 -6
  127. novelwriter-2.7.dist-info/RECORD +162 -0
  128. {novelWriter-2.6.2.dist-info → novelwriter-2.7.dist-info}/WHEEL +1 -1
  129. novelWriter-2.6.2.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.2.dist-info → novelwriter-2.7.dist-info}/entry_points.txt +0 -0
  347. {novelWriter-2.6.2.dist-info → novelwriter-2.7.dist-info/licenses}/LICENSE.md +0 -0
  348. {novelWriter-2.6.2.dist-info → novelwriter-2.7.dist-info}/top_level.txt +0 -0
novelwriter/guimain.py CHANGED
@@ -30,11 +30,11 @@ from datetime import datetime
30
30
  from pathlib import Path
31
31
  from time import time
32
32
 
33
- from PyQt5.QtCore import Qt, QTimer, pyqtSlot
34
- from PyQt5.QtGui import QCloseEvent, QCursor, QIcon
35
- from PyQt5.QtWidgets import (
33
+ from PyQt6.QtCore import Qt, QTimer, pyqtSlot
34
+ from PyQt6.QtGui import QCloseEvent, QCursor, QIcon, QShortcut
35
+ from PyQt6.QtWidgets import (
36
36
  QApplication, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox,
37
- QShortcut, QSplitter, QStackedWidget, QVBoxLayout, QWidget
37
+ QSplitter, QStackedWidget, QVBoxLayout, QWidget
38
38
  )
39
39
 
40
40
  from novelwriter import CONFIG, SHARED, __hexversion__, __version__
@@ -56,7 +56,6 @@ from novelwriter.gui.projtree import GuiProjectView
56
56
  from novelwriter.gui.search import GuiProjectSearch
57
57
  from novelwriter.gui.sidebar import GuiSideBar
58
58
  from novelwriter.gui.statusbar import GuiMainStatus
59
- from novelwriter.gui.theme import GuiTheme
60
59
  from novelwriter.tools.dictionaries import GuiDictionaries
61
60
  from novelwriter.tools.manuscript import GuiManuscript
62
61
  from novelwriter.tools.noveldetails import GuiNovelDetails
@@ -92,8 +91,8 @@ class GuiMain(QMainWindow):
92
91
  logger.info("OS: %s", CONFIG.osType)
93
92
  logger.info("Kernel: %s", CONFIG.kernelVer)
94
93
  logger.info("Host: %s", CONFIG.hostName)
95
- logger.info("Qt5: %s (0x%06x)", CONFIG.verQtString, CONFIG.verQtValue)
96
- logger.info("PyQt5: %s (0x%06x)", CONFIG.verPyQtString, CONFIG.verPyQtValue)
94
+ logger.info("Qt: %s (0x%06x)", CONFIG.verQtString, CONFIG.verQtValue)
95
+ logger.info("PyQt: %s (0x%06x)", CONFIG.verPyQtString, CONFIG.verPyQtValue)
97
96
  logger.info("Python: %s (0x%08x)", CONFIG.verPyString, sys.hexversion)
98
97
  logger.info("GUI Language: %s", CONFIG.guiLocale)
99
98
 
@@ -101,7 +100,7 @@ class GuiMain(QMainWindow):
101
100
  # ============
102
101
 
103
102
  # Initialise UserData Instance
104
- SHARED.initSharedData(self, GuiTheme())
103
+ SHARED.initSharedData(self)
105
104
 
106
105
  # Prepare Main Window
107
106
  self.resize(*CONFIG.mainWinSize)
@@ -115,10 +114,6 @@ class GuiMain(QMainWindow):
115
114
  # Build the GUI
116
115
  # =============
117
116
 
118
- # Sizes
119
- mPx = CONFIG.pxInt(4)
120
- hWd = CONFIG.pxInt(4)
121
-
122
117
  # Main GUI Elements
123
118
  self.mainStatus = GuiMainStatus(self)
124
119
  self.projView = GuiProjectView(self)
@@ -143,7 +138,7 @@ class GuiMain(QMainWindow):
143
138
  self.treePane = QWidget(self)
144
139
  self.treeBox = QVBoxLayout()
145
140
  self.treeBox.setContentsMargins(0, 0, 0, 0)
146
- self.treeBox.setSpacing(mPx)
141
+ self.treeBox.setSpacing(4)
147
142
  self.treeBox.addWidget(self.projStack)
148
143
  self.treeBox.addWidget(self.itemDetails)
149
144
  self.treePane.setLayout(self.treeBox)
@@ -152,7 +147,7 @@ class GuiMain(QMainWindow):
152
147
  self.splitView = QSplitter(Qt.Orientation.Vertical, self)
153
148
  self.splitView.addWidget(self.docViewer)
154
149
  self.splitView.addWidget(self.docViewerPanel)
155
- self.splitView.setHandleWidth(hWd)
150
+ self.splitView.setHandleWidth(4)
156
151
  self.splitView.setOpaqueResize(False)
157
152
  self.splitView.setSizes(CONFIG.viewPanePos)
158
153
  self.splitView.setCollapsible(0, False)
@@ -163,7 +158,7 @@ class GuiMain(QMainWindow):
163
158
  self.splitDocs.addWidget(self.docEditor)
164
159
  self.splitDocs.addWidget(self.splitView)
165
160
  self.splitDocs.setOpaqueResize(False)
166
- self.splitDocs.setHandleWidth(hWd)
161
+ self.splitDocs.setHandleWidth(4)
167
162
  self.splitDocs.setCollapsible(0, False)
168
163
  self.splitDocs.setCollapsible(1, False)
169
164
 
@@ -173,7 +168,7 @@ class GuiMain(QMainWindow):
173
168
  self.splitMain.addWidget(self.treePane)
174
169
  self.splitMain.addWidget(self.splitDocs)
175
170
  self.splitMain.setOpaqueResize(False)
176
- self.splitMain.setHandleWidth(hWd)
171
+ self.splitMain.setHandleWidth(4)
177
172
  self.splitMain.setSizes(CONFIG.mainPanePos)
178
173
  self.splitMain.setCollapsible(0, False)
179
174
  self.splitMain.setCollapsible(0, False)
@@ -252,8 +247,6 @@ class GuiMain(QMainWindow):
252
247
  self.docEditor.itemHandleChanged.connect(self.novelView.setActiveHandle)
253
248
  self.docEditor.itemHandleChanged.connect(self.projView.setActiveHandle)
254
249
  self.docEditor.loadDocumentTagRequest.connect(self._followTag)
255
- self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
256
- self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
257
250
  self.docEditor.openDocumentRequest.connect(self._openDocument)
258
251
  self.docEditor.requestNewNoteCreation.connect(SHARED.createNewNote)
259
252
  self.docEditor.requestNextDocument.connect(self.openNextDocument)
@@ -317,11 +310,11 @@ class GuiMain(QMainWindow):
317
310
  self.asProjTimer.start()
318
311
  self.asDocTimer.start()
319
312
  self.mainStatus.clearStatus()
320
- self.showNormal()
321
313
 
322
314
  logger.debug("Ready: GUI")
323
315
  logger.info("novelWriter is ready ...")
324
316
  self.mainStatus.setStatusMessage(self.tr("novelWriter is ready ..."))
317
+ CONFIG.splashMessage("novelWriter is ready ...")
325
318
 
326
319
  return
327
320
 
@@ -347,6 +340,15 @@ class GuiMain(QMainWindow):
347
340
  logger.info("Command line path: %s", cmdOpen)
348
341
  self.openProject(cmdOpen)
349
342
 
343
+ # Add a small delay for the window coordinates to be ready
344
+ # before showing any dialogs
345
+ QTimer.singleShot(50, self.showPostLaunchDialogs)
346
+
347
+ return
348
+
349
+ @pyqtSlot()
350
+ def showPostLaunchDialogs(self) -> None:
351
+ """Show post launch dialogs."""
350
352
  if not SHARED.hasProject:
351
353
  self.showWelcomeDialog()
352
354
 
@@ -377,7 +379,7 @@ class GuiMain(QMainWindow):
377
379
  return True
378
380
 
379
381
  if not isYes:
380
- msgYes = SHARED.question("%s<br>%s" % (
382
+ msgYes = SHARED.question("{0}<br>{1}".format(
381
383
  self.tr("Close the current project?"),
382
384
  self.tr("Changes are saved automatically.")
383
385
  ))
@@ -537,7 +539,7 @@ class GuiMain(QMainWindow):
537
539
  ) -> bool:
538
540
  """Open a specific document, optionally at a given line."""
539
541
  if not (SHARED.hasProject and tHandle):
540
- logger.error("Nothing to open open")
542
+ logger.error("Nothing to open")
541
543
  return False
542
544
 
543
545
  if sTitle and tLine is None:
@@ -740,7 +742,6 @@ class GuiMain(QMainWindow):
740
742
 
741
743
  SHARED.project.index.rebuild()
742
744
  SHARED.project.tree.refreshAllItems()
743
- self.novelView.refreshTree()
744
745
 
745
746
  tEnd = time()
746
747
  self.mainStatus.setStatusMessage(
@@ -852,7 +853,7 @@ class GuiMain(QMainWindow):
852
853
 
853
854
  def closeMain(self) -> bool:
854
855
  """Save everything, and close novelWriter."""
855
- if SHARED.hasProject and CONFIG.askBeforeExit and not SHARED.question("%s<br>%s" % (
856
+ if SHARED.hasProject and CONFIG.askBeforeExit and not SHARED.question("{0}<br>{1}".format(
856
857
  self.tr("Do you want to exit novelWriter?"),
857
858
  self.tr("Changes are saved automatically.")
858
859
  )):
@@ -861,10 +862,10 @@ class GuiMain(QMainWindow):
861
862
  logger.info("Exiting novelWriter")
862
863
 
863
864
  if not SHARED.focusMode:
864
- CONFIG.setMainPanePos(self.splitMain.sizes())
865
- CONFIG.setOutlinePanePos(self.outlineView.splitSizes())
865
+ CONFIG.mainPanePos = self.splitMain.sizes()
866
+ CONFIG.outlinePanePos = self.outlineView.splitSizes()
866
867
  if self.docViewerPanel.isVisible():
867
- CONFIG.setViewPanePos(self.splitView.sizes())
868
+ CONFIG.viewPanePos = self.splitView.sizes()
868
869
 
869
870
  CONFIG.showViewerPanel = self.docViewerPanel.isVisible()
870
871
  wFull = Qt.WindowState.WindowFullScreen
@@ -1017,7 +1018,7 @@ class GuiMain(QMainWindow):
1017
1018
  fN = self.novelView.treeHasFocus()
1018
1019
 
1019
1020
  self._changeView(nwView.EDITOR)
1020
- if (vM and (vP and fP or vN and not fN)) or (not vM and vN):
1021
+ if (vM and ((vP and fP) or (vN and not fN))) or (not vM and vN):
1021
1022
  self._changeView(nwView.NOVEL)
1022
1023
  self.novelView.setTreeFocus()
1023
1024
  else:
@@ -1047,13 +1048,14 @@ class GuiMain(QMainWindow):
1047
1048
  self.initMain()
1048
1049
  self.saveDocument()
1049
1050
 
1050
- if tree:
1051
+ if tree and not theme:
1052
+ # These are also updated by a theme refresh
1051
1053
  SHARED.project.tree.refreshAllItems()
1054
+ self.novelView.refreshCurrentTree()
1052
1055
 
1053
1056
  if theme:
1054
- # We are doing this manually instead of connecting to
1055
- # paletteChanged since the processing order matters
1056
1057
  SHARED.theme.loadTheme()
1058
+ self.setPalette(QApplication.palette())
1057
1059
  self.docEditor.updateTheme()
1058
1060
  self.docViewer.updateTheme()
1059
1061
  self.docViewerPanel.updateTheme()
@@ -1064,16 +1066,18 @@ class GuiMain(QMainWindow):
1064
1066
  self.outlineView.updateTheme()
1065
1067
  self.itemDetails.updateTheme()
1066
1068
  self.mainStatus.updateTheme()
1069
+ SHARED.project.tree.refreshAllItems()
1067
1070
 
1068
1071
  if syntax:
1069
1072
  SHARED.theme.loadSyntax()
1070
- self.docEditor.updateSyntaxColours()
1073
+ self.docEditor.updateSyntaxColors()
1071
1074
 
1072
1075
  self.docEditor.initEditor()
1073
1076
  self.docViewer.initViewer()
1074
1077
  self.projView.initSettings()
1075
1078
  self.novelView.initSettings()
1076
1079
  self.outlineView.initSettings()
1080
+ self.mainStatus.initSettings()
1077
1081
 
1078
1082
  # Force update of word count
1079
1083
  self._lastTotalCount = 0
@@ -1179,6 +1183,12 @@ class GuiMain(QMainWindow):
1179
1183
  )
1180
1184
  elif view == nwView.OUTLINE:
1181
1185
  self.mainStack.setCurrentWidget(self.outlineView)
1186
+
1187
+ # Set active status
1188
+ isMain = self.mainStack.currentWidget() == self.splitMain
1189
+ isNovel = self.projStack.currentWidget() == self.novelView
1190
+ self.novelView.setActive(isMain and isNovel)
1191
+
1182
1192
  return
1183
1193
 
1184
1194
  @pyqtSlot(nwDocAction)
@@ -1254,15 +1264,23 @@ class GuiMain(QMainWindow):
1254
1264
  if self._lastTotalCount != currentTotalCount:
1255
1265
  self._lastTotalCount = currentTotalCount
1256
1266
 
1257
- SHARED.project.updateWordCounts()
1267
+ SHARED.project.updateCounts()
1258
1268
  if CONFIG.incNotesWCount:
1259
- iTotal = sum(SHARED.project.data.initCounts)
1260
- cTotal = sum(SHARED.project.data.currCounts)
1261
- self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
1269
+ if CONFIG.useCharCount:
1270
+ iTotal = sum(SHARED.project.data.initCounts[2:])
1271
+ cTotal = sum(SHARED.project.data.currCounts[2:])
1272
+ else:
1273
+ iTotal = sum(SHARED.project.data.initCounts[:2])
1274
+ cTotal = sum(SHARED.project.data.currCounts[:2])
1262
1275
  else:
1263
- iNovel, _ = SHARED.project.data.initCounts
1264
- cNovel, _ = SHARED.project.data.currCounts
1265
- self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
1276
+ if CONFIG.useCharCount:
1277
+ iTotal = SHARED.project.data.initCounts[2]
1278
+ cTotal = SHARED.project.data.currCounts[2]
1279
+ else:
1280
+ iTotal = SHARED.project.data.initCounts[0]
1281
+ cTotal = SHARED.project.data.currCounts[0]
1282
+
1283
+ self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
1266
1284
 
1267
1285
  return
1268
1286
 
novelwriter/shared.py CHANGED
@@ -31,16 +31,16 @@ from pathlib import Path
31
31
  from time import time
32
32
  from typing import TYPE_CHECKING, TypeVar
33
33
 
34
- from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, QUrl, pyqtSignal, pyqtSlot
35
- from PyQt5.QtGui import QDesktopServices, QFont
36
- from PyQt5.QtWidgets import QFileDialog, QFontDialog, QMessageBox, QWidget
34
+ from PyQt6.QtCore import QObject, QRunnable, QThreadPool, QTimer, QUrl, pyqtSignal, pyqtSlot
35
+ from PyQt6.QtGui import QDesktopServices, QFont
36
+ from PyQt6.QtWidgets import QFileDialog, QFontDialog, QMessageBox, QWidget
37
37
 
38
38
  from novelwriter.common import formatFileFilter
39
39
  from novelwriter.constants import nwFiles
40
40
  from novelwriter.core.spellcheck import NWSpellEnchant
41
41
  from novelwriter.enum import nwChange, nwItemClass
42
42
 
43
- if TYPE_CHECKING: # pragma: no cover
43
+ if TYPE_CHECKING:
44
44
  from novelwriter.core.project import NWProject
45
45
  from novelwriter.core.status import T_StatusKind
46
46
  from novelwriter.gui.theme import GuiTheme
@@ -54,8 +54,8 @@ NWWidget = TypeVar("NWWidget", bound=QWidget)
54
54
  class SharedData(QObject):
55
55
 
56
56
  __slots__ = (
57
- "_gui", "_theme", "_project", "_spelling", "_lockedBy", "_lastAlert",
58
- "_idleTime", "_idleRefTime",
57
+ "_gui", "_idleRefTime", "_idleTime", "_lastAlert", "_lockedBy",
58
+ "_project", "_spelling", "_theme",
59
59
  )
60
60
 
61
61
  focusModeChanged = pyqtSignal(bool)
@@ -63,10 +63,11 @@ class SharedData(QObject):
63
63
  indexChangedTags = pyqtSignal(list, list)
64
64
  indexCleared = pyqtSignal()
65
65
  mainClockTick = pyqtSignal()
66
+ novelStructureChanged = pyqtSignal(str)
66
67
  projectItemChanged = pyqtSignal(str, Enum)
67
- rootFolderChanged = pyqtSignal(str, Enum)
68
68
  projectStatusChanged = pyqtSignal(bool)
69
69
  projectStatusMessage = pyqtSignal(str)
70
+ rootFolderChanged = pyqtSignal(str, Enum)
70
71
  spellLanguageChanged = pyqtSignal(str, str)
71
72
  statusLabelsChanged = pyqtSignal(str)
72
73
 
@@ -100,28 +101,28 @@ class SharedData(QObject):
100
101
  def mainGui(self) -> GuiMain:
101
102
  """Return the Main GUI instance."""
102
103
  if self._gui is None:
103
- raise Exception("SharedData class not fully initialised")
104
+ raise RuntimeError("SharedData class not fully initialised")
104
105
  return self._gui
105
106
 
106
107
  @property
107
108
  def theme(self) -> GuiTheme:
108
109
  """Return the GUI Theme instance."""
109
110
  if self._theme is None:
110
- raise Exception("SharedData class not fully initialised")
111
+ raise RuntimeError("SharedData class not fully initialised")
111
112
  return self._theme
112
113
 
113
114
  @property
114
115
  def project(self) -> NWProject:
115
116
  """Return the active NWProject instance."""
116
117
  if self._project is None:
117
- raise Exception("SharedData class not fully initialised")
118
+ raise RuntimeError("SharedData class not fully initialised")
118
119
  return self._project
119
120
 
120
121
  @property
121
122
  def spelling(self) -> NWSpellEnchant:
122
123
  """Return the active NWProject instance."""
123
124
  if self._spelling is None:
124
- raise Exception("SharedData class not fully initialised")
125
+ raise RuntimeError("SharedData class not fully initialised")
125
126
  return self._spelling
126
127
 
127
128
  @property
@@ -164,17 +165,24 @@ class SharedData(QObject):
164
165
  # Methods
165
166
  ##
166
167
 
167
- def initSharedData(self, gui: GuiMain, theme: GuiTheme) -> None:
168
+ def initTheme(self, theme: GuiTheme) -> None:
169
+ """Initialise the GUI theme. This must be called before the GUI
170
+ is created.
171
+ """
172
+ self._theme = theme
173
+ return
174
+
175
+ def initSharedData(self, gui: GuiMain) -> None:
168
176
  """Initialise the SharedData instance. This must be called as
169
177
  soon as the Main GUI is created to ensure the SHARED singleton
170
178
  has the properties needed for operation.
171
179
  """
172
180
  self._clock.start()
173
181
  self._gui = gui
174
- self._theme = theme
175
182
  self._resetProject()
176
183
  logger.debug("Ready: SharedData")
177
- logger.debug("Thread Pool Max Count: %d", QThreadPool.globalInstance().maxThreadCount())
184
+ if pool := QThreadPool.globalInstance():
185
+ logger.debug("Thread Pool Max Count: %d", pool.maxThreadCount())
178
186
  return
179
187
 
180
188
  def closeDocument(self, tHandle: str | None = None) -> None:
@@ -266,7 +274,8 @@ class SharedData(QObject):
266
274
 
267
275
  def runInThreadPool(self, runnable: QRunnable, priority: int = 0) -> None:
268
276
  """Queue a runnable in the application thread pool."""
269
- QThreadPool.globalInstance().start(runnable, priority=priority)
277
+ if pool := QThreadPool.globalInstance():
278
+ pool.start(runnable, priority=priority)
270
279
  return
271
280
 
272
281
  def getProjectPath(
@@ -278,13 +287,13 @@ class SharedData(QObject):
278
287
  label = (self.tr("novelWriter Project File or Zip File")
279
288
  if allowZip else self.tr("novelWriter Project File"))
280
289
  ext = f"{nwFiles.PROJ_FILE} *.zip" if allowZip else nwFiles.PROJ_FILE
281
- ffilter = formatFileFilter([(label, ext), "*"])
290
+ fFilter = formatFileFilter([(label, ext), "*"])
282
291
  selected, _ = QFileDialog.getOpenFileName(
283
- parent, self.tr("Open Project"), str(path or ""), filter=ffilter
292
+ parent, self.tr("Open Project"), str(path or ""), filter=fFilter
284
293
  )
285
294
  return Path(selected) if selected else None
286
295
 
287
- def getFont(self, current: QFont, native: bool) -> tuple[QFont, bool]:
296
+ def getFont(self, current: QFont, native: bool) -> tuple[QFont, bool | None]:
288
297
  """Open the font dialog and select a font."""
289
298
  kwargs = {}
290
299
  if not native:
@@ -471,7 +480,7 @@ class _GuiAlert(QMessageBox):
471
480
  def setException(self, exception: Exception) -> None:
472
481
  """Add exception details."""
473
482
  info = self.informativeText()
474
- text = f"<b>{type(exception).__name__}</b>: {str(exception)}"
483
+ text = f"<b>{type(exception).__name__}</b>: {exception!s}"
475
484
  self.setInformativeText(f"{info}<br>{text}" if info else text)
476
485
  return
477
486
 
@@ -485,15 +494,15 @@ class _GuiAlert(QMessageBox):
485
494
  self.setStandardButtons(QMessageBox.StandardButton.Ok)
486
495
  pSz = 2*self._theme.baseIconHeight
487
496
  if level == self.INFO:
488
- self.setIconPixmap(self._theme.getPixmap("alert_info", (pSz, pSz)))
497
+ self.setIconPixmap(self._theme.getPixmap("alert_info", (pSz, pSz), "blue"))
489
498
  self.setWindowTitle(self.tr("Information"))
490
499
  elif level == self.WARN:
491
- self.setIconPixmap(self._theme.getPixmap("alert_warn", (pSz, pSz)))
500
+ self.setIconPixmap(self._theme.getPixmap("alert_warn", (pSz, pSz), "orange"))
492
501
  self.setWindowTitle(self.tr("Warning"))
493
502
  elif level == self.ERROR:
494
- self.setIconPixmap(self._theme.getPixmap("alert_error", (pSz, pSz)))
503
+ self.setIconPixmap(self._theme.getPixmap("alert_error", (pSz, pSz), "red"))
495
504
  self.setWindowTitle(self.tr("Error"))
496
505
  elif level == self.ASK:
497
- self.setIconPixmap(self._theme.getPixmap("alert_question", (pSz, pSz)))
506
+ self.setIconPixmap(self._theme.getPixmap("alert_question", (pSz, pSz), "blue"))
498
507
  self.setWindowTitle(self.tr("Question"))
499
508
  return
novelwriter/splash.py ADDED
@@ -0,0 +1,74 @@
1
+ """
2
+ novelWriter – Splash Screen
3
+ ===========================
4
+
5
+ File History:
6
+ Created: 2015-04-25 [2.7rc1]
7
+
8
+ This file is a part of novelWriter
9
+ Copyright (C) 2025 Veronica Berglyd Olsen and novelWriter contributors
10
+
11
+ This program is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ This program is distributed in the hope that it will be useful, but
17
+ WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
+ General Public License for more details.
20
+
21
+ You should have received a copy of the GNU General Public License
22
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+
28
+ from pathlib import Path
29
+ from time import sleep
30
+
31
+ from PyQt6.QtCore import QRect, Qt
32
+ from PyQt6.QtGui import QColor, QPainter, QPixmap
33
+ from PyQt6.QtWidgets import QSplashScreen
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ SPLASH_IMG = Path(__file__).parent / "assets" / "images" / "splash.png"
38
+
39
+
40
+ class NSplashScreen(QSplashScreen):
41
+
42
+ __slots__ = ("_color", "_rect", "_text")
43
+
44
+ def __init__(self) -> None:
45
+ super().__init__(pixmap=QPixmap(str(SPLASH_IMG)), flags=Qt.WindowType.WindowStaysOnTopHint)
46
+
47
+ logger.debug("Create: NSplashScreen")
48
+
49
+ font = self.font()
50
+ font.setPointSizeF(12.0)
51
+ self.setFont(font)
52
+ self._color = QColor(26, 52, 78)
53
+ self._rect = QRect(144, 110, 440, 30)
54
+ self._text = ""
55
+ return
56
+
57
+ def __del__(self) -> None: # pragma: no cover
58
+ logger.debug("Delete: NSplashScreen")
59
+ return
60
+
61
+ def drawContents(self, painter: QPainter) -> None:
62
+ """Draw the text message."""
63
+ painter.setPen(self._color)
64
+ painter.drawText(self._rect, Qt.AlignmentFlag.AlignLeft, self._text)
65
+ return
66
+
67
+ def showStatus(self, message: str) -> None:
68
+ """Update the status message."""
69
+ self._text = message
70
+ self.showMessage(message)
71
+ if message:
72
+ logger.info("[Splash] %s", message)
73
+ sleep(0.025)
74
+ return
@@ -0,0 +1,70 @@
1
+ """
2
+ novelWriter – Text Comments
3
+ ===========================
4
+
5
+ File History:
6
+ Created: 2023-11-23 [2.2b1]
7
+ Moved: 2025-02-09 [2.7b1]
8
+
9
+ This file is a part of novelWriter
10
+ Copyright (C) 2025 Veronica Berglyd Olsen and novelWriter contributors
11
+
12
+ This program is free software: you can redistribute it and/or modify
13
+ it under the terms of the GNU General Public License as published by
14
+ the Free Software Foundation, either version 3 of the License, or
15
+ (at your option) any later version.
16
+
17
+ This program is distributed in the hope that it will be useful, but
18
+ WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20
+ General Public License for more details.
21
+
22
+ You should have received a copy of the GNU General Public License
23
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ from novelwriter.enum import nwComment
28
+
29
+ MODIFIERS = {
30
+ "synopsis": nwComment.SYNOPSIS,
31
+ "short": nwComment.SHORT,
32
+ "note": nwComment.NOTE,
33
+ "footnote": nwComment.FOOTNOTE,
34
+ "story": nwComment.STORY,
35
+ }
36
+ KEY_REQ = {
37
+ "synopsis": 0, # Key not allowed
38
+ "short": 0, # Key not allowed
39
+ "note": 1, # Key optional
40
+ "footnote": 2, # Key required
41
+ "story": 2, # Key required
42
+ }
43
+
44
+
45
+ def _checkModKey(modifier: str, key: str) -> bool:
46
+ """Check if a modifier and key set are ok."""
47
+ if modifier in MODIFIERS:
48
+ if key == "":
49
+ return KEY_REQ[modifier] < 2
50
+ elif key.replace("_", "").isalnum():
51
+ return KEY_REQ[modifier] > 0
52
+ return False
53
+
54
+
55
+ def processComment(text: str) -> tuple[nwComment, str, str, int, int]:
56
+ """Extract comment style, key and text. Should only be called on
57
+ text starting with a %.
58
+ """
59
+ if text[:2] == "%~":
60
+ return nwComment.IGNORE, "", text[2:].lstrip(), 0, 0
61
+
62
+ check = text[1:].strip()
63
+ start, _, content = check.partition(":")
64
+ modifier, _, key = start.rstrip().partition(".")
65
+ if content and (clean := modifier.lower()) and _checkModKey(clean, key):
66
+ col = text.find(":") + 1
67
+ dot = text.find(".", 0, col) + 1
68
+ return MODIFIERS[clean], key, content.lstrip(), dot, col
69
+
70
+ return nwComment.PLAIN, "", check, 0, 0
@@ -93,7 +93,7 @@ class RegExPatterns:
93
93
  if CONFIG.dialogStyle in (1, 3):
94
94
  qO = CONFIG.fmtSQuoteOpen.strip()[:1]
95
95
  qC = CONFIG.fmtSQuoteClose.strip()[:1]
96
- if qO == qC:
96
+ if qO == qC or qC in self.AMBIGUOUS:
97
97
  rx.append(f"(?:\\B{qO}.+?{qC}\\B)")
98
98
  else:
99
99
  rx.append(f"(?:{qO}[^{qO}]+{qC})")
@@ -102,7 +102,7 @@ class RegExPatterns:
102
102
  if CONFIG.dialogStyle in (2, 3):
103
103
  qO = CONFIG.fmtDQuoteOpen.strip()[:1]
104
104
  qC = CONFIG.fmtDQuoteClose.strip()[:1]
105
- if qO == qC:
105
+ if qO == qC or qC in self.AMBIGUOUS:
106
106
  rx.append(f"(?:\\B{qO}.+?{qC}\\B)")
107
107
  else:
108
108
  rx.append(f"(?:{qO}[^{qO}]+{qC})")
@@ -128,8 +128,8 @@ REGEX_PATTERNS = RegExPatterns()
128
128
  class DialogParser:
129
129
 
130
130
  __slots__ = (
131
- "_quotes", "_dialog", "_alternate", "_enabled",
132
- "_narrator", "_breakD", "_breakQ", "_mode",
131
+ "_alternate", "_breakD", "_breakQ", "_dialog", "_enabled", "_mode",
132
+ "_narrator", "_quotes",
133
133
  )
134
134
 
135
135
  def __init__(self) -> None: