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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. novelwriter/__init__.py +98 -75
  2. novelwriter/assets/i18n/nw_cs_CZ.qm +0 -0
  3. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  4. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  5. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  6. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  7. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  8. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  9. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  10. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  11. novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
  12. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  13. novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
  14. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  15. novelwriter/assets/i18n/project_de_DE.json +3 -1
  16. novelwriter/assets/i18n/project_en_GB.json +2 -0
  17. novelwriter/assets/i18n/project_en_US.json +2 -0
  18. novelwriter/assets/i18n/project_it_IT.json +2 -0
  19. novelwriter/assets/i18n/project_ja_JP.json +2 -0
  20. novelwriter/assets/i18n/project_nb_NO.json +2 -0
  21. novelwriter/assets/i18n/project_nn_NO.json +5 -0
  22. novelwriter/assets/i18n/project_pl_PL.json +2 -0
  23. novelwriter/assets/i18n/project_pt_BR.json +2 -0
  24. novelwriter/assets/i18n/project_ru_RU.json +2 -0
  25. novelwriter/assets/i18n/project_zh_CN.json +2 -0
  26. novelwriter/assets/icons/font_awesome.icons +109 -0
  27. novelwriter/assets/icons/material_filled_bold.icons +109 -0
  28. novelwriter/assets/icons/material_filled_normal.icons +109 -0
  29. novelwriter/assets/icons/material_filled_thin.icons +109 -0
  30. novelwriter/assets/icons/material_rounded_bold.icons +109 -0
  31. novelwriter/assets/icons/material_rounded_normal.icons +109 -0
  32. novelwriter/assets/icons/material_rounded_thin.icons +109 -0
  33. novelwriter/assets/icons/remix_filled.icons +109 -0
  34. novelwriter/assets/icons/remix_outline.icons +109 -0
  35. novelwriter/assets/images/splash.png +0 -0
  36. novelwriter/assets/manual.pdf +0 -0
  37. novelwriter/assets/{manual_fr_FR.pdf → manual_fr.pdf} +0 -0
  38. novelwriter/assets/sample.zip +0 -0
  39. novelwriter/assets/syntax/cyberpunk_night.conf +1 -1
  40. novelwriter/assets/syntax/snazzy.conf +3 -3
  41. novelwriter/assets/text/credits_en.htm +12 -6
  42. novelwriter/assets/themes/cyberpunk_night.conf +23 -7
  43. novelwriter/assets/themes/default_dark.conf +20 -4
  44. novelwriter/assets/themes/default_light.conf +21 -5
  45. novelwriter/assets/themes/dracula.conf +20 -4
  46. novelwriter/assets/themes/snazzy.conf +48 -0
  47. novelwriter/assets/themes/solarized_dark.conf +24 -8
  48. novelwriter/assets/themes/solarized_light.conf +22 -6
  49. novelwriter/common.py +43 -27
  50. novelwriter/config.py +201 -139
  51. novelwriter/constants.py +92 -56
  52. novelwriter/core/buildsettings.py +26 -17
  53. novelwriter/core/coretools.py +52 -41
  54. novelwriter/core/docbuild.py +20 -13
  55. novelwriter/core/document.py +2 -2
  56. novelwriter/core/index.py +166 -432
  57. novelwriter/core/indexdata.py +406 -0
  58. novelwriter/core/item.py +50 -32
  59. novelwriter/core/itemmodel.py +43 -38
  60. novelwriter/core/novelmodel.py +225 -0
  61. novelwriter/core/options.py +1 -1
  62. novelwriter/core/project.py +24 -25
  63. novelwriter/core/projectdata.py +47 -29
  64. novelwriter/core/projectxml.py +18 -8
  65. novelwriter/core/sessions.py +32 -15
  66. novelwriter/core/spellcheck.py +4 -3
  67. novelwriter/core/status.py +12 -15
  68. novelwriter/core/storage.py +1 -1
  69. novelwriter/core/tree.py +55 -13
  70. novelwriter/dialogs/about.py +19 -22
  71. novelwriter/dialogs/docmerge.py +23 -24
  72. novelwriter/dialogs/docsplit.py +26 -26
  73. novelwriter/dialogs/editlabel.py +19 -20
  74. novelwriter/dialogs/preferences.py +143 -69
  75. novelwriter/dialogs/projectsettings.py +51 -54
  76. novelwriter/dialogs/quotes.py +14 -19
  77. novelwriter/dialogs/wordlist.py +25 -30
  78. novelwriter/enum.py +8 -0
  79. novelwriter/error.py +16 -16
  80. novelwriter/extensions/configlayout.py +24 -20
  81. novelwriter/extensions/eventfilters.py +9 -5
  82. novelwriter/extensions/modified.py +34 -15
  83. novelwriter/extensions/novelselector.py +18 -5
  84. novelwriter/extensions/pagedsidebar.py +39 -49
  85. novelwriter/extensions/progressbars.py +10 -8
  86. novelwriter/extensions/statusled.py +6 -13
  87. novelwriter/extensions/switch.py +31 -41
  88. novelwriter/extensions/switchbox.py +8 -3
  89. novelwriter/extensions/versioninfo.py +4 -4
  90. novelwriter/formats/shared.py +1 -1
  91. novelwriter/formats/todocx.py +14 -10
  92. novelwriter/formats/tohtml.py +7 -5
  93. novelwriter/formats/tokenizer.py +37 -33
  94. novelwriter/formats/tomarkdown.py +6 -2
  95. novelwriter/formats/toodt.py +27 -22
  96. novelwriter/formats/toqdoc.py +19 -14
  97. novelwriter/formats/toraw.py +6 -2
  98. novelwriter/gui/doceditor.py +314 -305
  99. novelwriter/gui/dochighlight.py +46 -45
  100. novelwriter/gui/docviewer.py +102 -104
  101. novelwriter/gui/docviewerpanel.py +47 -51
  102. novelwriter/gui/editordocument.py +8 -5
  103. novelwriter/gui/itemdetails.py +15 -18
  104. novelwriter/gui/mainmenu.py +147 -146
  105. novelwriter/gui/noveltree.py +246 -406
  106. novelwriter/gui/outline.py +145 -76
  107. novelwriter/gui/projtree.py +139 -132
  108. novelwriter/gui/search.py +34 -31
  109. novelwriter/gui/sidebar.py +13 -18
  110. novelwriter/gui/statusbar.py +27 -21
  111. novelwriter/gui/theme.py +554 -436
  112. novelwriter/guimain.py +56 -38
  113. novelwriter/shared.py +32 -23
  114. novelwriter/splash.py +74 -0
  115. novelwriter/text/comments.py +70 -0
  116. novelwriter/text/patterns.py +4 -4
  117. novelwriter/tools/dictionaries.py +20 -29
  118. novelwriter/tools/lipsum.py +18 -18
  119. novelwriter/tools/manusbuild.py +39 -42
  120. novelwriter/tools/manuscript.py +114 -127
  121. novelwriter/tools/manussettings.py +129 -102
  122. novelwriter/tools/noveldetails.py +60 -72
  123. novelwriter/tools/welcome.py +57 -75
  124. novelwriter/tools/writingstats.py +112 -102
  125. novelwriter/types.py +5 -7
  126. {novelWriter-2.6.2.dist-info → novelwriter-2.7.dist-info}/METADATA +6 -6
  127. novelwriter-2.7.dist-info/RECORD +162 -0
  128. {novelWriter-2.6.2.dist-info → novelwriter-2.7.dist-info}/WHEEL +1 -1
  129. novelWriter-2.6.2.dist-info/RECORD +0 -363
  130. novelwriter/assets/icons/typicons_dark/README.md +0 -29
  131. novelwriter/assets/icons/typicons_dark/icons.conf +0 -134
  132. novelwriter/assets/icons/typicons_dark/mixed_copy.svg +0 -4
  133. novelwriter/assets/icons/typicons_dark/mixed_document-chapter.svg +0 -12
  134. novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +0 -6
  135. novelwriter/assets/icons/typicons_dark/mixed_document-note.svg +0 -12
  136. novelwriter/assets/icons/typicons_dark/mixed_document-scene.svg +0 -12
  137. novelwriter/assets/icons/typicons_dark/mixed_document-section.svg +0 -12
  138. novelwriter/assets/icons/typicons_dark/mixed_document-title.svg +0 -12
  139. novelwriter/assets/icons/typicons_dark/mixed_edit.svg +0 -4
  140. novelwriter/assets/icons/typicons_dark/mixed_import.svg +0 -5
  141. novelwriter/assets/icons/typicons_dark/mixed_input-checked.svg +0 -5
  142. novelwriter/assets/icons/typicons_dark/mixed_input-none.svg +0 -5
  143. novelwriter/assets/icons/typicons_dark/mixed_input-unchecked.svg +0 -5
  144. novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +0 -6
  145. novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +0 -6
  146. novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +0 -6
  147. novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +0 -6
  148. novelwriter/assets/icons/typicons_dark/mixed_search-replace.svg +0 -6
  149. novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +0 -6
  150. novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +0 -6
  151. novelwriter/assets/icons/typicons_dark/nw_deco-h0.svg +0 -4
  152. novelwriter/assets/icons/typicons_dark/nw_deco-h1.svg +0 -4
  153. novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +0 -4
  154. novelwriter/assets/icons/typicons_dark/nw_deco-h2.svg +0 -4
  155. novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +0 -4
  156. novelwriter/assets/icons/typicons_dark/nw_deco-h3.svg +0 -4
  157. novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +0 -4
  158. novelwriter/assets/icons/typicons_dark/nw_deco-h4.svg +0 -4
  159. novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +0 -4
  160. novelwriter/assets/icons/typicons_dark/nw_deco-noveltree-more.svg +0 -4
  161. novelwriter/assets/icons/typicons_dark/nw_font.svg +0 -4
  162. novelwriter/assets/icons/typicons_dark/nw_panel.svg +0 -4
  163. novelwriter/assets/icons/typicons_dark/nw_quote.svg +0 -4
  164. novelwriter/assets/icons/typicons_dark/nw_search-case.svg +0 -4
  165. novelwriter/assets/icons/typicons_dark/nw_search-preserve.svg +0 -4
  166. novelwriter/assets/icons/typicons_dark/nw_search-regex.svg +0 -4
  167. novelwriter/assets/icons/typicons_dark/nw_search-word.svg +0 -4
  168. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +0 -4
  169. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +0 -6
  170. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +0 -4
  171. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +0 -6
  172. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +0 -7
  173. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +0 -4
  174. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +0 -6
  175. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +0 -7
  176. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +0 -7
  177. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +0 -7
  178. novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +0 -5
  179. novelwriter/assets/icons/typicons_dark/typ_arrow-down-thick-grey.svg +0 -4
  180. novelwriter/assets/icons/typicons_dark/typ_arrow-forward.svg +0 -4
  181. novelwriter/assets/icons/typicons_dark/typ_arrow-maximise.svg +0 -4
  182. novelwriter/assets/icons/typicons_dark/typ_arrow-minimise.svg +0 -4
  183. novelwriter/assets/icons/typicons_dark/typ_arrow-repeat-grey.svg +0 -4
  184. novelwriter/assets/icons/typicons_dark/typ_book-grey.svg +0 -4
  185. novelwriter/assets/icons/typicons_dark/typ_book.svg +0 -6
  186. novelwriter/assets/icons/typicons_dark/typ_bookmark.svg +0 -4
  187. novelwriter/assets/icons/typicons_dark/typ_calendar.svg +0 -4
  188. novelwriter/assets/icons/typicons_dark/typ_cancel-grey.svg +0 -4
  189. novelwriter/assets/icons/typicons_dark/typ_cancel.svg +0 -4
  190. novelwriter/assets/icons/typicons_dark/typ_chart-bar-grey.svg +0 -4
  191. novelwriter/assets/icons/typicons_dark/typ_chevron-down.svg +0 -4
  192. novelwriter/assets/icons/typicons_dark/typ_chevron-left.svg +0 -4
  193. novelwriter/assets/icons/typicons_dark/typ_chevron-right.svg +0 -4
  194. novelwriter/assets/icons/typicons_dark/typ_chevron-up.svg +0 -4
  195. novelwriter/assets/icons/typicons_dark/typ_cog.svg +0 -4
  196. novelwriter/assets/icons/typicons_dark/typ_delete-full.svg +0 -4
  197. novelwriter/assets/icons/typicons_dark/typ_delete.svg +0 -4
  198. novelwriter/assets/icons/typicons_dark/typ_directions-full.svg +0 -4
  199. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +0 -4
  200. novelwriter/assets/icons/typicons_dark/typ_document-text.svg +0 -8
  201. novelwriter/assets/icons/typicons_dark/typ_document.svg +0 -4
  202. novelwriter/assets/icons/typicons_dark/typ_export-grey.svg +0 -4
  203. novelwriter/assets/icons/typicons_dark/typ_export.svg +0 -4
  204. novelwriter/assets/icons/typicons_dark/typ_eye.svg +0 -4
  205. novelwriter/assets/icons/typicons_dark/typ_flag.svg +0 -4
  206. novelwriter/assets/icons/typicons_dark/typ_folder-open.svg +0 -4
  207. novelwriter/assets/icons/typicons_dark/typ_folder.svg +0 -5
  208. novelwriter/assets/icons/typicons_dark/typ_globe-grey.svg +0 -4
  209. novelwriter/assets/icons/typicons_dark/typ_key.svg +0 -4
  210. novelwriter/assets/icons/typicons_dark/typ_lightbulb-full.svg +0 -4
  211. novelwriter/assets/icons/typicons_dark/typ_location.svg +0 -4
  212. novelwriter/assets/icons/typicons_dark/typ_media-pause-grey.svg +0 -4
  213. novelwriter/assets/icons/typicons_dark/typ_media-record-outline.svg +0 -4
  214. novelwriter/assets/icons/typicons_dark/typ_media-record.svg +0 -4
  215. novelwriter/assets/icons/typicons_dark/typ_minus.svg +0 -4
  216. novelwriter/assets/icons/typicons_dark/typ_pencil.svg +0 -5
  217. novelwriter/assets/icons/typicons_dark/typ_pin-outline.svg +0 -4
  218. novelwriter/assets/icons/typicons_dark/typ_pin.svg +0 -4
  219. novelwriter/assets/icons/typicons_dark/typ_plus.svg +0 -4
  220. novelwriter/assets/icons/typicons_dark/typ_puzzle-outline.svg +0 -4
  221. novelwriter/assets/icons/typicons_dark/typ_puzzle.svg +0 -4
  222. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +0 -4
  223. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +0 -4
  224. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +0 -4
  225. novelwriter/assets/icons/typicons_dark/typ_search.svg +0 -4
  226. novelwriter/assets/icons/typicons_dark/typ_star.svg +0 -4
  227. novelwriter/assets/icons/typicons_dark/typ_stopwatch-grey.svg +0 -4
  228. novelwriter/assets/icons/typicons_dark/typ_th-dot-menu.svg +0 -4
  229. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +0 -4
  230. novelwriter/assets/icons/typicons_dark/typ_th-list-grey.svg +0 -4
  231. novelwriter/assets/icons/typicons_dark/typ_th-list.svg +0 -9
  232. novelwriter/assets/icons/typicons_dark/typ_times.svg +0 -4
  233. novelwriter/assets/icons/typicons_dark/typ_trash.svg +0 -5
  234. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +0 -4
  235. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +0 -4
  236. novelwriter/assets/icons/typicons_dark/typ_user.svg +0 -5
  237. novelwriter/assets/icons/typicons_dark/typ_warning-full.svg +0 -4
  238. novelwriter/assets/icons/typicons_light/README.md +0 -29
  239. novelwriter/assets/icons/typicons_light/icons.conf +0 -134
  240. novelwriter/assets/icons/typicons_light/mixed_copy.svg +0 -4
  241. novelwriter/assets/icons/typicons_light/mixed_document-chapter.svg +0 -12
  242. novelwriter/assets/icons/typicons_light/mixed_document-new.svg +0 -6
  243. novelwriter/assets/icons/typicons_light/mixed_document-note.svg +0 -12
  244. novelwriter/assets/icons/typicons_light/mixed_document-scene.svg +0 -12
  245. novelwriter/assets/icons/typicons_light/mixed_document-section.svg +0 -12
  246. novelwriter/assets/icons/typicons_light/mixed_document-title.svg +0 -12
  247. novelwriter/assets/icons/typicons_light/mixed_edit.svg +0 -4
  248. novelwriter/assets/icons/typicons_light/mixed_import.svg +0 -5
  249. novelwriter/assets/icons/typicons_light/mixed_input-checked.svg +0 -5
  250. novelwriter/assets/icons/typicons_light/mixed_input-none.svg +0 -5
  251. novelwriter/assets/icons/typicons_light/mixed_input-unchecked.svg +0 -5
  252. novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +0 -6
  253. novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +0 -6
  254. novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +0 -6
  255. novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +0 -6
  256. novelwriter/assets/icons/typicons_light/mixed_search-replace.svg +0 -6
  257. novelwriter/assets/icons/typicons_light/mixed_size-height.svg +0 -6
  258. novelwriter/assets/icons/typicons_light/mixed_size-width.svg +0 -6
  259. novelwriter/assets/icons/typicons_light/nw_deco-h0.svg +0 -4
  260. novelwriter/assets/icons/typicons_light/nw_deco-h1.svg +0 -4
  261. novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +0 -4
  262. novelwriter/assets/icons/typicons_light/nw_deco-h2.svg +0 -4
  263. novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +0 -4
  264. novelwriter/assets/icons/typicons_light/nw_deco-h3.svg +0 -4
  265. novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +0 -4
  266. novelwriter/assets/icons/typicons_light/nw_deco-h4.svg +0 -4
  267. novelwriter/assets/icons/typicons_light/nw_deco-note.svg +0 -4
  268. novelwriter/assets/icons/typicons_light/nw_deco-noveltree-more.svg +0 -4
  269. novelwriter/assets/icons/typicons_light/nw_font.svg +0 -4
  270. novelwriter/assets/icons/typicons_light/nw_panel.svg +0 -4
  271. novelwriter/assets/icons/typicons_light/nw_quote.svg +0 -4
  272. novelwriter/assets/icons/typicons_light/nw_search-case.svg +0 -4
  273. novelwriter/assets/icons/typicons_light/nw_search-preserve.svg +0 -4
  274. novelwriter/assets/icons/typicons_light/nw_search-regex.svg +0 -4
  275. novelwriter/assets/icons/typicons_light/nw_search-word.svg +0 -4
  276. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +0 -4
  277. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +0 -6
  278. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +0 -4
  279. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +0 -6
  280. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +0 -7
  281. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +0 -4
  282. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +0 -6
  283. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +0 -7
  284. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +0 -7
  285. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +0 -7
  286. novelwriter/assets/icons/typicons_light/nw_toolbar.svg +0 -5
  287. novelwriter/assets/icons/typicons_light/typ_arrow-down-thick-grey.svg +0 -4
  288. novelwriter/assets/icons/typicons_light/typ_arrow-forward.svg +0 -4
  289. novelwriter/assets/icons/typicons_light/typ_arrow-maximise.svg +0 -4
  290. novelwriter/assets/icons/typicons_light/typ_arrow-minimise.svg +0 -4
  291. novelwriter/assets/icons/typicons_light/typ_arrow-repeat-grey.svg +0 -4
  292. novelwriter/assets/icons/typicons_light/typ_book-grey.svg +0 -4
  293. novelwriter/assets/icons/typicons_light/typ_book.svg +0 -6
  294. novelwriter/assets/icons/typicons_light/typ_bookmark.svg +0 -4
  295. novelwriter/assets/icons/typicons_light/typ_calendar.svg +0 -4
  296. novelwriter/assets/icons/typicons_light/typ_cancel-grey.svg +0 -4
  297. novelwriter/assets/icons/typicons_light/typ_cancel.svg +0 -4
  298. novelwriter/assets/icons/typicons_light/typ_chart-bar-grey.svg +0 -4
  299. novelwriter/assets/icons/typicons_light/typ_chevron-down.svg +0 -4
  300. novelwriter/assets/icons/typicons_light/typ_chevron-left.svg +0 -4
  301. novelwriter/assets/icons/typicons_light/typ_chevron-right.svg +0 -4
  302. novelwriter/assets/icons/typicons_light/typ_chevron-up.svg +0 -4
  303. novelwriter/assets/icons/typicons_light/typ_cog.svg +0 -4
  304. novelwriter/assets/icons/typicons_light/typ_delete-full.svg +0 -4
  305. novelwriter/assets/icons/typicons_light/typ_delete.svg +0 -4
  306. novelwriter/assets/icons/typicons_light/typ_directions-full.svg +0 -4
  307. novelwriter/assets/icons/typicons_light/typ_document-add.svg +0 -4
  308. novelwriter/assets/icons/typicons_light/typ_document-text.svg +0 -5
  309. novelwriter/assets/icons/typicons_light/typ_document.svg +0 -4
  310. novelwriter/assets/icons/typicons_light/typ_export-grey.svg +0 -4
  311. novelwriter/assets/icons/typicons_light/typ_export.svg +0 -4
  312. novelwriter/assets/icons/typicons_light/typ_eye.svg +0 -4
  313. novelwriter/assets/icons/typicons_light/typ_flag.svg +0 -4
  314. novelwriter/assets/icons/typicons_light/typ_folder-open.svg +0 -4
  315. novelwriter/assets/icons/typicons_light/typ_folder.svg +0 -5
  316. novelwriter/assets/icons/typicons_light/typ_globe-grey.svg +0 -4
  317. novelwriter/assets/icons/typicons_light/typ_key.svg +0 -4
  318. novelwriter/assets/icons/typicons_light/typ_lightbulb-full.svg +0 -4
  319. novelwriter/assets/icons/typicons_light/typ_location.svg +0 -4
  320. novelwriter/assets/icons/typicons_light/typ_media-pause-grey.svg +0 -4
  321. novelwriter/assets/icons/typicons_light/typ_media-record-outline.svg +0 -4
  322. novelwriter/assets/icons/typicons_light/typ_media-record.svg +0 -4
  323. novelwriter/assets/icons/typicons_light/typ_minus.svg +0 -4
  324. novelwriter/assets/icons/typicons_light/typ_pencil.svg +0 -5
  325. novelwriter/assets/icons/typicons_light/typ_pin-outline.svg +0 -4
  326. novelwriter/assets/icons/typicons_light/typ_pin.svg +0 -4
  327. novelwriter/assets/icons/typicons_light/typ_plus.svg +0 -4
  328. novelwriter/assets/icons/typicons_light/typ_puzzle-outline.svg +0 -4
  329. novelwriter/assets/icons/typicons_light/typ_puzzle.svg +0 -4
  330. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +0 -4
  331. novelwriter/assets/icons/typicons_light/typ_refresh.svg +0 -4
  332. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +0 -4
  333. novelwriter/assets/icons/typicons_light/typ_search.svg +0 -4
  334. novelwriter/assets/icons/typicons_light/typ_star.svg +0 -4
  335. novelwriter/assets/icons/typicons_light/typ_stopwatch-grey.svg +0 -4
  336. novelwriter/assets/icons/typicons_light/typ_th-dot-menu.svg +0 -4
  337. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +0 -4
  338. novelwriter/assets/icons/typicons_light/typ_th-list-grey.svg +0 -4
  339. novelwriter/assets/icons/typicons_light/typ_th-list.svg +0 -9
  340. novelwriter/assets/icons/typicons_light/typ_times.svg +0 -4
  341. novelwriter/assets/icons/typicons_light/typ_trash.svg +0 -5
  342. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +0 -4
  343. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +0 -4
  344. novelwriter/assets/icons/typicons_light/typ_user.svg +0 -5
  345. novelwriter/assets/icons/typicons_light/typ_warning-full.svg +0 -4
  346. {novelWriter-2.6.2.dist-info → novelwriter-2.7.dist-info}/entry_points.txt +0 -0
  347. {novelWriter-2.6.2.dist-info → novelwriter-2.7.dist-info/licenses}/LICENSE.md +0 -0
  348. {novelWriter-2.6.2.dist-info → novelwriter-2.7.dist-info}/top_level.txt +0 -0
