novelWriter 2.6.2__py3-none-any.whl → 2.7b1__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 (334) hide show
  1. novelwriter/__init__.py +84 -74
  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_en_GB.json +1 -0
  16. novelwriter/assets/icons/font_awesome.icons +109 -0
  17. novelwriter/assets/icons/material_filled_bold.icons +109 -0
  18. novelwriter/assets/icons/material_filled_normal.icons +109 -0
  19. novelwriter/assets/icons/material_filled_thin.icons +109 -0
  20. novelwriter/assets/icons/material_rounded_bold.icons +109 -0
  21. novelwriter/assets/icons/material_rounded_normal.icons +109 -0
  22. novelwriter/assets/icons/material_rounded_thin.icons +109 -0
  23. novelwriter/assets/icons/remix_filled.icons +108 -0
  24. novelwriter/assets/icons/remix_outline.icons +108 -0
  25. novelwriter/assets/manual.pdf +0 -0
  26. novelwriter/assets/{manual_fr_FR.pdf → manual_fr.pdf} +0 -0
  27. novelwriter/assets/sample.zip +0 -0
  28. novelwriter/assets/syntax/cyberpunk_night.conf +1 -1
  29. novelwriter/assets/text/credits_en.htm +6 -6
  30. novelwriter/assets/themes/cyberpunk_night.conf +23 -7
  31. novelwriter/assets/themes/default_dark.conf +20 -4
  32. novelwriter/assets/themes/default_light.conf +21 -5
  33. novelwriter/assets/themes/dracula.conf +20 -4
  34. novelwriter/assets/themes/solarized_dark.conf +24 -8
  35. novelwriter/assets/themes/solarized_light.conf +22 -6
  36. novelwriter/common.py +33 -26
  37. novelwriter/config.py +118 -127
  38. novelwriter/constants.py +75 -56
  39. novelwriter/core/buildsettings.py +23 -16
  40. novelwriter/core/coretools.py +11 -7
  41. novelwriter/core/docbuild.py +19 -13
  42. novelwriter/core/document.py +2 -2
  43. novelwriter/core/index.py +142 -432
  44. novelwriter/core/indexdata.py +403 -0
  45. novelwriter/core/item.py +35 -28
  46. novelwriter/core/itemmodel.py +27 -26
  47. novelwriter/core/novelmodel.py +223 -0
  48. novelwriter/core/options.py +1 -1
  49. novelwriter/core/project.py +10 -11
  50. novelwriter/core/projectdata.py +5 -5
  51. novelwriter/core/projectxml.py +1 -1
  52. novelwriter/core/sessions.py +3 -2
  53. novelwriter/core/spellcheck.py +4 -3
  54. novelwriter/core/status.py +12 -15
  55. novelwriter/core/storage.py +1 -1
  56. novelwriter/core/tree.py +46 -8
  57. novelwriter/dialogs/about.py +19 -22
  58. novelwriter/dialogs/docmerge.py +21 -23
  59. novelwriter/dialogs/docsplit.py +20 -23
  60. novelwriter/dialogs/editlabel.py +9 -13
  61. novelwriter/dialogs/preferences.py +111 -48
  62. novelwriter/dialogs/projectsettings.py +48 -54
  63. novelwriter/dialogs/quotes.py +14 -19
  64. novelwriter/dialogs/wordlist.py +25 -30
  65. novelwriter/enum.py +8 -0
  66. novelwriter/error.py +16 -16
  67. novelwriter/extensions/configlayout.py +18 -18
  68. novelwriter/extensions/eventfilters.py +9 -5
  69. novelwriter/extensions/modified.py +34 -15
  70. novelwriter/extensions/novelselector.py +18 -5
  71. novelwriter/extensions/pagedsidebar.py +39 -49
  72. novelwriter/extensions/progressbars.py +10 -8
  73. novelwriter/extensions/statusled.py +6 -13
  74. novelwriter/extensions/switch.py +19 -30
  75. novelwriter/extensions/switchbox.py +7 -3
  76. novelwriter/extensions/versioninfo.py +4 -4
  77. novelwriter/formats/shared.py +1 -1
  78. novelwriter/formats/todocx.py +14 -10
  79. novelwriter/formats/tohtml.py +7 -5
  80. novelwriter/formats/tokenizer.py +36 -33
  81. novelwriter/formats/tomarkdown.py +6 -2
  82. novelwriter/formats/toodt.py +27 -22
  83. novelwriter/formats/toqdoc.py +19 -14
  84. novelwriter/formats/toraw.py +6 -2
  85. novelwriter/gui/doceditor.py +216 -265
  86. novelwriter/gui/dochighlight.py +46 -45
  87. novelwriter/gui/docviewer.py +102 -104
  88. novelwriter/gui/docviewerpanel.py +47 -51
  89. novelwriter/gui/editordocument.py +8 -5
  90. novelwriter/gui/itemdetails.py +15 -18
  91. novelwriter/gui/mainmenu.py +147 -146
  92. novelwriter/gui/noveltree.py +239 -405
  93. novelwriter/gui/outline.py +137 -76
  94. novelwriter/gui/projtree.py +138 -132
  95. novelwriter/gui/search.py +33 -31
  96. novelwriter/gui/sidebar.py +13 -18
  97. novelwriter/gui/statusbar.py +13 -15
  98. novelwriter/gui/theme.py +533 -430
  99. novelwriter/guimain.py +27 -29
  100. novelwriter/shared.py +32 -23
  101. novelwriter/text/comments.py +70 -0
  102. novelwriter/text/patterns.py +4 -4
  103. novelwriter/tools/dictionaries.py +20 -29
  104. novelwriter/tools/lipsum.py +16 -17
  105. novelwriter/tools/manusbuild.py +39 -42
  106. novelwriter/tools/manuscript.py +113 -126
  107. novelwriter/tools/manussettings.py +78 -83
  108. novelwriter/tools/noveldetails.py +51 -64
  109. novelwriter/tools/welcome.py +56 -75
  110. novelwriter/tools/writingstats.py +44 -57
  111. novelwriter/types.py +5 -7
  112. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/METADATA +6 -6
  113. novelwriter-2.7b1.dist-info/RECORD +159 -0
  114. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/WHEEL +1 -1
  115. novelWriter-2.6.2.dist-info/RECORD +0 -363
  116. novelwriter/assets/icons/typicons_dark/README.md +0 -29
  117. novelwriter/assets/icons/typicons_dark/icons.conf +0 -134
  118. novelwriter/assets/icons/typicons_dark/mixed_copy.svg +0 -4
  119. novelwriter/assets/icons/typicons_dark/mixed_document-chapter.svg +0 -12
  120. novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +0 -6
  121. novelwriter/assets/icons/typicons_dark/mixed_document-note.svg +0 -12
  122. novelwriter/assets/icons/typicons_dark/mixed_document-scene.svg +0 -12
  123. novelwriter/assets/icons/typicons_dark/mixed_document-section.svg +0 -12
  124. novelwriter/assets/icons/typicons_dark/mixed_document-title.svg +0 -12
  125. novelwriter/assets/icons/typicons_dark/mixed_edit.svg +0 -4
  126. novelwriter/assets/icons/typicons_dark/mixed_import.svg +0 -5
  127. novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg +0 -5
  128. novelwriter/assets/icons/typicons_dark/mixed_input-none.svg +0 -5
  129. novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg +0 -5
  130. novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +0 -6
  131. novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +0 -6
  132. novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +0 -6
  133. novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +0 -6
  134. novelwriter/assets/icons/typicons_dark/mixed_search-replace.svg +0 -6
  135. novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +0 -6
  136. novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +0 -6
  137. novelwriter/assets/icons/typicons_dark/nw_deco-h0.svg +0 -4
  138. novelwriter/assets/icons/typicons_dark/nw_deco-h1.svg +0 -4
  139. novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +0 -4
  140. novelwriter/assets/icons/typicons_dark/nw_deco-h2.svg +0 -4
  141. novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +0 -4
  142. novelwriter/assets/icons/typicons_dark/nw_deco-h3.svg +0 -4
  143. novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +0 -4
  144. novelwriter/assets/icons/typicons_dark/nw_deco-h4.svg +0 -4
  145. novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +0 -4
  146. novelwriter/assets/icons/typicons_dark/nw_deco-noveltree-more.svg +0 -4
  147. novelwriter/assets/icons/typicons_dark/nw_font.svg +0 -4
  148. novelwriter/assets/icons/typicons_dark/nw_panel.svg +0 -4
  149. novelwriter/assets/icons/typicons_dark/nw_quote.svg +0 -4
  150. novelwriter/assets/icons/typicons_dark/nw_search-case.svg +0 -4
  151. novelwriter/assets/icons/typicons_dark/nw_search-preserve.svg +0 -4
  152. novelwriter/assets/icons/typicons_dark/nw_search-regex.svg +0 -4
  153. novelwriter/assets/icons/typicons_dark/nw_search-word.svg +0 -4
  154. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +0 -4
  155. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +0 -6
  156. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +0 -4
  157. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +0 -6
  158. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +0 -7
  159. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +0 -4
  160. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +0 -6
  161. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +0 -7
  162. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +0 -7
  163. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +0 -7
  164. novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +0 -5
  165. novelwriter/assets/icons/typicons_dark/typ_arrow-down-thick-grey.svg +0 -4
  166. novelwriter/assets/icons/typicons_dark/typ_arrow-forward.svg +0 -4
  167. novelwriter/assets/icons/typicons_dark/typ_arrow-maximise.svg +0 -4
  168. novelwriter/assets/icons/typicons_dark/typ_arrow-minimise.svg +0 -4
  169. novelwriter/assets/icons/typicons_dark/typ_arrow-repeat-grey.svg +0 -4
  170. novelwriter/assets/icons/typicons_dark/typ_book-grey.svg +0 -4
  171. novelwriter/assets/icons/typicons_dark/typ_book.svg +0 -6
  172. novelwriter/assets/icons/typicons_dark/typ_bookmark.svg +0 -4
  173. novelwriter/assets/icons/typicons_dark/typ_calendar.svg +0 -4
  174. novelwriter/assets/icons/typicons_dark/typ_cancel-grey.svg +0 -4
  175. novelwriter/assets/icons/typicons_dark/typ_cancel.svg +0 -4
  176. novelwriter/assets/icons/typicons_dark/typ_chart-bar-grey.svg +0 -4
  177. novelwriter/assets/icons/typicons_dark/typ_chevron-down.svg +0 -4
  178. novelwriter/assets/icons/typicons_dark/typ_chevron-left.svg +0 -4
  179. novelwriter/assets/icons/typicons_dark/typ_chevron-right.svg +0 -4
  180. novelwriter/assets/icons/typicons_dark/typ_chevron-up.svg +0 -4
  181. novelwriter/assets/icons/typicons_dark/typ_cog.svg +0 -4
  182. novelwriter/assets/icons/typicons_dark/typ_delete-full.svg +0 -4
  183. novelwriter/assets/icons/typicons_dark/typ_delete.svg +0 -4
  184. novelwriter/assets/icons/typicons_dark/typ_directions-full.svg +0 -4
  185. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +0 -4
  186. novelwriter/assets/icons/typicons_dark/typ_document-text.svg +0 -8
  187. novelwriter/assets/icons/typicons_dark/typ_document.svg +0 -4
  188. novelwriter/assets/icons/typicons_dark/typ_export-grey.svg +0 -4
  189. novelwriter/assets/icons/typicons_dark/typ_export.svg +0 -4
  190. novelwriter/assets/icons/typicons_dark/typ_eye.svg +0 -4
  191. novelwriter/assets/icons/typicons_dark/typ_flag.svg +0 -4
  192. novelwriter/assets/icons/typicons_dark/typ_folder-open.svg +0 -4
  193. novelwriter/assets/icons/typicons_dark/typ_folder.svg +0 -5
  194. novelwriter/assets/icons/typicons_dark/typ_globe-grey.svg +0 -4
  195. novelwriter/assets/icons/typicons_dark/typ_key.svg +0 -4
  196. novelwriter/assets/icons/typicons_dark/typ_lightbulb-full.svg +0 -4
  197. novelwriter/assets/icons/typicons_dark/typ_location.svg +0 -4
  198. novelwriter/assets/icons/typicons_dark/typ_media-pause-grey.svg +0 -4
  199. novelwriter/assets/icons/typicons_dark/typ_media-record-outline.svg +0 -4
  200. novelwriter/assets/icons/typicons_dark/typ_media-record.svg +0 -4
  201. novelwriter/assets/icons/typicons_dark/typ_minus.svg +0 -4
  202. novelwriter/assets/icons/typicons_dark/typ_pencil.svg +0 -5
  203. novelwriter/assets/icons/typicons_dark/typ_pin-outline.svg +0 -4
  204. novelwriter/assets/icons/typicons_dark/typ_pin.svg +0 -4
  205. novelwriter/assets/icons/typicons_dark/typ_plus.svg +0 -4
  206. novelwriter/assets/icons/typicons_dark/typ_puzzle-outline.svg +0 -4
  207. novelwriter/assets/icons/typicons_dark/typ_puzzle.svg +0 -4
  208. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +0 -4
  209. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +0 -4
  210. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +0 -4
  211. novelwriter/assets/icons/typicons_dark/typ_search.svg +0 -4
  212. novelwriter/assets/icons/typicons_dark/typ_star.svg +0 -4
  213. novelwriter/assets/icons/typicons_dark/typ_stopwatch-grey.svg +0 -4
  214. novelwriter/assets/icons/typicons_dark/typ_th-dot-menu.svg +0 -4
  215. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +0 -4
  216. novelwriter/assets/icons/typicons_dark/typ_th-list-grey.svg +0 -4
  217. novelwriter/assets/icons/typicons_dark/typ_th-list.svg +0 -9
  218. novelwriter/assets/icons/typicons_dark/typ_times.svg +0 -4
  219. novelwriter/assets/icons/typicons_dark/typ_trash.svg +0 -5
  220. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +0 -4
  221. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +0 -4
  222. novelwriter/assets/icons/typicons_dark/typ_user.svg +0 -5
  223. novelwriter/assets/icons/typicons_dark/typ_warning-full.svg +0 -4
  224. novelwriter/assets/icons/typicons_light/README.md +0 -29
  225. novelwriter/assets/icons/typicons_light/icons.conf +0 -134
  226. novelwriter/assets/icons/typicons_light/mixed_copy.svg +0 -4
  227. novelwriter/assets/icons/typicons_light/mixed_document-chapter.svg +0 -12
  228. novelwriter/assets/icons/typicons_light/mixed_document-new.svg +0 -6
  229. novelwriter/assets/icons/typicons_light/mixed_document-note.svg +0 -12
  230. novelwriter/assets/icons/typicons_light/mixed_document-scene.svg +0 -12
  231. novelwriter/assets/icons/typicons_light/mixed_document-section.svg +0 -12
  232. novelwriter/assets/icons/typicons_light/mixed_document-title.svg +0 -12
  233. novelwriter/assets/icons/typicons_light/mixed_edit.svg +0 -4
  234. novelwriter/assets/icons/typicons_light/mixed_import.svg +0 -5
  235. novelwriter/assets/icons/typicons_light/mixed_input-checked.svg +0 -5
  236. novelwriter/assets/icons/typicons_light/mixed_input-none.svg +0 -5
  237. novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg +0 -5
  238. novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +0 -6
  239. novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +0 -6
  240. novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +0 -6
  241. novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +0 -6
  242. novelwriter/assets/icons/typicons_light/mixed_search-replace.svg +0 -6
  243. novelwriter/assets/icons/typicons_light/mixed_size-height.svg +0 -6
  244. novelwriter/assets/icons/typicons_light/mixed_size-width.svg +0 -6
  245. novelwriter/assets/icons/typicons_light/nw_deco-h0.svg +0 -4
  246. novelwriter/assets/icons/typicons_light/nw_deco-h1.svg +0 -4
  247. novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +0 -4
  248. novelwriter/assets/icons/typicons_light/nw_deco-h2.svg +0 -4
  249. novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +0 -4
  250. novelwriter/assets/icons/typicons_light/nw_deco-h3.svg +0 -4
  251. novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +0 -4
  252. novelwriter/assets/icons/typicons_light/nw_deco-h4.svg +0 -4
  253. novelwriter/assets/icons/typicons_light/nw_deco-note.svg +0 -4
  254. novelwriter/assets/icons/typicons_light/nw_deco-noveltree-more.svg +0 -4
  255. novelwriter/assets/icons/typicons_light/nw_font.svg +0 -4
  256. novelwriter/assets/icons/typicons_light/nw_panel.svg +0 -4
  257. novelwriter/assets/icons/typicons_light/nw_quote.svg +0 -4
  258. novelwriter/assets/icons/typicons_light/nw_search-case.svg +0 -4
  259. novelwriter/assets/icons/typicons_light/nw_search-preserve.svg +0 -4
  260. novelwriter/assets/icons/typicons_light/nw_search-regex.svg +0 -4
  261. novelwriter/assets/icons/typicons_light/nw_search-word.svg +0 -4
  262. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +0 -4
  263. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +0 -6
  264. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +0 -4
  265. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +0 -6
  266. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +0 -7
  267. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +0 -4
  268. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +0 -6
  269. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +0 -7
  270. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +0 -7
  271. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +0 -7
  272. novelwriter/assets/icons/typicons_light/nw_toolbar.svg +0 -5
  273. novelwriter/assets/icons/typicons_light/typ_arrow-down-thick-grey.svg +0 -4
  274. novelwriter/assets/icons/typicons_light/typ_arrow-forward.svg +0 -4
  275. novelwriter/assets/icons/typicons_light/typ_arrow-maximise.svg +0 -4
  276. novelwriter/assets/icons/typicons_light/typ_arrow-minimise.svg +0 -4
  277. novelwriter/assets/icons/typicons_light/typ_arrow-repeat-grey.svg +0 -4
  278. novelwriter/assets/icons/typicons_light/typ_book-grey.svg +0 -4
  279. novelwriter/assets/icons/typicons_light/typ_book.svg +0 -6
  280. novelwriter/assets/icons/typicons_light/typ_bookmark.svg +0 -4
  281. novelwriter/assets/icons/typicons_light/typ_calendar.svg +0 -4
  282. novelwriter/assets/icons/typicons_light/typ_cancel-grey.svg +0 -4
  283. novelwriter/assets/icons/typicons_light/typ_cancel.svg +0 -4
  284. novelwriter/assets/icons/typicons_light/typ_chart-bar-grey.svg +0 -4
  285. novelwriter/assets/icons/typicons_light/typ_chevron-down.svg +0 -4
  286. novelwriter/assets/icons/typicons_light/typ_chevron-left.svg +0 -4
  287. novelwriter/assets/icons/typicons_light/typ_chevron-right.svg +0 -4
  288. novelwriter/assets/icons/typicons_light/typ_chevron-up.svg +0 -4
  289. novelwriter/assets/icons/typicons_light/typ_cog.svg +0 -4
  290. novelwriter/assets/icons/typicons_light/typ_delete-full.svg +0 -4
  291. novelwriter/assets/icons/typicons_light/typ_delete.svg +0 -4
  292. novelwriter/assets/icons/typicons_light/typ_directions-full.svg +0 -4
  293. novelwriter/assets/icons/typicons_light/typ_document-add.svg +0 -4
  294. novelwriter/assets/icons/typicons_light/typ_document-text.svg +0 -5
  295. novelwriter/assets/icons/typicons_light/typ_document.svg +0 -4
  296. novelwriter/assets/icons/typicons_light/typ_export-grey.svg +0 -4
  297. novelwriter/assets/icons/typicons_light/typ_export.svg +0 -4
  298. novelwriter/assets/icons/typicons_light/typ_eye.svg +0 -4
  299. novelwriter/assets/icons/typicons_light/typ_flag.svg +0 -4
  300. novelwriter/assets/icons/typicons_light/typ_folder-open.svg +0 -4
  301. novelwriter/assets/icons/typicons_light/typ_folder.svg +0 -5
  302. novelwriter/assets/icons/typicons_light/typ_globe-grey.svg +0 -4
  303. novelwriter/assets/icons/typicons_light/typ_key.svg +0 -4
  304. novelwriter/assets/icons/typicons_light/typ_lightbulb-full.svg +0 -4
  305. novelwriter/assets/icons/typicons_light/typ_location.svg +0 -4
  306. novelwriter/assets/icons/typicons_light/typ_media-pause-grey.svg +0 -4
  307. novelwriter/assets/icons/typicons_light/typ_media-record-outline.svg +0 -4
  308. novelwriter/assets/icons/typicons_light/typ_media-record.svg +0 -4
  309. novelwriter/assets/icons/typicons_light/typ_minus.svg +0 -4
  310. novelwriter/assets/icons/typicons_light/typ_pencil.svg +0 -5
  311. novelwriter/assets/icons/typicons_light/typ_pin-outline.svg +0 -4
  312. novelwriter/assets/icons/typicons_light/typ_pin.svg +0 -4
  313. novelwriter/assets/icons/typicons_light/typ_plus.svg +0 -4
  314. novelwriter/assets/icons/typicons_light/typ_puzzle-outline.svg +0 -4
  315. novelwriter/assets/icons/typicons_light/typ_puzzle.svg +0 -4
  316. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +0 -4
  317. novelwriter/assets/icons/typicons_light/typ_refresh.svg +0 -4
  318. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +0 -4
  319. novelwriter/assets/icons/typicons_light/typ_search.svg +0 -4
  320. novelwriter/assets/icons/typicons_light/typ_star.svg +0 -4
  321. novelwriter/assets/icons/typicons_light/typ_stopwatch-grey.svg +0 -4
  322. novelwriter/assets/icons/typicons_light/typ_th-dot-menu.svg +0 -4
  323. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +0 -4
  324. novelwriter/assets/icons/typicons_light/typ_th-list-grey.svg +0 -4
  325. novelwriter/assets/icons/typicons_light/typ_th-list.svg +0 -9
  326. novelwriter/assets/icons/typicons_light/typ_times.svg +0 -4
  327. novelwriter/assets/icons/typicons_light/typ_trash.svg +0 -5
  328. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +0 -4
  329. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +0 -4
  330. novelwriter/assets/icons/typicons_light/typ_user.svg +0 -5
  331. novelwriter/assets/icons/typicons_light/typ_warning-full.svg +0 -4
  332. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info}/entry_points.txt +0 -0
  333. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.dist-info/licenses}/LICENSE.md +0 -0
  334. {novelWriter-2.6.2.dist-info → novelwriter-2.7b1.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)
