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
@@ -32,15 +32,15 @@ import logging
32
32
 
33
33
  from enum import Enum
34
34
 
35
- from PyQt5.QtCore import QModelIndex, QPoint, Qt, pyqtSignal, pyqtSlot
36
- from PyQt5.QtGui import QIcon, QMouseEvent, QPainter, QPalette
37
- from PyQt5.QtWidgets import (
38
- QAbstractItemView, QAction, QFrame, QHBoxLayout, QLabel, QMenu, QShortcut,
35
+ from PyQt6.QtCore import QModelIndex, QPoint, Qt, pyqtSignal, pyqtSlot
36
+ from PyQt6.QtGui import QAction, QIcon, QMouseEvent, QPainter, QPalette, QShortcut
37
+ from PyQt6.QtWidgets import (
38
+ QAbstractItemView, QFrame, QHBoxLayout, QLabel, QMenu,
39
39
  QStyleOptionViewItem, QTreeView, QVBoxLayout, QWidget
40
40
  )
41
41
 
42
42
  from novelwriter import CONFIG, SHARED
43
- from novelwriter.common import qtLambda
43
+ from novelwriter.common import qtAddAction, qtAddMenu, qtLambda
44
44
  from novelwriter.constants import nwLabels, nwStyles, nwUnicode, trConst
45
45
  from novelwriter.core.coretools import DocDuplicator, DocMerger, DocSplitter
46
46
  from novelwriter.core.item import NWItem
@@ -244,9 +244,9 @@ class GuiProjectToolBar(QWidget):
244
244
  self.projTree = projView.projTree
245
245
 
246
246
  iSz = SHARED.theme.baseIconSize
247
- mPx = CONFIG.pxInt(2)
248
247
 
249
248
  self.setContentsMargins(0, 0, 0, 0)
249
+ self.setBackgroundRole(QPalette.ColorRole.Base)
250
250
  self.setAutoFillBackground(True)
251
251
 
252
252
  # Widget Label
@@ -259,43 +259,48 @@ class GuiProjectToolBar(QWidget):
259
259
  self.mQuick = QMenu(self)
260
260
 
261
261
  self.tbQuick = NIconToolButton(self, iSz)
262
- self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
262
+ self.tbQuick.setToolTip("{0} [Ctrl+L]".format(self.tr("Quick Links")))
263
263
  self.tbQuick.setShortcut("Ctrl+L")
264
264
  self.tbQuick.setMenu(self.mQuick)
265
265
 
266
266
  # Move Buttons
267
267
  self.tbMoveU = NIconToolButton(self, iSz)
268
- self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
268
+ self.tbMoveU.setToolTip("{0} [Ctrl+Up]".format(self.tr("Move Up")))
269
269
  self.tbMoveU.clicked.connect(self.projTree.moveItemUp)
270
270
 
271
271
  self.tbMoveD = NIconToolButton(self, iSz)
272
- self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
272
+ self.tbMoveD.setToolTip("{0} [Ctrl+Down]".format(self.tr("Move Down")))
273
273
  self.tbMoveD.clicked.connect(self.projTree.moveItemDown)
274
274
 
275
275
  # Add Item Menu
276
276
  self.mAdd = QMenu(self)
277
277
 
278
- self.aAddEmpty = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["document"]))
279
- self.aAddEmpty.triggered.connect(
280
- qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=0, isNote=False)
278
+ self.aAddScene = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
279
+ self.aAddScene.triggered.connect(
280
+ qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False)
281
281
  )
282
282
 
283
- self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
283
+ self.aAddChap = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
284
284
  self.aAddChap.triggered.connect(
285
285
  qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=2, isNote=False)
286
286
  )
287
287
 
288
- self.aAddScene = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
289
- self.aAddScene.triggered.connect(
290
- qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False)
288
+ self.aAddPart = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h1"]))
289
+ self.aAddPart.triggered.connect(
290
+ qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=1, isNote=False)
291
+ )
292
+
293
+ self.aAddEmpty = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["document"]))
294
+ self.aAddEmpty.triggered.connect(
295
+ qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=0, isNote=False)
291
296
  )
292
297
 
293
- self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"]))
298
+ self.aAddNote = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["note"]))
294
299
  self.aAddNote.triggered.connect(
295
300
  qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=1, isNote=True)
296
301
  )
297
302
 
298
- self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
303
+ self.aAddFolder = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
299
304
  self.aAddFolder.triggered.connect(
300
305
  qtLambda(self.projTree.newTreeItem, nwItemType.FOLDER)
301
306
  )