@@ -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
@@ -254,48 +254,54 @@ class GuiProjectToolBar(QWidget):
254
254
  self.viewLabel.setFont(SHARED.theme.guiFontB)
255
255
  self.viewLabel.setContentsMargins(0, 0, 0, 0)
256
256
  self.viewLabel.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
257
+ self.projTree.setAccessibleName(self.viewLabel.text())
257
258
 
258
259
  # Quick Links
259
260
  self.mQuick = QMenu(self)
260
261
 
261
262
  self.tbQuick = NIconToolButton(self, iSz)
262
- self.tbQuick.setToolTip("%s [Ctrl+L]" % self.tr("Quick Links"))
263
+ self.tbQuick.setToolTip("{0} [Ctrl+L]".format(self.tr("Quick Links")))
263
264
  self.tbQuick.setShortcut("Ctrl+L")
264
265
  self.tbQuick.setMenu(self.mQuick)
265
266
 
266
267
  # Move Buttons
267
268
  self.tbMoveU = NIconToolButton(self, iSz)
268
- self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
269
+ self.tbMoveU.setToolTip("{0} [Ctrl+Up]".format(self.tr("Move Up")))
269
270
  self.tbMoveU.clicked.connect(self.projTree.moveItemUp)
270
271
 
271
272
  self.tbMoveD = NIconToolButton(self, iSz)
