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
@@ -0,0 +1,223 @@
1
+ """
2
+ novelWriter – Novel Model
3
+ =========================
4
+
5
+ File History:
6
+ Created: 2025-02-22 [2.7b1] NovelModel
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 typing import TYPE_CHECKING
29
+
30
+ from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
31
+ from PyQt6.QtGui import QIcon, QPixmap
32
+
33
+ from novelwriter import SHARED
34
+ from novelwriter.constants import nwKeyWords, nwLabels, nwStyles, trConst
35
+ from novelwriter.enum import nwNovelExtra
36
+ from novelwriter.types import QtAlignRight
37
+
38
+ if TYPE_CHECKING:
39
+ from novelwriter.core.indexdata import IndexHeading, IndexNode
40
+
41
+ logger = logging.getLogger(__name__)
42
+
43
+ C_FACTOR = 0x0100
44
+
45
+ R_TEXT = Qt.ItemDataRole.DisplayRole
46
+ R_ICON = Qt.ItemDataRole.DecorationRole
47
+ R_ALIGN = Qt.ItemDataRole.TextAlignmentRole
48
+ R_TIP = Qt.ItemDataRole.ToolTipRole
49
+ R_HANDLE = 0xff01
50
+ R_KEY = 0xff02
51
+
52
+ T_NodeData = str | QIcon | QPixmap | Qt.AlignmentFlag | None
53
+
54
+
55
+ class NovelModel(QAbstractTableModel):
56
+
57
+ __slots__ = ("_columns", "_extraKey", "_extraLabel", "_more", "_rows")
58
+
59
+ def __init__(self) -> None:
60
+ super().__init__()
61
+ self._rows: list[dict[int, T_NodeData]] = []
62
+ self._more = SHARED.theme.getIcon("more_arrow")
63
+ self._columns = 3
64
+ self._extraKey = ""
65
+ self._extraLabel = ""
66
+ return
67
+
68
+ def __del__(self) -> None: # pragma: no cover
69
+ logger.debug("Delete: NovelModel")
70
+ return
71
+
72
+ ##
73
+ # Properties
74
+ ##
75
+
76
+ @property
77
+ def columns(self) -> int:
78
+ """Return the number of columns."""
79
+ return self._columns
80
+
81
+ ##
82
+ # Setters
83
+ ##
84
+
85
+ def setExtraColumn(self, extra: nwNovelExtra) -> None:
86
+ """Set extra data column settings."""
87
+ match extra:
88
+ case nwNovelExtra.HIDDEN:
89
+ self._columns = 3
90
+ self._extraKey = ""
91
+ self._extraLabel = ""
92
+ case nwNovelExtra.POV:
93
+ self._columns = 4
94
+ self._extraKey = nwKeyWords.POV_KEY
95
+ self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.POV_KEY])
96
+ case nwNovelExtra.FOCUS:
97
+ self._columns = 4
98
+ self._extraKey = nwKeyWords.FOCUS_KEY
99
+ self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.FOCUS_KEY])
100
+ case nwNovelExtra.PLOT:
101
+ self._columns = 4
102
+ self._extraKey = nwKeyWords.PLOT_KEY
103
+ self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
104
+ return
105
+
106
+ ##
107
+ # Model Interface
108
+ ##
109
+
110
+ def rowCount(self, index: QModelIndex) -> int:
111
+ """Return the number of rows."""
112
+ return len(self._rows)
113
+
114
+ def columnCount(self, index: QModelIndex) -> int:
115
+ """Return the number of columns."""
116
+ return self._columns
117
+
118
+ def data(self, index: QModelIndex, role: Qt.ItemDataRole) -> T_NodeData:
119
+ """Return display data for a node."""
120
+ try:
121
+ return self._rows[index.row()].get(C_FACTOR*index.column() | role)
122
+ except Exception:
123
+ logger.error("Novel model index is inconsistent")
124
+ return None
125
+
126
+ def handle(self, index: QModelIndex) -> str | None:
127
+ """Return item handle for the row."""
128
+ try:
129
+ return self._rows[index.row()].get(R_HANDLE) # type: ignore
130
+ except Exception:
131
+ logger.error("Novel model index is inconsistent")
132
+ return None
133
+
134
+ def key(self, index: QModelIndex) -> str | None:
135
+ """Return item handle for the row."""
136
+ try:
137
+ return self._rows[index.row()].get(R_KEY) # type: ignore
138
+ except Exception:
139
+ logger.error("Novel model index is inconsistent")
140
+ return None
141
+
142
+ ##
143
+ # Data Methods
144
+ ##
145
+
146
+ def clear(self) -> None:
147
+ """Clear the model."""
148
+ self._rows.clear()
149
+ return
150
+
151
+ def append(self, node: IndexNode) -> None:
152
+ """Append a node to the model."""
153
+ handle = node.handle
154
+ for key, head in node.items():
155
+ if key != "T0000":
156
+ self._rows.append(self._generateEntry(handle, key, head))
157
+ return
158
+
159
+ def refresh(self, node: IndexNode) -> bool:
160
+ """Refresh an index node."""
161
+ handle = node.handle
162
+ current = []
163
+ for i, row in enumerate(self._rows):
164
+ if row.get(R_HANDLE) == handle:
165
+ current.append(i)
166
+
167
+ if current == []:
168
+ logger.warning("No novel model entries for '%s'", handle)
169
+ return False
170
+
171
+ cols = self._columns - 1
172
+ first = current[0]
173
+ last = current[-1]
174
+
175
+ remains = []
176
+ for key, head in node.items():
177
+ if key != "T0000":
178
+ if current:
179
+ j = current.pop(0)
180
+ self._rows[j] = self._generateEntry(handle, key, head)
181
+ else:
182
+ remains.append((key, head))
183
+
184
+ self.dataChanged.emit(self.createIndex(first, 0), self.createIndex(last, cols))
185
+
186
+ if remains:
187
+ # Inserting is safe for out of bounds indices
188
+ self.beginInsertRows(QModelIndex(), last, last + len(remains) - 1)
189
+ for k, (key, head) in enumerate(remains, last + 1):
190
+ self._rows.insert(k, self._generateEntry(handle, key, head))
191
+ self.endInsertRows()
192
+ elif current:
193
+ # Deleting ranges are safe for out of bounds indices
194
+ self.beginRemoveRows(QModelIndex(), current[0], current[-1])
195
+ del self._rows[current[0]:current[-1] + 1]
196
+ self.endRemoveRows()
197
+
198
+ return True
199
+
200
+ ##
201
+ # Internal Functions
202
+ ##
203
+
204
+ def _generateEntry(self, handle: str, key: str, head: IndexHeading) -> dict[int, T_NodeData]:
205
+ """Generate a cache entry."""
206
+ iLevel = nwStyles.H_LEVEL.get(head.level, 0)
207
+ data = {}
208
+ data[C_FACTOR*0 | R_TIP] = head.title
209
+ data[C_FACTOR*0 | R_TEXT] = head.title
210
+ data[C_FACTOR*0 | R_ICON] = SHARED.theme.getHeaderDecoration(iLevel)
211
+ data[C_FACTOR*1 | R_TEXT] = f"{head.mainCount:n}"
212
+ data[C_FACTOR*1 | R_ALIGN] = QtAlignRight
213
+ if self._columns == 3:
214
+ data[C_FACTOR*2 | R_ICON] = self._more
215
+ else:
216
+ if self._extraKey and (refs := head.getReferencesByKeyword(self._extraKey)):
217
+ text = ", ".join(refs)
218
+ data[C_FACTOR*2 | R_TEXT] = text
219
+ data[C_FACTOR*2 | R_TIP] = f"<b>{self._extraLabel}:</b> {text}"
220
+ data[C_FACTOR*3 | R_ICON] = self._more
221
+ data[R_HANDLE] = handle
222
+ data[R_KEY] = key
223
+ return data
@@ -35,7 +35,7 @@ from novelwriter.common import checkBool, checkFloat, checkInt, checkString, jso
35
35
  from novelwriter.constants import nwFiles
36
36
  from novelwriter.error import logException
37
37
 
38
- if TYPE_CHECKING: # pragma: no cover
38
+ if TYPE_CHECKING:
39
39
  from novelwriter.core.project import NWProject
40
40
 
41
41
  logger = logging.getLogger(__name__)
@@ -32,7 +32,7 @@ from pathlib import Path
32
32
  from time import time
33
33
  from typing import TYPE_CHECKING
34
34
 
35
- from PyQt5.QtCore import QCoreApplication
35
+ from PyQt6.QtCore import QCoreApplication
36
36
 
37
37
  from novelwriter import CONFIG, SHARED, __hexversion__, __version__
38
38
  from novelwriter.common import (
@@ -40,7 +40,7 @@ from novelwriter.common import (
40
40
  makeFileNameSafe, minmax
41
41
  )
42
42
  from novelwriter.constants import nwLabels, trConst
43
- from novelwriter.core.index import NWIndex
43
+ from novelwriter.core.index import Index
44
44
  from novelwriter.core.options import OptionState
45
45
  from novelwriter.core.projectdata import NWProjectData
46
46
  from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
@@ -50,8 +50,7 @@ from novelwriter.core.tree import NWTree
50
50
  from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
51
51
  from novelwriter.error import logException
52
52
 
53
- if TYPE_CHECKING: # pragma: no cover
54
- # Requires Python 3.10
53
+ if TYPE_CHECKING:
55
54
  from novelwriter.core.status import T_StatusKind, T_UpdateEntry
56
55
 
57
56
  logger = logging.getLogger(__name__)
@@ -68,8 +67,8 @@ class NWProjectState(Enum):
68
67
  class NWProject:
69
68
 
70
69
  __slots__ = (
71
- "_options", "_storage", "_data", "_tree", "_index", "_session",
72
- "_langData", "_changed", "_valid", "_state", "tr",
70
+ "_changed", "_data", "_index", "_langData", "_options", "_session",
71
+ "_state", "_storage", "_tree", "_valid", "tr",
73
72
  )
74
73
 
75
74
  def __init__(self) -> None:
@@ -79,7 +78,7 @@ class NWProject:
79
78
  self._storage = NWStorage(self) # The project storage handler
80
79
  self._data = NWProjectData(self) # The project settings
81
80
  self._tree = NWTree(self) # The project tree
82
- self._index = NWIndex(self) # The project index
81
+ self._index = Index(self) # The project index
83
82
  self._session = NWSessionLog(self) # The session record
84
83
 
85
84
  # Project Status
@@ -126,7 +125,7 @@ class NWProject:
126
125
  return self._tree
127
126
 
128
127
  @property
129
- def index(self) -> NWIndex:
128
+ def index(self) -> Index:
130
129
  return self._index
131
130
 
132
131
  @property
@@ -561,13 +560,13 @@ class NWProject:
561
560
 
562
561
  def _loadProjectLocalisation(self) -> bool:
563
562
  """Load the language data for the current project language."""
564
- if self._data.language is None or CONFIG._nwLangPath is None:
563
+ if self._data.language is None or CONFIG.nwLangPath is None:
565
564
  self._langData = {}
566
565
  return False
567
566
 
568
- langFile = Path(CONFIG._nwLangPath) / f"project_{self._data.language}.json"
567
+ langFile = Path(CONFIG.nwLangPath) / f"project_{self._data.language}.json"
569
568
  if not langFile.is_file():
570
- langFile = Path(CONFIG._nwLangPath) / "project_en_GB.json"
569
+ langFile = Path(CONFIG.nwLangPath) / "project_en_GB.json"
571
570
 
572
571
  try:
573
572
  with open(langFile, mode="r", encoding="utf-8") as inFile:
@@ -34,7 +34,7 @@ from novelwriter.common import (
34
34
  )
35
35
  from novelwriter.core.status import NWStatus
36
36
 
37
- if TYPE_CHECKING: # pragma: no cover
37
+ if TYPE_CHECKING:
38
38
  from novelwriter.core.project import NWProject
39
39
 
40
40
  logger = logging.getLogger(__name__)
@@ -69,10 +69,10 @@ class NWProjectData:
69
69
  self._initCounts = [0, 0]
70
70
  self._currCounts = [0, 0]
71
71
  self._lastHandle: dict[str, str | None] = {
72
- "editor": None,
73
- "viewer": None,
74
- "novelTree": None,
75
- "outline": None,
72
+ "editor": None,
73
+ "viewer": None,
74
+ "novel": None,
75
+ "outline": None,
76
76
  }
77
77
  self._autoReplace: dict[str, str] = {}
78
78
  self._titleFormat: dict[str, str] = {
@@ -39,7 +39,7 @@ from novelwriter.common import (
39
39
  hexToInt, simplified, xmlIndent, yesNo
40
40
  )
41
41
 
42
- if TYPE_CHECKING: # pragma: no cover
42
+ if TYPE_CHECKING:
43
43
  from novelwriter.core.projectdata import NWProjectData
44
44
  from novelwriter.core.status import NWStatus
45
45
 
@@ -26,7 +26,6 @@ from __future__ import annotations
26
26
  import json
27
27
  import logging
28
28
 
29
- from collections.abc import Iterable
30
29
  from pathlib import Path
31
30
  from time import time
32
31
  from typing import TYPE_CHECKING
@@ -35,7 +34,9 @@ from novelwriter.common import formatTimeStamp
35
34
  from novelwriter.constants import nwFiles
36
35
  from novelwriter.error import logException
37
36
 
38
- if TYPE_CHECKING: # pragma: no cover
37
+ if TYPE_CHECKING:
38
+ from collections.abc import Iterable
39
+
39
40
  from novelwriter.core.project import NWProject
40
41
 
41
42
  logger = logging.getLogger(__name__)
@@ -27,16 +27,17 @@ from __future__ import annotations
27
27
  import json
28
28
  import logging
29
29
 
30
- from collections.abc import Iterator
31
30
  from pathlib import Path
32
31
  from typing import TYPE_CHECKING
33
32
 
34
- from PyQt5.QtCore import QLocale
33
+ from PyQt6.QtCore import QLocale
35
34
 
36
35
  from novelwriter.constants import nwFiles
37
36
  from novelwriter.error import logException
38
37
 
39
- if TYPE_CHECKING: # pragma: no cover
38
+ if TYPE_CHECKING:
39
+ from collections.abc import Iterator
40
+
40
41
  from novelwriter.core.project import NWProject
41
42
 
42
43
  logger = logging.getLogger(__name__)
@@ -28,19 +28,18 @@ import dataclasses
28
28
  import logging
29
29
  import random
30
30
 
31
- from collections.abc import Iterable
32
- from typing import TYPE_CHECKING, Literal
31
+ from typing import TYPE_CHECKING, Literal, TypeGuard
33
32
 
34
- from PyQt5.QtCore import QPointF, Qt
35
- from PyQt5.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygonF
33
+ from PyQt6.QtCore import QPointF, Qt
34
+ from PyQt6.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygonF
36
35
 
37
36
  from novelwriter import SHARED
38
37
  from novelwriter.common import simplified
39
38
  from novelwriter.enum import nwStatusShape
40
39
  from novelwriter.types import QtPaintAntiAlias, QtTransparent
41
40
 
42
- if TYPE_CHECKING: # pragma: no cover
43
- from typing import TypeGuard # Requires Python 3.10
41
+ if TYPE_CHECKING:
42
+ from collections.abc import Iterable
44
43
 
45
44
  logger = logging.getLogger(__name__)
46
45
 
@@ -57,18 +56,16 @@ class StatusEntry:
57
56
  @classmethod
58
57
  def duplicate(cls, source: StatusEntry) -> StatusEntry:
59
58
  """Create a deep copy of the source object."""
60
- cls = dataclasses.replace(source)
61
- cls.color = QColor(source.color)
62
- cls.icon = QIcon(source.icon)
63
- return cls
59
+ status = dataclasses.replace(source)
60
+ status.color = QColor(source.color)
61
+ status.icon = QIcon(source.icon)
62
+ return status
64
63
 
65
64
 
66
65
  NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
67
66
 
68
- if TYPE_CHECKING: # pragma: no cover
69
- # Requires Python 3.10
70
- T_UpdateEntry = list[tuple[str | None, StatusEntry]]
71
- T_StatusKind = Literal["s", "i"]
67
+ T_UpdateEntry = list[tuple[str | None, StatusEntry]]
68
+ T_StatusKind = Literal["s", "i"]
72
69
 
73
70
 
74
71
  class NWStatus:
@@ -76,7 +73,7 @@ class NWStatus:
76
73
  STATUS = "s"
77
74
  IMPORT = "i"
78
75
 
79
- __slots__ = ("_store", "_default", "_prefix", "_height")
76
+ __slots__ = ("_default", "_height", "_prefix", "_store")
80
77
 
81
78
  def __init__(self, prefix: T_StatusKind) -> None:
82
79
  self._store: dict[str, StatusEntry] = {}
@@ -40,7 +40,7 @@ from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
40
40
  from novelwriter.core.spellcheck import UserDictionary
41
41
  from novelwriter.error import logException
42
42
 
43
- if TYPE_CHECKING: # pragma: no cover
43
+ if TYPE_CHECKING:
44
44
  from novelwriter.core.project import NWProject
45
45
 
46
46
  logger = logging.getLogger(__name__)
novelwriter/core/tree.py CHANGED
@@ -27,20 +27,21 @@ from __future__ import annotations
27
27
  import logging
28
28
  import random
29
29
 
30
- from collections.abc import Iterable, Iterator
31
30
  from pathlib import Path
32
31
  from typing import TYPE_CHECKING, Literal, overload
33
32
 
34
- from PyQt5.QtCore import QModelIndex
33
+ from PyQt6.QtCore import QModelIndex
35
34
 
36
35
  from novelwriter import SHARED
37
- from novelwriter.constants import nwFiles, nwLabels, trConst
36
+ from novelwriter.constants import nwFiles, nwLabels, nwStyles, trConst
38
37
  from novelwriter.core.item import NWItem
39
38
  from novelwriter.core.itemmodel import ProjectModel, ProjectNode
40
39
  from novelwriter.enum import nwChange, nwItemClass, nwItemLayout, nwItemType
41
40
  from novelwriter.error import logException
42
41
 
43
- if TYPE_CHECKING: # pragma: no cover
42
+ if TYPE_CHECKING:
43
+ from collections.abc import Iterable, Iterator
44
+
44
45
  from novelwriter.core.project import NWProject
45
46
 
46
47
  logger = logging.getLogger(__name__)
@@ -60,7 +61,7 @@ class NWTree:
60
61
  also used for file names.
61
62
  """