@@ -305,24 +310,24 @@ class GuiProjectToolBar(QWidget):
305
310
  self.mTemplates.menuItemTriggered.connect(lambda h: self.newDocumentFromTemplate.emit(h))
306
311
  self.mAdd.addMenu(self.mTemplates)
307
312
 
308
- self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"]))
313
+ self.mAddRoot = qtAddMenu(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["root"]))
309
314
  self._buildRootMenu()
310
315
 
311
316
  self.tbAdd = NIconToolButton(self, iSz)
312
- self.tbAdd.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
317
+ self.tbAdd.setToolTip("{0} [Ctrl+N]".format(self.tr("Add Item")))
313
318
  self.tbAdd.setShortcut("Ctrl+N")
314
319
  self.tbAdd.setMenu(self.mAdd)
315
320
 
316
321
  # More Options Menu
317
322
  self.mMore = QMenu(self)
318
323
 
319
- self.aExpand = self.mMore.addAction(self.tr("Expand All"))
324
+ self.aExpand = qtAddAction(self.mMore, self.tr("Expand All"))
320
325
  self.aExpand.triggered.connect(self.projTree.expandAll)
321
326
 
322
- self.aCollapse = self.mMore.addAction(self.tr("Collapse All"))
327
+ self.aCollapse = qtAddAction(self.mMore, self.tr("Collapse All"))
323
328
  self.aCollapse.triggered.connect(self.projTree.collapseAll)
324
329
 
325
- self.aEmptyTrash = self.mMore.addAction(self.tr("Empty Trash"))
330
+ self.aEmptyTrash = qtAddAction(self.mMore, self.tr("Empty Trash"))
326
331
  self.aEmptyTrash.triggered.connect(self.projTree.emptyTrash)
327
332
 
328
333
  self.tbMore = NIconToolButton(self, iSz)
@@ -337,7 +342,7 @@ class GuiProjectToolBar(QWidget):
337
342
  self.outerBox.addWidget(self.tbMoveD)
338
343
  self.outerBox.addWidget(self.tbAdd)
339
344
  self.outerBox.addWidget(self.tbMore)
340
- self.outerBox.setContentsMargins(mPx, mPx, 0, mPx)
345
+ self.outerBox.setContentsMargins(4, 2, 0, 2)
341
346
  self.outerBox.setSpacing(0)
342
347
 
343
348
  self.setLayout(self.outerBox)
@@ -353,10 +358,6 @@ class GuiProjectToolBar(QWidget):
353
358
 
354
359
  def updateTheme(self) -> None:
355
360
  """Update theme elements."""
356
- qPalette = self.palette()
357
- qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
358
- self.setPalette(qPalette)
359
-
360
361
  buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
361
362
  self.tbQuick.setStyleSheet(buttonStyle)
362
363
  self.tbMoveU.setStyleSheet(buttonStyle)
@@ -364,17 +365,18 @@ class GuiProjectToolBar(QWidget):
364
365
  self.tbAdd.setStyleSheet(buttonStyle)
365
366
  self.tbMore.setStyleSheet(buttonStyle)
366
367
 
367
- self.tbQuick.setThemeIcon("bookmark")
368
- self.tbMoveU.setThemeIcon("up")
369
- self.tbMoveD.setThemeIcon("down")
370
- self.tbAdd.setThemeIcon("add")
371
- self.tbMore.setThemeIcon("menu")
368
+ self.tbQuick.setThemeIcon("bookmarks", "blue")
369
+ self.tbMoveU.setThemeIcon("chevron_up", "blue")
370
+ self.tbMoveD.setThemeIcon("chevron_down", "blue")
371
+ self.tbAdd.setThemeIcon("add", "green")
372
+ self.tbMore.setThemeIcon("more_vertical")
372
373
 
373
- self.aAddEmpty.setIcon(SHARED.theme.getIcon("proj_document"))
374
- self.aAddChap.setIcon(SHARED.theme.getIcon("proj_chapter"))
375
- self.aAddScene.setIcon(SHARED.theme.getIcon("proj_scene"))
376
- self.aAddNote.setIcon(SHARED.theme.getIcon("proj_note"))
377
- self.aAddFolder.setIcon(SHARED.theme.getIcon("proj_folder"))
374
+ self.aAddScene.setIcon(SHARED.theme.getIcon("prj_scene", "scene"))
375
+ self.aAddChap.setIcon(SHARED.theme.getIcon("prj_chapter", "chapter"))
376
+ self.aAddPart.setIcon(SHARED.theme.getIcon("prj_title", "title"))
377
+ self.aAddEmpty.setIcon(SHARED.theme.getIcon("prj_document", "file"))
378
+ self.aAddNote.setIcon(SHARED.theme.getIcon("prj_note", "note"))
379
+ self.aAddFolder.setIcon(SHARED.theme.getIcon("prj_folder", "folder"))
378
380
 