272
- self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
273
+ self.tbMoveD.setToolTip("{0} [Ctrl+Down]".format(self.tr("Move Down")))
273
274
  self.tbMoveD.clicked.connect(self.projTree.moveItemDown)
274
275
 
275
276
  # Add Item Menu
276
277
  self.mAdd = QMenu(self)
277
278
 
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)
279
+ self.aAddScene = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
280
+ self.aAddScene.triggered.connect(
281
+ qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False)
281
282
  )
282
283
 
283
- self.aAddChap = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
284
+ self.aAddChap = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
284
285
  self.aAddChap.triggered.connect(
285
286
  qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=2, isNote=False)
286
287
  )
287
288
 
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)
289
+ self.aAddPart = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h1"]))
290
+ self.aAddPart.triggered.connect(
291
+ qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=1, isNote=False)
292
+ )
293
+
294
+ self.aAddEmpty = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["document"]))
295
+ self.aAddEmpty.triggered.connect(
296
+ qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=0, isNote=False)
291
297
  )
292
298
 
293
- self.aAddNote = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["note"]))
299
+ self.aAddNote = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["note"]))
294
300
  self.aAddNote.triggered.connect(
295
301
  qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=1, isNote=True)
296
302
  )
297
303
 
298
- self.aAddFolder = self.mAdd.addAction(trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
304
+ self.aAddFolder = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["folder"]))
299
305
  self.aAddFolder.triggered.connect(
300
306
  qtLambda(self.projTree.newTreeItem, nwItemType.FOLDER)
301
307
  )