62
63
 
63
- __slots__ = ("_project", "_model", "_items", "_nodes", "_trash")
64
+ __slots__ = ("_items", "_model", "_nodes", "_project", "_ready", "_trash")
64
65
 
65
66
  def __init__(self, project: NWProject) -> None:
66
67
  self._project = project
@@ -68,6 +69,7 @@ class NWTree:
68
69
  self._items: dict[str, NWItem] = {}
69
70
  self._nodes: dict[str, ProjectNode] = {}
70
71
  self._trash = None
72
+ self._ready = False
71
73
  logger.debug("Ready: NWTree")
72
74
  return
73
75
 
@@ -106,6 +108,11 @@ class NWTree:
106
108
  # Properties
107
109
  ##
108
110
 
111
+ @property
112
+ def project(self) -> NWProject:
113
+ """Return the parent project."""
114
+ return self._project
115
+
109
116
  @property
110
117
  def trash(self) -> ProjectNode | None:
111
118
  """Return trash node, if it exists."""
@@ -249,11 +256,37 @@ class NWTree:
249
256
  logger.error("Not all items could be added to project tree")
250
257
 
251
258
  self._trash = self._getTrashNode()
259
+ self._ready = True
252
260
  self._model.endInsertRows()
253
261
  self._model.layoutChanged.emit()