379
381
  self.buildTemplatesMenu()
380
382
  self.buildQuickLinksMenu()
@@ -393,9 +395,9 @@ class GuiProjectToolBar(QWidget):
393
395
  logger.debug("Rebuilding quick links menu")
394
396
  self.mQuick.clear()
395
397
  for tHandle, nwItem in SHARED.project.tree.iterRoots(None):
396
- action = self.mQuick.addAction(nwItem.itemName)
398
+ action = qtAddAction(self.mQuick, nwItem.itemName)
397
399
  action.setData(tHandle)
398
- action.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]))
400
+ action.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass], "root"))
399
401
  action.triggered.connect(
400
402
  qtLambda(self.projView.setSelectedHandle, tHandle, doScroll=True)
401
403
  )
@@ -429,9 +431,10 @@ class GuiProjectToolBar(QWidget):
429
431
  """
430
432
  nwItem = SHARED.project.tree[tHandle]
431
433
  allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed()
432
- self.aAddEmpty.setVisible(allowDoc)
433
- self.aAddChap.setVisible(allowDoc)
434
434
  self.aAddScene.setVisible(allowDoc)
435
+ self.aAddChap.setVisible(allowDoc)
436
+ self.aAddPart.setVisible(allowDoc)
437
+ self.aAddEmpty.setVisible(allowDoc)
435
438
  return
436
439
 
437
440
  ##
@@ -441,8 +444,8 @@ class GuiProjectToolBar(QWidget):
441
444
  def _buildRootMenu(self) -> None:
442
445
  """Build the rood folder menu."""
443
446
  def addClass(itemClass: nwItemClass) -> None:
444
- aNew = self.mAddRoot.addAction(trConst(nwLabels.CLASS_NAME[itemClass]))
445
- aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass]))
447
+ aNew = qtAddAction(self.mAddRoot, trConst(nwLabels.CLASS_NAME[itemClass]))
448
+ aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass], "root"))
446
449
  aNew.triggered.connect(
447
450
  qtLambda(self.projTree.newTreeItem, nwItemType.ROOT, itemClass)
448
451
  )
@@ -507,7 +510,6 @@ class GuiProjectTree(QTreeView):
507
510
  self.customContextMenuRequested.connect(self.openContextMenu)
508
511
 
509
512
  # Connect signals
510
- self.clicked.connect(self._onSingleClick)
511
513
  self.doubleClicked.connect(self._onDoubleClick)
512
514
  self.collapsed.connect(self._onNodeCollapsed)
513
515
  self.expanded.connect(self._onNodeExpanded)
@@ -521,7 +523,6 @@ class GuiProjectTree(QTreeView):
521
523
 
522
524
  def initSettings(self) -> None:
523
525
  """Set or update tree widget settings."""
524
- # Scroll bars
525
526
  if CONFIG.hideVScroll:
526
527
  self.setVerticalScrollBarPolicy(QtScrollAlwaysOff)
527
528
  else:
@@ -558,21 +559,26 @@ class GuiProjectTree(QTreeView):
558
559
 
559
560
  def loadModel(self) -> None:
560
561
  """Load and prepare a new project model."""
562
+ if selectModelOld := self.selectionModel():
563
+ selectModelOld.disconnect()
564
+
561
565
  self.setModel(SHARED.project.tree.model)
562
566
 
563
567
  # Lock the column sizes
564
568
  iPx = SHARED.theme.baseIconHeight
565
- cMg = CONFIG.pxInt(6)
566
-
567
- treeHeader = self.header()
568
- treeHeader.setStretchLastSection(False)
569
- treeHeader.setMinimumSectionSize(iPx + cMg)
570
- treeHeader.setSectionResizeMode(ProjectNode.C_NAME, QtHeaderStretch)
571
- treeHeader.setSectionResizeMode(ProjectNode.C_COUNT, QtHeaderToContents)
572
- treeHeader.setSectionResizeMode(ProjectNode.C_ACTIVE, QtHeaderFixed)
573
- treeHeader.setSectionResizeMode(ProjectNode.C_STATUS, QtHeaderFixed)
574
- treeHeader.resizeSection(ProjectNode.C_ACTIVE, iPx + cMg)
575
- treeHeader.resizeSection(ProjectNode.C_STATUS, iPx + cMg)
569
+
570
+ if header := self.header():
571
+ header.setStretchLastSection(False)
572
+ header.setMinimumSectionSize(iPx + 6)
573
+ header.setSectionResizeMode(ProjectNode.C_NAME, QtHeaderStretch)
574
+ header.setSectionResizeMode(ProjectNode.C_COUNT, QtHeaderToContents)
575
+ header.setSectionResizeMode(ProjectNode.C_ACTIVE, QtHeaderFixed)
576
+ header.setSectionResizeMode(ProjectNode.C_STATUS, QtHeaderFixed)
577
+ header.resizeSection(ProjectNode.C_ACTIVE, iPx + 6)
578
+ header.resizeSection(ProjectNode.C_STATUS, iPx + 6)
579
+
580
+ if selectModelNew := self.selectionModel():
581
+ selectModelNew.currentChanged.connect(self._onSelectionChange)
576
582
 
577
583
  self.restoreExpandedState()
578
584
 
@@ -612,12 +618,12 @@ class GuiProjectTree(QTreeView):
612
618
  tHandle = None
613
619
  if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
614
620
 
615
- pos = -1
621
+ sPos = -1
616
622
  if (node := self._getNode(self.currentIndex())) and (itemRoot := node.item.itemRoot):
617
623
  if root := SHARED.project.tree.nodes.get(itemRoot):
618
- pos = root.row() + 1
624
+ sPos = root.row() + 1
619
625
 
620
- tHandle = SHARED.project.newRoot(itemClass, pos)
626
+ tHandle = SHARED.project.newRoot(itemClass, sPos)
621
627
  self.restoreExpandedState()
622
628
 
623
629
  elif itemType in (nwItemType.FILE, nwItemType.FOLDER):
@@ -630,52 +636,43 @@ class GuiProjectTree(QTreeView):
630
636
  SHARED.error(self.tr("Cannot add new files or folders to the Trash folder."))
631
637
  return
632
638
 
633
- # Collect some information about the selected item
634
- sLevel = nwStyles.H_LEVEL.get(node.item.mainHeading, 0)
635
- sIsParent = node.childCount() > 0
636
-
637
- # Set default label and determine if new item is to be added
638
- # as child or sibling to the selected item
639
+ # Set default label and determine where to put the new item
640
+ nNote = isNote
641
+ nLevel = hLevel
639
642
  if itemType == nwItemType.FILE:
640
643
  if copyDoc and (cItem := SHARED.project.tree[copyDoc]):
644
+ nNote = cItem.isNoteLayout()
645
+ nLevel = nwStyles.H_LEVEL.get(cItem.mainHeading, 0)
641
646
  newLabel = cItem.itemName
642
- asChild = sIsParent and node.item.isDocumentLayout()
643
647
  elif isNote:
644
648
  newLabel = self.tr("New Note")
645
- asChild = sIsParent
649
+ elif hLevel == 1:
650
+ newLabel = self.tr("New Part")
646
651
  elif hLevel == 2:
647
652
  newLabel = self.tr("New Chapter")
648
- asChild = sIsParent and node.item.isDocumentLayout() and sLevel < 2
649
653
  elif hLevel == 3:
650
654
  newLabel = self.tr("New Scene")
651
- asChild = sIsParent and node.item.isDocumentLayout() and sLevel < 3
652
655
  else:
653
656
  newLabel = self.tr("New Document")
654
- asChild = sIsParent and node.item.isDocumentLayout()
655
657
  else:
656
658
  newLabel = self.tr("New Folder")
657
- asChild = False
658
-
659
- pos = -1
660
- sHandle = None
661
- if not (asChild or node.item.isFolderType() or node.item.isRootType()):
662
- pos = node.row() + 1
663
- sHandle = node.item.itemParent
664
-
665
- sHandle = sHandle or node.item.itemHandle
666
- newLabel, dlgOk = GuiEditLabel.getLabel(self, text=newLabel)
667
- if dlgOk:
668
- # Add the file or folder
669
- if itemType == nwItemType.FILE:
670
- if tHandle := SHARED.project.newFile(newLabel, sHandle, pos):
671
- if copyDoc:
672
- SHARED.project.copyFileContent(tHandle, copyDoc)
673
- elif hLevel > 0:
674
- SHARED.project.writeNewFile(tHandle, hLevel, not isNote)
675
- SHARED.project.index.reIndexHandle(tHandle)
676
- SHARED.project.tree.refreshItems([tHandle])
677
- else:
678
- tHandle = SHARED.project.newFolder(newLabel, sHandle, pos)
659
+ nLevel = 0
660
+
661
+ sHandle, sPos = SHARED.project.tree.pickParent(node, nLevel, nNote)
662
+ if sHandle:
663
+ newLabel, dlgOk = GuiEditLabel.getLabel(self, text=newLabel)
664
+ if dlgOk:
665
+ # Add the file or folder
666
+ if itemType == nwItemType.FILE:
667
+ if tHandle := SHARED.project.newFile(newLabel, sHandle, sPos):
668
+ if copyDoc:
669
+ SHARED.project.copyFileContent(tHandle, copyDoc)
670
+ elif hLevel > 0:
671
+ SHARED.project.writeNewFile(tHandle, hLevel, not nNote)
672
+ SHARED.project.index.reIndexHandle(tHandle)
673
+ SHARED.project.tree.refreshItems([tHandle])
674
+ else:
675
+ tHandle = SHARED.project.newFolder(newLabel, sHandle, sPos)
679
676
 
680
677
  # Select the new item automatically
681
678
  if tHandle:
@@ -966,7 +963,8 @@ class GuiProjectTree(QTreeView):
966
963
  ctxMenu.buildMultiSelectMenu()
967
964
  else:
968
965
  ctxMenu.buildSingleSelectMenu()
969
- ctxMenu.exec(self.viewport().mapToGlobal(point))
966
+ if viewport := self.viewport():
967
+ ctxMenu.exec(viewport.mapToGlobal(point))
970
968
  ctxMenu.setParent(None)
971
969
  return
972
970
 
@@ -974,10 +972,10 @@ class GuiProjectTree(QTreeView):
974
972
  # Private Slots
975
973
  ##
976
974
 
977
- @pyqtSlot(QModelIndex)
978
- def _onSingleClick(self, index: QModelIndex) -> None:
975
+ @pyqtSlot(QModelIndex, QModelIndex)
976
+ def _onSelectionChange(self, current: QModelIndex, previous: QModelIndex) -> None:
979
977
  """The user changed which item is selected."""
980
- if node := self._getNode(index):
978
+ if node := self._getNode(current):
981
979
  self.projView.selectedItemChanged.emit(node.item.itemHandle)
982
980
  return
983
981
 
@@ -1026,7 +1024,7 @@ class GuiProjectTree(QTreeView):
1026
1024
  return [i for i in self.selectedIndexes() if i.column() == 0]
1027
1025
 
1028
1026
  def _getModel(self) -> ProjectModel | None:
1029
- """Return a project node corresponding to a model index."""
1027
+ """Return the model, if it exists."""
1030
1028
  if isinstance(model := self.model(), ProjectModel):
1031
1029
  return model
1032
1030
  return None
@@ -1087,7 +1085,8 @@ class _UpdatableMenu(QMenu):
1087
1085
 
1088
1086
  def setActionsVisible(self, value: bool) -> None:
1089
1087
  """Set the visibility of root action."""
1090
- self.menuAction().setVisible(value)
1088
+ if action := self.menuAction():
1089
+ action.setVisible(value)
1091
1090
  return
1092
1091
 
1093
1092
  ##
@@ -1103,7 +1102,7 @@ class _UpdatableMenu(QMenu):
1103
1102
 
1104
1103
  class _TreeContextMenu(QMenu):
1105
1104
 
1106
- __slots__ = ("_tree", "_view", "_node", "_item", "_model", "_handle", "_indices", "_children")
1105
+ __slots__ = ("_children", "_handle", "_indices", "_item", "_model", "_node", "_tree", "_view")
1107
1106
 
1108
1107
  def __init__(
1109
1108
  self, projTree: GuiProjectTree, model: ProjectModel,
@@ -1131,7 +1130,7 @@ class _TreeContextMenu(QMenu):
1131
1130
 
1132
1131
  def buildTrashMenu(self) -> None:
1133
1132
  """Build the special menu for the Trash folder."""
1134
- action = self.addAction(self.tr("Empty Trash"))
1133
+ action = qtAddAction(self, self.tr("Empty Trash"))
1135
1134
  action.triggered.connect(self._tree.emptyTrash)
1136
1135
  if self._children:
1137
1136
  self._expandCollapse()
@@ -1152,7 +1151,7 @@ class _TreeContextMenu(QMenu):
1152
1151
  self.addSeparator()
1153
1152
 
1154
1153
  # Edit Item Settings
1155
- action = self.addAction(self.tr("Rename"))
1154
+ action = qtAddAction(self, self.tr("Rename"))
1156
1155
  action.triggered.connect(qtLambda(self._view.renameTreeItem, self._handle))
1157
1156
  if isFile:
1158
1157
  self._itemHeader()
@@ -1167,7 +1166,7 @@ class _TreeContextMenu(QMenu):
1167
1166
  # Process Item
1168
1167
  if self._children:
1169
1168
  self._expandCollapse()
1170
- action = self.addAction(self.tr("Duplicate"))
1169
+ action = qtAddAction(self, self.tr("Duplicate"))
1171
1170
  action.triggered.connect(qtLambda(self._tree.duplicateFromHandle, self._handle))
1172
1171
  self._deleteOrTrash()
1173
1172
 
@@ -1187,12 +1186,12 @@ class _TreeContextMenu(QMenu):
1187
1186
 
1188
1187
  def _docActions(self) -> None:
1189
1188
  """Add document actions."""
1190
- action = self.addAction(self.tr("Open Document"))
1189
+ action = qtAddAction(self, self.tr("Open Document"))
1191
1190
  action.triggered.connect(qtLambda(
1192
1191
  self._view.openDocumentRequest.emit,
1193
1192
  self._handle, nwDocMode.EDIT, "", True
1194
1193
  ))
1195
- action = self.addAction(self.tr("View Document"))
1194
+ action = qtAddAction(self, self.tr("View Document"))
1196
1195
  action.triggered.connect(qtLambda(
1197
1196
  self._view.openDocumentRequest.emit,
1198
1197
  self._handle, nwDocMode.VIEW, "", False
@@ -1201,10 +1200,11 @@ class _TreeContextMenu(QMenu):
1201
1200
 
1202
1201
  def _itemCreation(self) -> None:
1203
1202
  """Add create item actions."""
1204
- menu = self.addMenu(self.tr("Create New ..."))
1205
- menu.addAction(self._view.projBar.aAddEmpty)
1206
- menu.addAction(self._view.projBar.aAddChap)
1203
+ menu = qtAddMenu(self, self.tr("Create New ..."))
1207
1204
  menu.addAction(self._view.projBar.aAddScene)
1205
+ menu.addAction(self._view.projBar.aAddChap)
1206
+ menu.addAction(self._view.projBar.aAddPart)
1207
+ menu.addAction(self._view.projBar.aAddEmpty)
1208
1208
  menu.addAction(self._view.projBar.aAddNote)
1209
1209
  menu.addAction(self._view.projBar.aAddFolder)
1210
1210
  return
@@ -1213,7 +1213,7 @@ class _TreeContextMenu(QMenu):
1213
1213
  """Check if there is a header that can be used for rename."""
1214
1214
  SHARED.saveEditor()
1215
1215
  if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
1216
- action = self.addAction(self.tr("Rename to Heading"))
1216
+ action = qtAddAction(self, self.tr("Rename to Heading"))
1217
1217
  action.triggered.connect(
1218
1218
  qtLambda(self._view.renameTreeItem, self._handle, hItem.title)
1219
1219
  )
@@ -1222,50 +1222,54 @@ class _TreeContextMenu(QMenu):
1222
1222
  def _itemActive(self) -> None:
1223
1223
  """Add Active/Inactive actions."""
1224
1224
  if len(self._indices) > 1:
1225
- mSub = self.addMenu(self.tr("Set Active to ..."))
1226
- aOne = mSub.addAction(SHARED.theme.getIcon("checked"), self._tree.trActive)
1225
+ mSub = qtAddMenu(self, self.tr("Set Active to ..."))
1226
+ aOne = qtAddAction(mSub, self._tree.trActive)
1227
+ aOne.setIcon(SHARED.theme.getIcon("checked", "green"))
1227
1228
  aOne.triggered.connect(qtLambda(self._iterItemActive, True))
1228
- aTwo = mSub.addAction(SHARED.theme.getIcon("unchecked"), self._tree.trInactive)
1229
+ aTwo = qtAddAction(mSub, self._tree.trInactive)
1230
+ aTwo.setIcon(SHARED.theme.getIcon("unchecked", "red"))
1229
1231
  aTwo.triggered.connect(qtLambda(self._iterItemActive, False))
1230
1232
  else:
1231
- action = self.addAction(self.tr("Toggle Active"))
1233
+ action = qtAddAction(self, self.tr("Toggle Active"))
1232
1234
  action.triggered.connect(self._toggleItemActive)
1233
1235
  return
1234
1236
 
1235
1237
  def _itemStatusImport(self, multi: bool) -> None:
1236
1238
  """Add actions for changing status or importance."""
1237
1239
  if self._item.isNovelLike():
1238
- menu = self.addMenu(self.tr("Set Status to ..."))
1240
+ menu = qtAddMenu(self, self.tr("Set Status to ..."))
1239
1241
  current = self._item.itemStatus
1240
1242
  for key, entry in SHARED.project.data.itemStatus.iterItems():
1241
1243
  name = entry.name
1242
1244
  if not multi and current == key:
1243
1245
  name += f" ({nwUnicode.U_CHECK})"
1244
- action = menu.addAction(entry.icon, name)
1246
+ action = qtAddAction(menu, name)
1247
+ action.setIcon(entry.icon)
1245
1248
  if multi:
1246
1249
  action.triggered.connect(qtLambda(self._iterSetItemStatus, key))
1247
1250
  else:
1248
1251
  action.triggered.connect(qtLambda(self._changeItemStatus, key))
1249
1252
  menu.addSeparator()
1250
- action = menu.addAction(self.tr("Manage Labels ..."))
1253
+ action = qtAddAction(menu, self.tr("Manage Labels ..."))
1251
1254
  action.triggered.connect(qtLambda(
1252
1255
  self._view.projectSettingsRequest.emit,
1253
1256
  GuiProjectSettings.PAGE_STATUS
1254
1257
  ))
1255
1258
  else:
1256
- menu = self.addMenu(self.tr("Set Importance to ..."))
1259
+ menu = qtAddMenu(self, self.tr("Set Importance to ..."))
1257
1260
  current = self._item.itemImport
1258
1261
  for key, entry in SHARED.project.data.itemImport.iterItems():
1259
1262
  name = entry.name
1260
1263
  if not multi and current == key:
1261
1264
  name += f" ({nwUnicode.U_CHECK})"
1262
- action = menu.addAction(entry.icon, name)
1265
+ action = qtAddAction(menu, name)
1266
+ action.setIcon(entry.icon)
1263
1267
  if multi:
1264
1268
  action.triggered.connect(qtLambda(self._iterSetItemImport, key))
1265
1269
  else:
1266
1270
  action.triggered.connect(qtLambda(self._changeItemImport, key))
1267
1271
  menu.addSeparator()
1268
- action = menu.addAction(self.tr("Manage Labels ..."))
1272
+ action = qtAddAction(menu, self.tr("Manage Labels ..."))
1269
1273
  action.triggered.connect(qtLambda(
1270
1274
  self._view.projectSettingsRequest.emit,
1271
1275
  GuiProjectSettings.PAGE_IMPORT
@@ -1274,7 +1278,7 @@ class _TreeContextMenu(QMenu):
1274
1278
 
1275
1279
  def _itemTransform(self, isFile: bool, isFolder: bool) -> None:
1276
1280
  """Add actions for the Transform menu."""
1277
- menu = self.addMenu(self.tr("Transform ..."))
1281
+ menu = qtAddMenu(self, self.tr("Transform ..."))
1278
1282
 
1279
1283
  trDoc = trConst(nwLabels.LAYOUT_NAME[nwItemLayout.DOCUMENT])
1280
1284
  trNote = trConst(nwLabels.LAYOUT_NAME[nwItemLayout.NOTE])
@@ -1284,42 +1288,42 @@ class _TreeContextMenu(QMenu):
1284
1288
  isNoteFile = isFile and self._item.isNoteLayout()
1285
1289
 
1286
1290
  if isNoteFile and self._item.documentAllowed():
1287
- action = menu.addAction(self.tr("Convert to {0}").format(trDoc))
1291
+ action = qtAddAction(menu, self.tr("Convert to {0}").format(trDoc))
1288
1292
  action.triggered.connect(qtLambda(self._changeItemLayout, loDoc))
1289
1293
 
1290
1294
  if isDocFile:
1291
- action = menu.addAction(self.tr("Convert to {0}").format(trNote))
1295
+ action = qtAddAction(menu, self.tr("Convert to {0}").format(trNote))
1292
1296
  action.triggered.connect(qtLambda(self._changeItemLayout, loNote))
1293
1297
 
1294
1298
  if isFolder and self._item.documentAllowed():
1295
- action = menu.addAction(self.tr("Convert to {0}").format(trDoc))
1299
+ action = qtAddAction(menu, self.tr("Convert to {0}").format(trDoc))
1296
1300
  action.triggered.connect(qtLambda(self._convertFolderToFile, loDoc))
1297
1301
 
1298
1302
  if isFolder:
1299
- action = menu.addAction(self.tr("Convert to {0}").format(trNote))
1303
+ action = qtAddAction(menu, self.tr("Convert to {0}").format(trNote))
1300
1304
  action.triggered.connect(qtLambda(self._convertFolderToFile, loNote))
1301
1305
 
1302
1306
  if self._children and isFile:
1303
- action = menu.addAction(self.tr("Merge Child Items into Self"))
1307
+ action = qtAddAction(menu, self.tr("Merge Child Items into Self"))
1304
1308
  action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, False))
1305
- action = menu.addAction(self.tr("Merge Child Items into New"))
1309
+ action = qtAddAction(menu, self.tr("Merge Child Items into New"))
1306
1310
  action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, True))
1307
1311
 
1308
1312
  if self._children and isFolder:
1309
- action = menu.addAction(self.tr("Merge Documents in Folder"))
1313
+ action = qtAddAction(menu, self.tr("Merge Documents in Folder"))
1310
1314
  action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, True))
1311
1315
 
1312
1316
  if isFile:
1313
- action = menu.addAction(self.tr("Split Document by Headings"))
1317
+ action = qtAddAction(menu, self.tr("Split Document by Headings"))
1314
1318
  action.triggered.connect(qtLambda(self._tree.splitDocument, self._handle))
1315
1319
 
1316
1320
  return
1317
1321
 
1318
1322
  def _expandCollapse(self) -> None:
1319
1323
  """Add actions for expand and collapse."""
1320
- action = self.addAction(self.tr("Expand All"))
1324
+ action = qtAddAction(self, self.tr("Expand All"))
1321
1325
  action.triggered.connect(qtLambda(self._tree.expandFromIndex, self._indices[0]))
1322
- action = self.addAction(self.tr("Collapse All"))
1326
+ action = qtAddAction(self, self.tr("Collapse All"))
1323
1327
  action.triggered.connect(qtLambda(self._tree.collapseFromIndex, self._indices[0]))
1324
1328
  return
1325
1329
 
@@ -1327,12 +1331,12 @@ class _TreeContextMenu(QMenu):
1327
1331
  """Add move to Trash action."""
1328
1332
  if (
1329
1333
  self._model.trashSelection(self._indices)
1330
- or len(self._indices) == 1 and self._item.isRootType()
1334
+ or (len(self._indices) == 1 and self._item.isRootType())
1331
1335
  ):
1332
1336
  text = self.tr("Delete Permanently")
1333
1337
  else:
1334
1338
  text = self.tr("Move to Trash")
1335
- action = self.addAction(text)
1339
+ action = qtAddAction(self, text)
1336
1340
  action.triggered.connect(self._tree.processDeleteRequest)
1337
1341
  return
1338
1342
 
@@ -1399,6 +1403,7 @@ class _TreeContextMenu(QMenu):
1399
1403
  if itemLayout == nwItemLayout.DOCUMENT and self._item.documentAllowed():
1400
1404
  self._item.setLayout(nwItemLayout.DOCUMENT)
1401
1405
  self._item.notifyToRefresh()
1406
+ self._item.notifyNovelStructureChange()
1402
1407
  elif itemLayout == nwItemLayout.NOTE:
1403
1408
  self._item.setLayout(nwItemLayout.NOTE)
1404
1409
  self._item.notifyToRefresh()
@@ -1415,6 +1420,7 @@ class _TreeContextMenu(QMenu):
1415
1420
  self._item.setType(nwItemType.FILE)
1416
1421
  self._item.setLayout(nwItemLayout.DOCUMENT)
1417
1422
  self._item.notifyToRefresh()
1423
+ self._item.notifyNovelStructureChange()
1418
1424
  elif msgYes and itemLayout == nwItemLayout.NOTE:
1419
1425
  self._item.setType(nwItemType.FILE)
1420
1426
  self._item.setLayout(nwItemLayout.NOTE)