@@ -305,24 +311,24 @@ class GuiProjectToolBar(QWidget):
305
311
  self.mTemplates.menuItemTriggered.connect(lambda h: self.newDocumentFromTemplate.emit(h))
306
312
  self.mAdd.addMenu(self.mTemplates)
307
313
 
308
- self.mAddRoot = self.mAdd.addMenu(trConst(nwLabels.ITEM_DESCRIPTION["root"]))
314
+ self.mAddRoot = qtAddMenu(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["root"]))
309
315
  self._buildRootMenu()
310
316
 
311
317
  self.tbAdd = NIconToolButton(self, iSz)
312
- self.tbAdd.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
318
+ self.tbAdd.setToolTip("{0} [Ctrl+N]".format(self.tr("Add Item")))
313
319
  self.tbAdd.setShortcut("Ctrl+N")
314
320
  self.tbAdd.setMenu(self.mAdd)
315
321
 
316
322
  # More Options Menu
317
323
  self.mMore = QMenu(self)
318
324
 
319
- self.aExpand = self.mMore.addAction(self.tr("Expand All"))
325
+ self.aExpand = qtAddAction(self.mMore, self.tr("Expand All"))
320
326
  self.aExpand.triggered.connect(self.projTree.expandAll)
321
327
 
322
- self.aCollapse = self.mMore.addAction(self.tr("Collapse All"))
328
+ self.aCollapse = qtAddAction(self.mMore, self.tr("Collapse All"))
323
329
  self.aCollapse.triggered.connect(self.projTree.collapseAll)