254
262
 
255
263
  return
256
264
 
265
+ def pickParent(self, sNode: ProjectNode, hLevel: int, isNote: bool) -> tuple[str | None, int]:
266
+ """Pick an appropriate parent handle for adding a new item."""
267
+ if sNode.item.isFolderType() or sNode.item.isRootType():
268
+ # Always add as a direct child of folders
269
+ return sNode.item.itemHandle, sNode.childCount()
270
+
271
+ pNode = sNode.parent()
272
+ pLevel = nwStyles.H_LEVEL.get(pNode.item.mainHeading, 0) if pNode else 0
273
+
274
+ # Notes are treated as H0, and scenes and sections both as H3
275
+ sLevel = min(0 if isNote else nwStyles.H_LEVEL.get(sNode.item.mainHeading, 0), 3)
276
+
277
+ if pNode and pNode.item.isFileType() and pLevel >= hLevel and sLevel > hLevel:
278
+ # If the selected item is a smaller heading and the parent heading
279
+ # is equal or larger, we make it a sibling of the parent (See #2260)
280
+ return pNode.item.itemParent, pNode.row() + 1
281
+
282
+ if sNode.childCount() > 0 and (0 < sLevel < hLevel or isNote):
283
+ # If the selected item already has child nodes and has a larger
284
+ # heading or is a note, we make the new item a child
285
+ return sNode.item.itemHandle, sNode.childCount()
286
+
287
+ # The default behaviour is to make the new item a sibling
288
+ return sNode.item.itemParent, sNode.row() + 1
289
+
257
290
  def refreshItems(self, items: list[str]) -> None:
258
291
  """Refresh these items on the GUI. If they are an ordered range,
259
292
  also set the isRange flag to True.
@@ -278,6 +311,12 @@ class NWTree:
278
311
  self._model.layoutChanged.emit()
279
312
  return
280
313
 
314
+ def novelStructureChanged(self, tHandle: str) -> None:
315
+ """Emit a novel structure change signal."""
316
+ if self._ready:
317
+ SHARED.novelStructureChanged.emit(tHandle)
318
+ return
319
+
281
320
  def checkConsistency(self, prefix: str) -> tuple[int, int]:
282
321
  """Check the project tree consistency. Also check the content
283
322
  folder and add back files that were discovered but were not
@@ -388,7 +427,7 @@ class NWTree:
388
427
 
389
428
  def checkType(self, tHandle: str, itemType: nwItemType) -> bool:
390
429
  """Check if item exists and is of the specified item type."""
391
- if tItem := self.__getitem__(tHandle):
430
+ if tItem := self[tHandle]:
392
431
  return tItem.itemType == itemType
393
432
  return False
394
433
 
@@ -406,8 +445,7 @@ class NWTree:
406
445
  node = parent
407
446
  else:
408
447
  return path
409
- else:
410
- logger.error("Max project tree depth reached")
448
+ logger.error("Max project tree depth reached")
411
449
  return path