@@ -377,7 +370,7 @@ class GuiMain(QMainWindow):
377
370
  return True
378
371
 
379
372
  if not isYes:
380
- msgYes = SHARED.question("%s<br>%s" % (
373
+ msgYes = SHARED.question("{0}<br>{1}".format(
381
374
  self.tr("Close the current project?"),
382
375
  self.tr("Changes are saved automatically.")
383
376
  ))
@@ -537,7 +530,7 @@ class GuiMain(QMainWindow):
537
530
  ) -> bool:
538
531
  """Open a specific document, optionally at a given line."""
539
532
  if not (SHARED.hasProject and tHandle):
540
- logger.error("Nothing to open open")
533
+ logger.error("Nothing to open")
541
534
  return False
542
535
 
543
536
  if sTitle and tLine is None:
@@ -740,7 +733,6 @@ class GuiMain(QMainWindow):
740
733
 
741
734
  SHARED.project.index.rebuild()
742
735
  SHARED.project.tree.refreshAllItems()
743
- self.novelView.refreshTree()
744
736
 
745
737
  tEnd = time()
746
738
  self.mainStatus.setStatusMessage(
@@ -852,7 +844,7 @@ class GuiMain(QMainWindow):
852
844
 
853
845
  def closeMain(self) -> bool:
854
846
  """Save everything, and close novelWriter."""
855
- if SHARED.hasProject and CONFIG.askBeforeExit and not SHARED.question("%s<br>%s" % (
847
+ if SHARED.hasProject and CONFIG.askBeforeExit and not SHARED.question("{0}<br>{1}".format(
856
848
  self.tr("Do you want to exit novelWriter?"),
857
849
  self.tr("Changes are saved automatically.")
858
850
  )):
@@ -861,10 +853,10 @@ class GuiMain(QMainWindow):
861
853
  logger.info("Exiting novelWriter")
862
854
 
863
855
  if not SHARED.focusMode:
864
- CONFIG.setMainPanePos(self.splitMain.sizes())
865
- CONFIG.setOutlinePanePos(self.outlineView.splitSizes())
856
+ CONFIG.mainPanePos = self.splitMain.sizes()
857
+ CONFIG.outlinePanePos = self.outlineView.splitSizes()
866
858
  if self.docViewerPanel.isVisible():
867
- CONFIG.setViewPanePos(self.splitView.sizes())
859
+ CONFIG.viewPanePos = self.splitView.sizes()
868
860
 
869
861
  CONFIG.showViewerPanel = self.docViewerPanel.isVisible()
870
862
  wFull = Qt.WindowState.WindowFullScreen
@@ -1017,7 +1009,7 @@ class GuiMain(QMainWindow):
1017
1009
  fN = self.novelView.treeHasFocus()
1018
1010
 
1019
1011
  self._changeView(nwView.EDITOR)
1020
- if (vM and (vP and fP or vN and not fN)) or (not vM and vN):
1012
+ if (vM and ((vP and fP) or (vN and not fN))) or (not vM and vN):
1021
1013
  self._changeView(nwView.NOVEL)
1022
1014
  self.novelView.setTreeFocus()
1023
1015
  else:
@@ -1051,9 +1043,8 @@ class GuiMain(QMainWindow):
1051
1043
  SHARED.project.tree.refreshAllItems()
1052
1044
 
1053
1045
  if theme:
1054
- # We are doing this manually instead of connecting to
1055
- # paletteChanged since the processing order matters
1056
1046
  SHARED.theme.loadTheme()
1047
+ self.setPalette(QApplication.palette())
1057
1048
  self.docEditor.updateTheme()
1058
1049
  self.docViewer.updateTheme()
1059
1050
  self.docViewerPanel.updateTheme()
@@ -1064,10 +1055,11 @@ class GuiMain(QMainWindow):
1064
1055
  self.outlineView.updateTheme()
1065
1056
  self.itemDetails.updateTheme()
1066
1057
  self.mainStatus.updateTheme()
1058
+ SHARED.project.tree.refreshAllItems()
1067
1059
 
1068
1060
  if syntax:
1069
1061
  SHARED.theme.loadSyntax()
1070
- self.docEditor.updateSyntaxColours()
1062
+ self.docEditor.updateSyntaxColors()
1071
1063
 
1072
1064
  self.docEditor.initEditor()
1073
1065
  self.docViewer.initViewer()
@@ -1179,6 +1171,12 @@ class GuiMain(QMainWindow):
1179
1171
  )
1180
1172
  elif view == nwView.OUTLINE:
1181
1173
  self.mainStack.setCurrentWidget(self.outlineView)
1174
+
1175
+ # Set active status
1176
+ isMain = self.mainStack.currentWidget() == self.splitMain
1177
+ isNovel = self.projStack.currentWidget() == self.novelView
1178
+ self.novelView.setActive(isMain and isNovel)
1179
+
1182
1180
  return
1183
1181
 
1184
1182
  @pyqtSlot(nwDocAction)
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
@@ -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:
@@ -28,18 +28,18 @@ import logging
28
28
  from pathlib import Path
29
29
  from zipfile import ZipFile
30
30
 
31
- from PyQt5.QtCore import pyqtSlot
32
- from PyQt5.QtGui import QCloseEvent, QTextCursor
33
- from PyQt5.QtWidgets import (
31
+ from PyQt6.QtCore import pyqtSlot
32
+ from PyQt6.QtGui import QCloseEvent, QTextCursor
33
+ from PyQt6.QtWidgets import (
34
34
  QApplication, QDialogButtonBox, QFileDialog, QFrame, QHBoxLayout, QLabel,
35
35
  QLineEdit, QPlainTextEdit, QPushButton, QVBoxLayout, QWidget
36
36
  )
37
37
 
38
38
  from novelwriter import CONFIG, SHARED
39
- from novelwriter.common import cssCol, formatFileFilter, formatInt, getFileSize, openExternalPath
39
+ from novelwriter.common import formatFileFilter, formatInt, getFileSize, openExternalPath
40
40
  from novelwriter.error import formatException
41
41
  from novelwriter.extensions.modified import NIconToolButton, NNonBlockingDialog
42
- from novelwriter.types import QtDialogClose
42
+ from novelwriter.types import QtDialogClose, QtHexArgb
43
43
 
44
44
  logger = logging.getLogger(__name__)
45
45
 
@@ -57,12 +57,9 @@ class GuiDictionaries(NNonBlockingDialog):
57
57
  self._currDicts = set()
58
58
 
59
59
  iSz = SHARED.theme.baseIconSize
60
- iPx = CONFIG.pxInt(4)
61
- mPx = CONFIG.pxInt(8)
62
- sPx = CONFIG.pxInt(16)
63
60
 
64
- self.setMinimumWidth(CONFIG.pxInt(500))
65
- self.setMinimumHeight(CONFIG.pxInt(300))
61
+ self.setMinimumWidth(500)
62
+ self.setMinimumHeight(300)
66
63
 
67
64
  # Hunspell Dictionaries
68
65
  loUrl = "https://extensions.libreoffice.org"
@@ -78,13 +75,13 @@ class GuiDictionaries(NNonBlockingDialog):
78
75
  self.huBrowse = NIconToolButton(self, iSz, "browse")
79
76
  self.huBrowse.clicked.connect(self._doBrowseHunspell)
80
77
  self.huImport = QPushButton(self.tr("Add Dictionary"), self)
81
- self.huImport.setIcon(SHARED.theme.getIcon("add"))
78
+ self.huImport.setIcon(SHARED.theme.getIcon("add", "green"))
82
79
  self.huImport.clicked.connect(self._doImportHunspell)
83
80
 
84
81
  self.huPathBox = QHBoxLayout()
85
82
  self.huPathBox.addWidget(self.huInput)
86
83
  self.huPathBox.addWidget(self.huBrowse)
87
- self.huPathBox.setSpacing(iPx)
84
+ self.huPathBox.setSpacing(4)
88
85
  self.huAddBox = QHBoxLayout()
89
86
  self.huAddBox.addStretch(1)
90
87
  self.huAddBox.addWidget(self.huImport)
@@ -99,12 +96,11 @@ class GuiDictionaries(NNonBlockingDialog):
99
96
  self.inBox = QHBoxLayout()
100
97
  self.inBox.addWidget(self.inPath)
101
98
  self.inBox.addWidget(self.inBrowse)
102
- self.inBox.setSpacing(iPx)
99
+ self.inBox.setSpacing(4)
103
100
 
104
101
  # Info Box
105
102
  self.infoBox = QPlainTextEdit(self)
106
103
  self.infoBox.setReadOnly(True)
107
- self.infoBox.setFixedHeight(4*SHARED.theme.fontPixelSize)
108
104
  self.infoBox.setFrameStyle(QFrame.Shape.NoFrame)
109
105
 
110
106
  # Buttons
@@ -112,21 +108,16 @@ class GuiDictionaries(NNonBlockingDialog):
112
108
  self.buttonBox.rejected.connect(self.reject)
113
109
 
114
110
  # Assemble
115
- self.innerBox = QVBoxLayout()
116
- self.innerBox.addWidget(self.huInfo)
117
- self.innerBox.addLayout(self.huPathBox)
118
- self.innerBox.addLayout(self.huAddBox)
119
- self.innerBox.addSpacing(mPx)
120
- self.innerBox.addWidget(self.inInfo)
121
- self.innerBox.addLayout(self.inBox)
122
- self.innerBox.addWidget(self.infoBox)
123
- self.innerBox.setSpacing(iPx)
124
-
125
111
  self.outerBox = QVBoxLayout()
126
- self.outerBox.addLayout(self.innerBox, 0)
127
- self.outerBox.addStretch(1)
112
+ self.outerBox.addWidget(self.huInfo, 0)
113
+ self.outerBox.addLayout(self.huPathBox, 0)
114
+ self.outerBox.addLayout(self.huAddBox, 0)
115
+ self.outerBox.addSpacing(8)
116
+ self.outerBox.addWidget(self.inInfo, 0)
117
+ self.outerBox.addLayout(self.inBox, 0)
118
+ self.outerBox.addWidget(self.infoBox, 1)
119
+ self.outerBox.addSpacing(8)
128
120
  self.outerBox.addWidget(self.buttonBox, 0)
129
- self.outerBox.setSpacing(sPx)
130
121
 
131
122
  self.setLayout(self.outerBox)
132
123
 
@@ -251,8 +242,8 @@ class GuiDictionaries(NNonBlockingDialog):
251
242
  cursor.movePosition(QTextCursor.MoveOperation.End)
252
243
  if cursor.position() > 0:
253
244
  cursor.insertText("\n")
254
- textCol = cssCol(SHARED.theme.errorText if err else self.palette().text().color())
255
- cursor.insertHtml(f"<font style='color: {textCol}'>{text}</font>")
245
+ textCol = SHARED.theme.errorText if err else self.palette().text().color()
246
+ cursor.insertHtml(f"<font style='color: {textCol.name(QtHexArgb)}'>{text}</font>")
256
247
  cursor.movePosition(QTextCursor.MoveOperation.End)
257
248
  cursor.deleteChar()
258
249
  self.infoBox.setTextCursor(cursor)
@@ -26,8 +26,8 @@ from __future__ import annotations
26
26
  import logging
27
27
  import random
28
28
 
29
- from PyQt5.QtCore import pyqtSlot
30
- from PyQt5.QtWidgets import (
29
+ from PyQt6.QtCore import pyqtSlot
30
+ from PyQt6.QtWidgets import (
31
31
  QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel, QSpinBox, QVBoxLayout,
32
32
  QWidget
33
33
  )
@@ -52,18 +52,15 @@ class GuiLipsum(NDialog):
52
52
 
53
53
  self._lipsumText = ""
54
54
 
55
- vSp = CONFIG.pxInt(4)
56
- nPx = CONFIG.pxInt(64)
57
-
58
55
  self.innerBox = QHBoxLayout()
59
- self.innerBox.setSpacing(CONFIG.pxInt(16))
56
+ self.innerBox.setSpacing(16)
60
57
 
61
58
  # Icon
62
59
  self.docIcon = QLabel(self)
63
- self.docIcon.setPixmap(SHARED.theme.getPixmap("proj_document", (nPx, nPx)))
60
+ self.docIcon.setPixmap(SHARED.theme.getPixmap("text", (64, 64), "blue"))
64
61
 
65
62
  self.leftBox = QVBoxLayout()
66
- self.leftBox.setSpacing(vSp)
63
+ self.leftBox.setSpacing(4)
67
64
  self.leftBox.addWidget(self.docIcon)
68
65
  self.leftBox.addStretch(1)
69
66
  self.innerBox.addLayout(self.leftBox)
@@ -87,7 +84,7 @@ class GuiLipsum(NDialog):
87
84
  self.formBox.addWidget(self.paraCount, 1, 1, 1, 1, QtAlignRight)
88
85
  self.formBox.addWidget(self.randLabel, 2, 0, 1, 1, QtAlignLeft)
89
86
  self.formBox.addWidget(self.randSwitch, 2, 1, 1, 1, QtAlignRight)
90
- self.formBox.setVerticalSpacing(vSp)
87
+ self.formBox.setVerticalSpacing(4)
91
88
  self.formBox.setRowStretch(3, 1)
92
89
  self.innerBox.addLayout(self.formBox)
93
90
 
@@ -96,17 +93,19 @@ class GuiLipsum(NDialog):
96
93
  self.buttonBox.rejected.connect(self.reject)
97
94
 
98
95
  self.btnClose = self.buttonBox.addButton(QtDialogClose)
99
- self.btnClose.setAutoDefault(False)
96
+ if self.btnClose:
97
+ self.btnClose.setAutoDefault(False)
100
98
 
101
99
  self.btnInsert = self.buttonBox.addButton(self.tr("Insert"), QtRoleAction)
102
- self.btnInsert.clicked.connect(self._doInsert)
103
- self.btnInsert.setAutoDefault(False)
100
+ if self.btnInsert:
101
+ self.btnInsert.clicked.connect(self._doInsert)
102
+ self.btnInsert.setAutoDefault(False)
104
103
 
105
104
  # Assemble
106
105
  self.outerBox = QVBoxLayout()
107
106
  self.outerBox.addLayout(self.innerBox)
108
107
  self.outerBox.addWidget(self.buttonBox)
109
- self.outerBox.setSpacing(CONFIG.pxInt(16))
108
+ self.outerBox.setSpacing(16)
110
109
  self.setLayout(self.outerBox)
111
110
 
112
111
  logger.debug("Ready: GuiLipsum")
@@ -125,10 +124,10 @@ class GuiLipsum(NDialog):
125
124
  @classmethod
126
125
  def getLipsum(cls, parent: QWidget) -> str:
127
126
  """Pop the dialog and return the lipsum text."""
128
- cls = GuiLipsum(parent)
129
- cls.exec()
130
- text = cls.lipsumText
131
- cls.softDelete()
127
+ dialog = cls(parent)
128
+ dialog.exec()
129
+ text = dialog.lipsumText
130
+ dialog.softDelete()
132
131
  return text
133
132
 
134
133
  ##