324
330
 
325
- self.aEmptyTrash = self.mMore.addAction(self.tr("Empty Trash"))
331
+ self.aEmptyTrash = qtAddAction(self.mMore, self.tr("Empty Trash"))
326
332
  self.aEmptyTrash.triggered.connect(self.projTree.emptyTrash)
327
333
 
328
334
  self.tbMore = NIconToolButton(self, iSz)
@@ -337,7 +343,7 @@ class GuiProjectToolBar(QWidget):
337
343
  self.outerBox.addWidget(self.tbMoveD)
338
344
  self.outerBox.addWidget(self.tbAdd)
339
345
  self.outerBox.addWidget(self.tbMore)
340
- self.outerBox.setContentsMargins(mPx, mPx, 0, mPx)
346
+ self.outerBox.setContentsMargins(4, 2, 0, 2)
341
347
  self.outerBox.setSpacing(0)
342
348
 
343
349
  self.setLayout(self.outerBox)
@@ -353,10 +359,6 @@ class GuiProjectToolBar(QWidget):
353
359
 
354
360
  def updateTheme(self) -> None:
355
361
  """Update theme elements."""
356
- qPalette = self.palette()
357
- qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
358
- self.setPalette(qPalette)
359
-
360
362
  buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
361
363
  self.tbQuick.setStyleSheet(buttonStyle)
362
364
  self.tbMoveU.setStyleSheet(buttonStyle)
@@ -364,17 +366,18 @@ class GuiProjectToolBar(QWidget):
364
366
  self.tbAdd.setStyleSheet(buttonStyle)
365
367
  self.tbMore.setStyleSheet(buttonStyle)
366
368
 
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")
369
+ self.tbQuick.setThemeIcon("bookmarks", "blue")
370
+ self.tbMoveU.setThemeIcon("chevron_up", "blue")
371
+ self.tbMoveD.setThemeIcon("chevron_down", "blue")
372
+ self.tbAdd.setThemeIcon("add", "green")
373
+ self.tbMore.setThemeIcon("more_vertical")
372
374
 
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"))
375
+ self.aAddScene.setIcon(SHARED.theme.getIcon("prj_scene", "scene"))
376
+ self.aAddChap.setIcon(SHARED.theme.getIcon("prj_chapter", "chapter"))
377
+ self.aAddPart.setIcon(SHARED.theme.getIcon("prj_title", "title"))
378
+ self.aAddEmpty.setIcon(SHARED.theme.getIcon("prj_document", "file"))
379
+ self.aAddNote.setIcon(SHARED.theme.getIcon("prj_note", "note"))
380
+ self.aAddFolder.setIcon(SHARED.theme.getIcon("prj_folder", "folder"))
378
381
 
379
382
  self.buildTemplatesMenu()
380
383
  self.buildQuickLinksMenu()
@@ -393,9 +396,9 @@ class GuiProjectToolBar(QWidget):
393
396
  logger.debug("Rebuilding quick links menu")
394
397
  self.mQuick.clear()
395
398
  for tHandle, nwItem in SHARED.project.tree.iterRoots(None):
396
- action = self.mQuick.addAction(nwItem.itemName)
399
+ action = qtAddAction(self.mQuick, nwItem.itemName)
397
400
  action.setData(tHandle)
398
- action.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass]))
401
+ action.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[nwItem.itemClass], "root"))
399
402
  action.triggered.connect(
400
403
  qtLambda(self.projView.setSelectedHandle, tHandle, doScroll=True)
401
404
  )