412
450
 
413
451
  def subTree(self, tHandle: str) -> list[str]:
@@ -25,17 +25,21 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
- from PyQt5.QtGui import QCloseEvent, QColor
29
- from PyQt5.QtWidgets import (
28
+ from typing import TYPE_CHECKING
29
+
30
+ from PyQt6.QtWidgets import (
30
31
  QDialogButtonBox, QHBoxLayout, QLabel, QTextBrowser, QVBoxLayout, QWidget
31
32
  )
32
33
 
33
34
  from novelwriter import CONFIG, SHARED
34
- from novelwriter.common import cssCol, readTextFile
35
- from novelwriter.extensions.configlayout import NColourLabel
35
+ from novelwriter.common import readTextFile
36
+ from novelwriter.extensions.configlayout import NColorLabel
36
37
  from novelwriter.extensions.modified import NDialog
37
38
  from novelwriter.extensions.versioninfo import VersionInfoWidget
38
- from novelwriter.types import QtAlignRightTop, QtDialogClose
39
+ from novelwriter.types import QtAlignRightTop, QtDialogClose, QtHexArgb
40
+
41
+ if TYPE_CHECKING:
42
+ from PyQt6.QtGui import QCloseEvent
39
43
 
40
44
  logger = logging.getLogger(__name__)
41
45
 
@@ -49,19 +53,13 @@ class GuiAbout(NDialog):
49
53
  self.setObjectName("GuiAbout")
50
54
 
51
55
  self.setWindowTitle(self.tr("About novelWriter"))
52
- self.resize(CONFIG.pxInt(700), CONFIG.pxInt(500))
53
-
54
- hA = CONFIG.pxInt(8)
55
- hB = CONFIG.pxInt(16)
56
- nwH = CONFIG.pxInt(36)
57
- nwPx = CONFIG.pxInt(128)
56
+ self.resize(700, 500)
58
57
 
59
58
  # Logo and Banner
60
- self.nwImage = SHARED.theme.loadDecoration("nw-text", h=nwH)
61
- self.bgColor = QColor(255, 255, 255) if SHARED.theme.isLightTheme else QColor(54, 54, 54)
59
+ self.nwImage = SHARED.theme.getDecoration("nw-text", h=36)
62
60
 
63
61
  self.nwLogo = QLabel(self)
64
- self.nwLogo.setPixmap(SHARED.theme.getPixmap("novelwriter", (nwPx, nwPx)))
62
+ self.nwLogo.setPixmap(SHARED.theme.getPixmap("novelwriter", (128, 128)))
65
63
 
66
64
  self.nwLabel = QLabel(self)
67
65
  self.nwLabel.setPixmap(self.nwImage)
@@ -74,14 +72,13 @@ class GuiAbout(NDialog):
74
72
  self.nwLicence.setOpenExternalLinks(True)
75
73
 
76
74
  # Credits
77
- self.lblCredits = NColourLabel(
75
+ self.lblCredits = NColorLabel(
78
76
  self.tr("Credits"), self, scale=1.6, bold=True
79
77
  )
80
78
 
81
79
  self.txtCredits = QTextBrowser(self)
82
80
  self.txtCredits.setOpenExternalLinks(True)
83
- self.txtCredits.document().setDocumentMargin(0)
84
- self.txtCredits.setViewportMargins(0, hA, hA, 0)
81
+ self.txtCredits.setViewportMargins(0, 8, 8, 0)
85
82
 
86
83
  # Buttons
87
84
  self.btnBox = QDialogButtonBox(QtDialogClose, self)
@@ -89,15 +86,15 @@ class GuiAbout(NDialog):
89
86
 
90
87
  # Assemble
91
88
  self.innerBox = QVBoxLayout()
92
- self.innerBox.addSpacing(hB)
89
+ self.innerBox.addSpacing(16)
93
90
  self.innerBox.addWidget(self.nwLabel)
94
91
  self.innerBox.addWidget(self.nwInfo)
95
- self.innerBox.addSpacing(hA)
92
+ self.innerBox.addSpacing(8)
96
93
  self.innerBox.addWidget(self.nwLicence)
97
- self.innerBox.addSpacing(hA)
94
+ self.innerBox.addSpacing(8)
98
95
  self.innerBox.addWidget(self.lblCredits)
99
96
  self.innerBox.addWidget(self.txtCredits)
100
- self.innerBox.addSpacing(hB)
97
+ self.innerBox.addSpacing(16)
101
98
  self.innerBox.addWidget(self.btnBox)
102
99
 
103
100
  self.outerBox = QHBoxLayout()
@@ -142,7 +139,7 @@ class GuiAbout(NDialog):
142
139
 
143
140
  def _setStyleSheet(self) -> None:
144
141
  """Set stylesheet text document."""
145
- baseCol = cssCol(self.palette().window().color())
142
+ baseCol = self.palette().window().color().name(QtHexArgb)
146
143
  self.txtCredits.setStyleSheet(
147
144
  f"QTextBrowser {{border: none; background: {baseCol};}} "
148
145
  )