@@ -429,9 +432,10 @@ class GuiProjectToolBar(QWidget):
429
432
  """
430
433
  nwItem = SHARED.project.tree[tHandle]
431
434
  allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed()
432
- self.aAddEmpty.setVisible(allowDoc)
433
- self.aAddChap.setVisible(allowDoc)
434
435
  self.aAddScene.setVisible(allowDoc)
436
+ self.aAddChap.setVisible(allowDoc)
437
+ self.aAddPart.setVisible(allowDoc)
438
+ self.aAddEmpty.setVisible(allowDoc)
435
439
  return
436
440
 
437
441
  ##
@@ -441,8 +445,8 @@ class GuiProjectToolBar(QWidget):
441
445
  def _buildRootMenu(self) -> None:
442
446
  """Build the rood folder menu."""
443
447
  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]))
448
+ aNew = qtAddAction(self.mAddRoot, trConst(nwLabels.CLASS_NAME[itemClass]))
449
+ aNew.setIcon(SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass], "root"))
446
450
  aNew.triggered.connect(
447
451
  qtLambda(self.projTree.newTreeItem, nwItemType.ROOT, itemClass)
448
452
  )
@@ -507,7 +511,6 @@ class GuiProjectTree(QTreeView):
507
511
  self.customContextMenuRequested.connect(self.openContextMenu)
508
512
 
509
513
  # Connect signals
510
- self.clicked.connect(self._onSingleClick)
511
514
  self.doubleClicked.connect(self._onDoubleClick)
512
515
  self.collapsed.connect(self._onNodeCollapsed)
513
516
  self.expanded.connect(self._onNodeExpanded)
@@ -521,7 +524,6 @@ class GuiProjectTree(QTreeView):
521
524
 
522
525
  def initSettings(self) -> None:
523
526
  """Set or update tree widget settings."""
524
- # Scroll bars
525
527
  if CONFIG.hideVScroll:
526
528
  self.setVerticalScrollBarPolicy(QtScrollAlwaysOff)
527
529
  else:
@@ -558,21 +560,26 @@ class GuiProjectTree(QTreeView):
558
560
 
559
561
  def loadModel(self) -> None:
560
562
  """Load and prepare a new project model."""
563
+ if selectModelOld := self.selectionModel():
564
+ selectModelOld.disconnect()
565
+
561
566
  self.setModel(SHARED.project.tree.model)
562
567
 
563
568
  # Lock the column sizes
564
569
  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)
570
+
571
+ if header := self.header():
572
+ header.setStretchLastSection(False)
573
+ header.setMinimumSectionSize(iPx + 6)
574
+ header.setSectionResizeMode(ProjectNode.C_NAME, QtHeaderStretch)
575
+ header.setSectionResizeMode(ProjectNode.C_COUNT, QtHeaderToContents)
576
+ header.setSectionResizeMode(ProjectNode.C_ACTIVE, QtHeaderFixed)
577
+ header.setSectionResizeMode(ProjectNode.C_STATUS, QtHeaderFixed)
578
+ header.resizeSection(ProjectNode.C_ACTIVE, iPx + 6)
579
+ header.resizeSection(ProjectNode.C_STATUS, iPx + 6)
580
+
581
+ if selectModelNew := self.selectionModel():
582
+ selectModelNew.currentChanged.connect(self._onSelectionChange)
576
583
 
577
584
  self.restoreExpandedState()
578
585
 
@@ -612,12 +619,12 @@ class GuiProjectTree(QTreeView):
612
619
  tHandle = None
613
620
  if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
614
621
 
615
- pos = -1
622
+ sPos = -1
616
623
  if (node := self._getNode(self.currentIndex())) and (itemRoot := node.item.itemRoot):
617
624
  if root := SHARED.project.tree.nodes.get(itemRoot):
618
- pos = root.row() + 1
625
+ sPos = root.row() + 1
619
626
 
620
- tHandle = SHARED.project.newRoot(itemClass, pos)
627
+ tHandle = SHARED.project.newRoot(itemClass, sPos)
621
628
  self.restoreExpandedState()
622
629
 
623
630
  elif itemType in (nwItemType.FILE, nwItemType.FOLDER):
@@ -630,52 +637,43 @@ class GuiProjectTree(QTreeView):
630
637
  SHARED.error(self.tr("Cannot add new files or folders to the Trash folder."))
631
638
  return
632
639
 
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
640
+ # Set default label and determine where to put the new item
641
+ nNote = isNote
642
+ nLevel = hLevel
639
643
  if itemType == nwItemType.FILE:
640
644
  if copyDoc and (cItem := SHARED.project.tree[copyDoc]):
645
+ nNote = cItem.isNoteLayout()
646
+ nLevel = nwStyles.H_LEVEL.get(cItem.mainHeading, 0)
641
647
  newLabel = cItem.itemName
642
- asChild = sIsParent and node.item.isDocumentLayout()
643
648
  elif isNote:
644
649
  newLabel = self.tr("New Note")
645
- asChild = sIsParent
650
+ elif hLevel == 1:
651
+ newLabel = self.tr("New Part")
646
652
  elif hLevel == 2:
647
653
  newLabel = self.tr("New Chapter")
648
- asChild = sIsParent and node.item.isDocumentLayout() and sLevel < 2
649
654
  elif hLevel == 3:
650
655
  newLabel = self.tr("New Scene")
651
- asChild = sIsParent and node.item.isDocumentLayout() and sLevel < 3
652
656
  else:
653
657
  newLabel = self.tr("New Document")
654
- asChild = sIsParent and node.item.isDocumentLayout()
655
658
  else:
656
659
  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)
660
+ nLevel = 0
661
+
662
+ sHandle, sPos = SHARED.project.tree.pickParent(node, nLevel, nNote)
663
+ if sHandle:
664
+ newLabel, dlgOk = GuiEditLabel.getLabel(self, text=newLabel)
665
+ if dlgOk:
666
+ # Add the file or folder
667
+ if itemType == nwItemType.FILE:
668
+ if tHandle := SHARED.project.newFile(newLabel, sHandle, sPos):
669
+ if copyDoc:
670
+ SHARED.project.copyFileContent(tHandle, copyDoc)
671
+ elif hLevel > 0:
672
+ SHARED.project.writeNewFile(tHandle, hLevel, not nNote)
673
+ SHARED.project.index.reIndexHandle(tHandle)
674
+ SHARED.project.tree.refreshItems([tHandle])
675
+ else:
676
+ tHandle = SHARED.project.newFolder(newLabel, sHandle, sPos)
679
677
 
680
678
  # Select the new item automatically
681
679
  if tHandle:
@@ -966,7 +964,8 @@ class GuiProjectTree(QTreeView):
966
964
  ctxMenu.buildMultiSelectMenu()
967
965
  else:
968
966
  ctxMenu.buildSingleSelectMenu()
969
- ctxMenu.exec(self.viewport().mapToGlobal(point))
967
+ if viewport := self.viewport():
968
+ ctxMenu.exec(viewport.mapToGlobal(point))
970
969
  ctxMenu.setParent(None)
971
970
  return
972
971
 
@@ -974,10 +973,10 @@ class GuiProjectTree(QTreeView):
974
973
  # Private Slots
975
974
  ##
976
975
 
977
- @pyqtSlot(QModelIndex)
978
- def _onSingleClick(self, index: QModelIndex) -> None:
976
+ @pyqtSlot(QModelIndex, QModelIndex)
977
+ def _onSelectionChange(self, current: QModelIndex, previous: QModelIndex) -> None:
979
978
  """The user changed which item is selected."""
980
- if node := self._getNode(index):
979
+ if node := self._getNode(current):
981
980
  self.projView.selectedItemChanged.emit(node.item.itemHandle)
982
981
  return
983
982
 
@@ -1026,7 +1025,7 @@ class GuiProjectTree(QTreeView):
1026
1025
  return [i for i in self.selectedIndexes() if i.column() == 0]
1027
1026
 
1028
1027
  def _getModel(self) -> ProjectModel | None:
1029
- """Return a project node corresponding to a model index."""
1028
+ """Return the model, if it exists."""
1030
1029
  if isinstance(model := self.model(), ProjectModel):
1031
1030
  return model
1032
1031
  return None
@@ -1087,7 +1086,8 @@ class _UpdatableMenu(QMenu):
1087
1086
 
1088
1087
  def setActionsVisible(self, value: bool) -> None:
1089
1088
  """Set the visibility of root action."""
1090
- self.menuAction().setVisible(value)
1089
+ if action := self.menuAction():
1090
+ action.setVisible(value)
1091
1091
  return
1092
1092
 
1093
1093
  ##
@@ -1103,7 +1103,7 @@ class _UpdatableMenu(QMenu):
1103
1103
 
1104
1104
  class _TreeContextMenu(QMenu):
1105
1105
 
1106
- __slots__ = ("_tree", "_view", "_node", "_item", "_model", "_handle", "_indices", "_children")
1106
+ __slots__ = ("_children", "_handle", "_indices", "_item", "_model", "_node", "_tree", "_view")
1107
1107
 
1108
1108
  def __init__(
1109
1109
  self, projTree: GuiProjectTree, model: ProjectModel,
@@ -1131,7 +1131,7 @@ class _TreeContextMenu(QMenu):
1131
1131
 
1132
1132
  def buildTrashMenu(self) -> None:
1133
1133
  """Build the special menu for the Trash folder."""
1134
- action = self.addAction(self.tr("Empty Trash"))
1134
+ action = qtAddAction(self, self.tr("Empty Trash"))
1135
1135
  action.triggered.connect(self._tree.emptyTrash)
1136
1136
  if self._children:
1137
1137
  self._expandCollapse()
@@ -1152,7 +1152,7 @@ class _TreeContextMenu(QMenu):
1152
1152
  self.addSeparator()
1153
1153
 
1154
1154
  # Edit Item Settings
1155
- action = self.addAction(self.tr("Rename"))
1155
+ action = qtAddAction(self, self.tr("Rename"))
1156
1156
  action.triggered.connect(qtLambda(self._view.renameTreeItem, self._handle))
1157
1157
  if isFile:
1158
1158
  self._itemHeader()
@@ -1167,7 +1167,7 @@ class _TreeContextMenu(QMenu):
1167
1167
  # Process Item
1168
1168
  if self._children:
1169
1169
  self._expandCollapse()
1170
- action = self.addAction(self.tr("Duplicate"))
1170
+ action = qtAddAction(self, self.tr("Duplicate"))
1171
1171
  action.triggered.connect(qtLambda(self._tree.duplicateFromHandle, self._handle))
1172
1172
  self._deleteOrTrash()
1173
1173
 
@@ -1187,12 +1187,12 @@ class _TreeContextMenu(QMenu):
1187
1187
 
1188
1188
  def _docActions(self) -> None:
1189
1189
  """Add document actions."""
1190
- action = self.addAction(self.tr("Open Document"))
1190
+ action = qtAddAction(self, self.tr("Open Document"))
1191
1191
  action.triggered.connect(qtLambda(
1192
1192
  self._view.openDocumentRequest.emit,
1193
1193
  self._handle, nwDocMode.EDIT, "", True
1194
1194
  ))
1195
- action = self.addAction(self.tr("View Document"))
1195
+ action = qtAddAction(self, self.tr("View Document"))
1196
1196
  action.triggered.connect(qtLambda(
1197
1197
  self._view.openDocumentRequest.emit,
1198
1198
  self._handle, nwDocMode.VIEW, "", False
@@ -1201,10 +1201,11 @@ class _TreeContextMenu(QMenu):
1201
1201
 
1202
1202
  def _itemCreation(self) -> None:
1203
1203
  """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)
1204
+ menu = qtAddMenu(self, self.tr("Create New ..."))
1207
1205
  menu.addAction(self._view.projBar.aAddScene)
1206
+ menu.addAction(self._view.projBar.aAddChap)
1207
+ menu.addAction(self._view.projBar.aAddPart)
1208
+ menu.addAction(self._view.projBar.aAddEmpty)
1208
1209
  menu.addAction(self._view.projBar.aAddNote)
1209
1210
  menu.addAction(self._view.projBar.aAddFolder)
1210
1211
  return
@@ -1213,7 +1214,7 @@ class _TreeContextMenu(QMenu):
1213
1214
  """Check if there is a header that can be used for rename."""
1214
1215
  SHARED.saveEditor()
1215
1216
  if hItem := SHARED.project.index.getItemHeading(self._handle, "T0001"):
1216
- action = self.addAction(self.tr("Rename to Heading"))
1217
+ action = qtAddAction(self, self.tr("Rename to Heading"))
1217
1218
  action.triggered.connect(
1218
1219
  qtLambda(self._view.renameTreeItem, self._handle, hItem.title)
1219
1220
  )
@@ -1222,50 +1223,54 @@ class _TreeContextMenu(QMenu):
1222
1223
  def _itemActive(self) -> None:
1223
1224
  """Add Active/Inactive actions."""
1224
1225
  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)
1226
+ mSub = qtAddMenu(self, self.tr("Set Active to ..."))
1227
+ aOne = qtAddAction(mSub, self._tree.trActive)
1228
+ aOne.setIcon(SHARED.theme.getIcon("checked", "green"))
1227
1229
  aOne.triggered.connect(qtLambda(self._iterItemActive, True))
1228
- aTwo = mSub.addAction(SHARED.theme.getIcon("unchecked"), self._tree.trInactive)
1230
+ aTwo = qtAddAction(mSub, self._tree.trInactive)
1231
+ aTwo.setIcon(SHARED.theme.getIcon("unchecked", "red"))
1229
1232
  aTwo.triggered.connect(qtLambda(self._iterItemActive, False))
1230
1233
  else:
1231
- action = self.addAction(self.tr("Toggle Active"))
1234
+ action = qtAddAction(self, self.tr("Toggle Active"))
1232
1235
  action.triggered.connect(self._toggleItemActive)
1233
1236
  return
1234
1237
 
1235
1238
  def _itemStatusImport(self, multi: bool) -> None:
1236
1239
  """Add actions for changing status or importance."""
1237
1240
  if self._item.isNovelLike():
1238
- menu = self.addMenu(self.tr("Set Status to ..."))
1241
+ menu = qtAddMenu(self, self.tr("Set Status to ..."))
1239
1242
  current = self._item.itemStatus
1240
1243
  for key, entry in SHARED.project.data.itemStatus.iterItems():
1241
1244
  name = entry.name
1242
1245
  if not multi and current == key:
1243
1246
  name += f" ({nwUnicode.U_CHECK})"
1244
- action = menu.addAction(entry.icon, name)
1247
+ action = qtAddAction(menu, name)
1248
+ action.setIcon(entry.icon)
1245
1249
  if multi:
1246
1250
  action.triggered.connect(qtLambda(self._iterSetItemStatus, key))
1247
1251
  else:
1248
1252
  action.triggered.connect(qtLambda(self._changeItemStatus, key))
1249
1253
  menu.addSeparator()
1250
- action = menu.addAction(self.tr("Manage Labels ..."))
1254
+ action = qtAddAction(menu, self.tr("Manage Labels ..."))
1251
1255
  action.triggered.connect(qtLambda(
1252
1256
  self._view.projectSettingsRequest.emit,
1253
1257
  GuiProjectSettings.PAGE_STATUS
1254
1258
  ))
1255
1259
  else:
1256
- menu = self.addMenu(self.tr("Set Importance to ..."))
1260
+ menu = qtAddMenu(self, self.tr("Set Importance to ..."))
1257
1261
  current = self._item.itemImport
1258
1262
  for key, entry in SHARED.project.data.itemImport.iterItems():
1259
1263
  name = entry.name
1260
1264
  if not multi and current == key:
1261
1265
  name += f" ({nwUnicode.U_CHECK})"
1262
- action = menu.addAction(entry.icon, name)
1266
+ action = qtAddAction(menu, name)
1267
+ action.setIcon(entry.icon)
1263
1268
  if multi:
1264
1269
  action.triggered.connect(qtLambda(self._iterSetItemImport, key))
1265
1270
  else:
1266
1271
  action.triggered.connect(qtLambda(self._changeItemImport, key))
1267
1272
  menu.addSeparator()
1268
- action = menu.addAction(self.tr("Manage Labels ..."))
1273
+ action = qtAddAction(menu, self.tr("Manage Labels ..."))
1269
1274
  action.triggered.connect(qtLambda(
1270
1275
  self._view.projectSettingsRequest.emit,
1271
1276
  GuiProjectSettings.PAGE_IMPORT
@@ -1274,7 +1279,7 @@ class _TreeContextMenu(QMenu):
1274
1279
 
1275
1280
  def _itemTransform(self, isFile: bool, isFolder: bool) -> None:
1276
1281
  """Add actions for the Transform menu."""
1277
- menu = self.addMenu(self.tr("Transform ..."))
1282
+ menu = qtAddMenu(self, self.tr("Transform ..."))
1278
1283
 
1279
1284
  trDoc = trConst(nwLabels.LAYOUT_NAME[nwItemLayout.DOCUMENT])
1280
1285
  trNote = trConst(nwLabels.LAYOUT_NAME[nwItemLayout.NOTE])
@@ -1284,42 +1289,42 @@ class _TreeContextMenu(QMenu):
1284
1289
  isNoteFile = isFile and self._item.isNoteLayout()
1285
1290
 
1286
1291
  if isNoteFile and self._item.documentAllowed():
1287
- action = menu.addAction(self.tr("Convert to {0}").format(trDoc))
1292
+ action = qtAddAction(menu, self.tr("Convert to {0}").format(trDoc))
1288
1293
  action.triggered.connect(qtLambda(self._changeItemLayout, loDoc))
1289
1294
 
1290
1295
  if isDocFile:
1291
- action = menu.addAction(self.tr("Convert to {0}").format(trNote))
1296
+ action = qtAddAction(menu, self.tr("Convert to {0}").format(trNote))
1292
1297
  action.triggered.connect(qtLambda(self._changeItemLayout, loNote))
1293
1298
 
1294
1299
  if isFolder and self._item.documentAllowed():
1295
- action = menu.addAction(self.tr("Convert to {0}").format(trDoc))
1300
+ action = qtAddAction(menu, self.tr("Convert to {0}").format(trDoc))
1296
1301
  action.triggered.connect(qtLambda(self._convertFolderToFile, loDoc))
1297
1302
 
1298
1303
  if isFolder:
1299
- action = menu.addAction(self.tr("Convert to {0}").format(trNote))
1304
+ action = qtAddAction(menu, self.tr("Convert to {0}").format(trNote))
1300
1305
  action.triggered.connect(qtLambda(self._convertFolderToFile, loNote))
1301
1306
 
1302
1307
  if self._children and isFile:
1303
- action = menu.addAction(self.tr("Merge Child Items into Self"))
1308
+ action = qtAddAction(menu, self.tr("Merge Child Items into Self"))
1304
1309
  action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, False))
1305
- action = menu.addAction(self.tr("Merge Child Items into New"))
1310
+ action = qtAddAction(menu, self.tr("Merge Child Items into New"))
1306
1311
  action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, True))
1307
1312
 
1308
1313
  if self._children and isFolder:
1309
- action = menu.addAction(self.tr("Merge Documents in Folder"))
1314
+ action = qtAddAction(menu, self.tr("Merge Documents in Folder"))
1310
1315
  action.triggered.connect(qtLambda(self._tree.mergeDocuments, self._handle, True))
1311
1316
 
1312
1317
  if isFile:
1313
- action = menu.addAction(self.tr("Split Document by Headings"))
1318
+ action = qtAddAction(menu, self.tr("Split Document by Headings"))
1314
1319
  action.triggered.connect(qtLambda(self._tree.splitDocument, self._handle))
1315
1320
 
1316
1321
  return
1317
1322
 
1318
1323
  def _expandCollapse(self) -> None:
1319
1324
  """Add actions for expand and collapse."""
1320
- action = self.addAction(self.tr("Expand All"))
1325
+ action = qtAddAction(self, self.tr("Expand All"))
1321
1326
  action.triggered.connect(qtLambda(self._tree.expandFromIndex, self._indices[0]))
1322
- action = self.addAction(self.tr("Collapse All"))
1327
+ action = qtAddAction(self, self.tr("Collapse All"))
1323
1328
  action.triggered.connect(qtLambda(self._tree.collapseFromIndex, self._indices[0]))
1324
1329
  return
1325
1330
 
@@ -1327,12 +1332,12 @@ class _TreeContextMenu(QMenu):
1327
1332
  """Add move to Trash action."""
1328
1333
  if (
1329
1334
  self._model.trashSelection(self._indices)
1330
- or len(self._indices) == 1 and self._item.isRootType()
1335
+ or (len(self._indices) == 1 and self._item.isRootType())
1331
1336
  ):
1332
1337
  text = self.tr("Delete Permanently")
1333
1338
  else:
1334
1339
  text = self.tr("Move to Trash")
1335
- action = self.addAction(text)
1340
+ action = qtAddAction(self, text)
1336
1341
  action.triggered.connect(self._tree.processDeleteRequest)
1337
1342
  return
1338
1343
 
@@ -1399,6 +1404,7 @@ class _TreeContextMenu(QMenu):
1399
1404
  if itemLayout == nwItemLayout.DOCUMENT and self._item.documentAllowed():
1400
1405
  self._item.setLayout(nwItemLayout.DOCUMENT)
1401
1406
  self._item.notifyToRefresh()
1407
+ self._item.notifyNovelStructureChange()
1402
1408
  elif itemLayout == nwItemLayout.NOTE:
1403
1409
  self._item.setLayout(nwItemLayout.NOTE)
1404
1410
  self._item.notifyToRefresh()
@@ -1415,6 +1421,7 @@ class _TreeContextMenu(QMenu):
1415
1421
  self._item.setType(nwItemType.FILE)
1416
1422
  self._item.setLayout(nwItemLayout.DOCUMENT)
1417
1423
  self._item.notifyToRefresh()
1424
+ self._item.notifyNovelStructureChange()
1418
1425
  elif msgYes and itemLayout == nwItemLayout.NOTE:
1419
1426
  self._item.setType(nwItemType.FILE)
1420
1427
  self._item.setLayout(nwItemLayout.NOTE)