novelWriter 2.7.4__py3-none-any.whl → 2.8b1__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 (196) hide show
  1. novelwriter/__init__.py +8 -7
  2. novelwriter/assets/icons/font_awesome.icons +22 -4
  3. novelwriter/assets/icons/material_filled_normal.icons +20 -2
  4. novelwriter/assets/icons/material_filled_thin.icons +20 -2
  5. novelwriter/assets/icons/material_rounded_normal.icons +20 -2
  6. novelwriter/assets/icons/material_rounded_thin.icons +20 -2
  7. novelwriter/assets/icons/material_sharp_normal.icons +20 -2
  8. novelwriter/assets/icons/material_sharp_thin.icons +20 -2
  9. novelwriter/assets/icons/remix_filled.icons +20 -2
  10. novelwriter/assets/icons/remix_outline.icons +20 -2
  11. novelwriter/assets/images/welcome.webp +0 -0
  12. novelwriter/assets/manual.pdf +0 -0
  13. novelwriter/assets/manual_fr.pdf +0 -0
  14. novelwriter/assets/sample.zip +0 -0
  15. novelwriter/assets/text/credits_en.htm +61 -11
  16. novelwriter/assets/themes/aura.conf +97 -0
  17. novelwriter/assets/themes/aura_bright.conf +95 -0
  18. novelwriter/assets/themes/aura_soft.conf +97 -0
  19. novelwriter/assets/themes/b2t_garden_dark.conf +97 -0
  20. novelwriter/assets/themes/b2t_garden_light.conf +97 -0
  21. novelwriter/assets/themes/b2t_suburb_dark.conf +97 -0
  22. novelwriter/assets/themes/b2t_suburb_light.conf +97 -0
  23. novelwriter/assets/themes/b4t_classic_o_dark.conf +97 -0
  24. novelwriter/assets/themes/b4t_classic_o_light.conf +97 -0
  25. novelwriter/assets/themes/b4t_modern_c_dark.conf +97 -0
  26. novelwriter/assets/themes/b4t_modern_c_light.conf +97 -0
  27. novelwriter/assets/themes/blue_streak_dark.conf +97 -0
  28. novelwriter/assets/themes/blue_streak_light.conf +97 -0
  29. novelwriter/assets/themes/castle_day.conf +95 -0
  30. novelwriter/assets/themes/castle_night.conf +95 -0
  31. novelwriter/assets/themes/catppuccin_latte.conf +97 -0
  32. novelwriter/assets/themes/catppuccin_mocha.conf +97 -0
  33. novelwriter/assets/themes/chalky_soil.conf +95 -0
  34. novelwriter/assets/themes/chernozem.conf +95 -0
  35. novelwriter/assets/themes/cyberpunk_night.conf +88 -40
  36. novelwriter/assets/themes/default_dark.conf +89 -41
  37. novelwriter/assets/themes/default_light.conf +89 -41
  38. novelwriter/assets/themes/dracula.conf +91 -42
  39. novelwriter/assets/themes/espresso.conf +97 -0
  40. novelwriter/assets/themes/everforest_dark.conf +97 -0
  41. novelwriter/assets/themes/everforest_light.conf +97 -0
  42. novelwriter/assets/themes/floral_daydream.conf +95 -0
  43. novelwriter/assets/themes/floral_midnight.conf +95 -0
  44. novelwriter/assets/themes/full_moon.conf +95 -0
  45. novelwriter/assets/themes/grey_dark.conf +97 -0
  46. novelwriter/assets/themes/grey_light.conf +97 -0
  47. novelwriter/assets/themes/horizon_dark.conf +97 -0
  48. novelwriter/assets/themes/horizon_light.conf +97 -0
  49. novelwriter/assets/themes/jewel_case_dark.conf +95 -0
  50. novelwriter/assets/themes/jewel_case_light.conf +95 -0
  51. novelwriter/assets/themes/lcars.conf +97 -0
  52. novelwriter/assets/themes/light_owl.conf +117 -0
  53. novelwriter/assets/themes/new_moon.conf +97 -0
  54. novelwriter/assets/themes/night_owl.conf +117 -0
  55. novelwriter/assets/themes/noctis.conf +129 -0
  56. novelwriter/assets/themes/noctis_lux.conf +129 -0
  57. novelwriter/assets/themes/nord.conf +97 -0
  58. novelwriter/assets/themes/nordlicht.conf +95 -0
  59. novelwriter/assets/themes/otium_dark.conf +95 -0
  60. novelwriter/assets/themes/otium_light.conf +95 -0
  61. novelwriter/assets/themes/paragon.conf +96 -0
  62. novelwriter/assets/themes/primer_light.conf +97 -0
  63. novelwriter/assets/themes/primer_night.conf +97 -0
  64. novelwriter/assets/themes/rose_pine.conf +97 -0
  65. novelwriter/assets/themes/rose_pine_dawn.conf +97 -0
  66. novelwriter/assets/themes/ruby_day.conf +95 -0
  67. novelwriter/assets/themes/ruby_night.conf +95 -0
  68. novelwriter/assets/themes/selenium_dark.conf +95 -0
  69. novelwriter/assets/themes/selenium_light.conf +95 -0
  70. novelwriter/assets/themes/sepia_dark.conf +95 -0
  71. novelwriter/assets/themes/sepia_light.conf +95 -0
  72. novelwriter/assets/themes/snazzy.conf +102 -40
  73. novelwriter/assets/themes/solarized_dark.conf +108 -40
  74. novelwriter/assets/themes/solarized_light.conf +108 -40
  75. novelwriter/assets/themes/sultana_light.conf +95 -0
  76. novelwriter/assets/themes/sultana_night.conf +95 -0
  77. novelwriter/assets/themes/tango_dark.conf +111 -0
  78. novelwriter/assets/themes/tango_light.conf +111 -0
  79. novelwriter/assets/themes/tomorrow.conf +117 -0
  80. novelwriter/assets/themes/tomorrow_night.conf +117 -0
  81. novelwriter/assets/themes/tomorrow_night_blue.conf +117 -0
  82. novelwriter/assets/themes/tomorrow_night_bright.conf +117 -0
  83. novelwriter/assets/themes/tomorrow_night_eighties.conf +117 -0
  84. novelwriter/assets/themes/vivid_black_green.conf +97 -0
  85. novelwriter/assets/themes/vivid_black_red.conf +97 -0
  86. novelwriter/assets/themes/vivid_white_green.conf +97 -0
  87. novelwriter/assets/themes/vivid_white_red.conf +97 -0
  88. novelwriter/assets/themes/warpgate.conf +96 -0
  89. novelwriter/assets/themes/waterlily_dark.conf +95 -0
  90. novelwriter/assets/themes/waterlily_light.conf +95 -0
  91. novelwriter/common.py +47 -17
  92. novelwriter/config.py +57 -62
  93. novelwriter/constants.py +32 -6
  94. novelwriter/core/buildsettings.py +3 -23
  95. novelwriter/core/coretools.py +21 -25
  96. novelwriter/core/docbuild.py +4 -9
  97. novelwriter/core/document.py +2 -6
  98. novelwriter/core/index.py +33 -53
  99. novelwriter/core/indexdata.py +17 -22
  100. novelwriter/core/item.py +11 -35
  101. novelwriter/core/itemmodel.py +5 -21
  102. novelwriter/core/novelmodel.py +3 -7
  103. novelwriter/core/options.py +3 -4
  104. novelwriter/core/project.py +31 -21
  105. novelwriter/core/projectdata.py +2 -21
  106. novelwriter/core/projectxml.py +13 -21
  107. novelwriter/core/sessions.py +2 -4
  108. novelwriter/core/spellcheck.py +12 -13
  109. novelwriter/core/status.py +27 -20
  110. novelwriter/core/storage.py +5 -10
  111. novelwriter/core/tree.py +6 -15
  112. novelwriter/dialogs/about.py +9 -10
  113. novelwriter/dialogs/docmerge.py +17 -14
  114. novelwriter/dialogs/docsplit.py +18 -14
  115. novelwriter/dialogs/editlabel.py +15 -9
  116. novelwriter/dialogs/preferences.py +69 -68
  117. novelwriter/dialogs/projectsettings.py +88 -67
  118. novelwriter/dialogs/quotes.py +15 -10
  119. novelwriter/dialogs/wordlist.py +18 -21
  120. novelwriter/enum.py +75 -30
  121. novelwriter/error.py +6 -11
  122. novelwriter/extensions/configlayout.py +8 -34
  123. novelwriter/extensions/eventfilters.py +3 -3
  124. novelwriter/extensions/modified.py +87 -32
  125. novelwriter/extensions/novelselector.py +13 -12
  126. novelwriter/extensions/pagedsidebar.py +10 -18
  127. novelwriter/extensions/progressbars.py +5 -11
  128. novelwriter/extensions/statusled.py +3 -6
  129. novelwriter/extensions/switch.py +8 -11
  130. novelwriter/extensions/switchbox.py +2 -11
  131. novelwriter/extensions/versioninfo.py +6 -7
  132. novelwriter/formats/shared.py +10 -2
  133. novelwriter/formats/todocx.py +15 -37
  134. novelwriter/formats/tohtml.py +52 -61
  135. novelwriter/formats/tokenizer.py +33 -64
  136. novelwriter/formats/tomarkdown.py +4 -11
  137. novelwriter/formats/toodt.py +12 -71
  138. novelwriter/formats/toqdoc.py +11 -21
  139. novelwriter/formats/toraw.py +2 -6
  140. novelwriter/gui/doceditor.py +207 -245
  141. novelwriter/gui/dochighlight.py +142 -101
  142. novelwriter/gui/docviewer.py +53 -84
  143. novelwriter/gui/docviewerpanel.py +18 -41
  144. novelwriter/gui/editordocument.py +12 -17
  145. novelwriter/gui/itemdetails.py +5 -14
  146. novelwriter/gui/mainmenu.py +24 -32
  147. novelwriter/gui/noveltree.py +13 -51
  148. novelwriter/gui/outline.py +20 -61
  149. novelwriter/gui/projtree.py +40 -96
  150. novelwriter/gui/search.py +9 -24
  151. novelwriter/gui/sidebar.py +54 -22
  152. novelwriter/gui/statusbar.py +7 -22
  153. novelwriter/gui/theme.py +482 -368
  154. novelwriter/guimain.py +87 -101
  155. novelwriter/shared.py +79 -48
  156. novelwriter/splash.py +9 -5
  157. novelwriter/text/comments.py +1 -1
  158. novelwriter/text/counting.py +9 -5
  159. novelwriter/text/patterns.py +20 -15
  160. novelwriter/tools/dictionaries.py +18 -16
  161. novelwriter/tools/lipsum.py +15 -17
  162. novelwriter/tools/manusbuild.py +25 -45
  163. novelwriter/tools/manuscript.py +94 -95
  164. novelwriter/tools/manussettings.py +149 -104
  165. novelwriter/tools/noveldetails.py +10 -24
  166. novelwriter/tools/welcome.py +24 -72
  167. novelwriter/tools/writingstats.py +17 -26
  168. novelwriter/types.py +25 -13
  169. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/METADATA +7 -7
  170. novelwriter-2.8b1.dist-info/RECORD +212 -0
  171. novelwriter/assets/images/welcome-dark.jpg +0 -0
  172. novelwriter/assets/images/welcome-light.jpg +0 -0
  173. novelwriter/assets/syntax/cyberpunk_night.conf +0 -28
  174. novelwriter/assets/syntax/default_dark.conf +0 -42
  175. novelwriter/assets/syntax/default_light.conf +0 -42
  176. novelwriter/assets/syntax/dracula.conf +0 -44
  177. novelwriter/assets/syntax/grey_dark.conf +0 -29
  178. novelwriter/assets/syntax/grey_light.conf +0 -29
  179. novelwriter/assets/syntax/light_owl.conf +0 -49
  180. novelwriter/assets/syntax/night_owl.conf +0 -49
  181. novelwriter/assets/syntax/snazzy.conf +0 -42
  182. novelwriter/assets/syntax/solarized_dark.conf +0 -29
  183. novelwriter/assets/syntax/solarized_light.conf +0 -29
  184. novelwriter/assets/syntax/tango.conf +0 -39
  185. novelwriter/assets/syntax/tomorrow.conf +0 -49
  186. novelwriter/assets/syntax/tomorrow_night.conf +0 -49
  187. novelwriter/assets/syntax/tomorrow_night_blue.conf +0 -49
  188. novelwriter/assets/syntax/tomorrow_night_bright.conf +0 -49
  189. novelwriter/assets/syntax/tomorrow_night_eighties.conf +0 -49
  190. novelwriter/assets/themes/default.conf +0 -3
  191. novelwriter-2.7.4.dist-info/RECORD +0 -163
  192. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/WHEEL +0 -0
  193. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/entry_points.txt +0 -0
  194. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/licenses/LICENSE.md +0 -0
  195. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/licenses/setup/LICENSE-Apache-2.0.txt +0 -0
  196. {novelwriter-2.7.4.dist-info → novelwriter-2.8b1.dist-info}/top_level.txt +0 -0
novelwriter/core/item.py CHANGED
@@ -20,7 +20,7 @@ General Public License for more details.
20
20
 
21
21
  You should have received a copy of the GNU General Public License
22
22
  along with this program. If not, see <https://www.gnu.org/licenses/>.
23
- """
23
+ """ # noqa
24
24
  from __future__ import annotations
25
25
 
26
26
  import logging
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
44
44
 
45
45
 
46
46
  class NWItem:
47
- """Core: Item Data Class
47
+ """Core: Item Data Class.
48
48
 
49
49
  This class holds all the project information about a project item.
50
50
  Each item must be associated with a project and have a valid handle.
@@ -84,16 +84,14 @@ class NWItem:
84
84
  self._wordInit = 0 # Initial character count
85
85
  self._charInit = 0 # Initial word count
86
86
 
87
- return
88
-
89
87
  def __repr__(self) -> str:
90
88
  return f"<NWItem handle={self._handle}, parent={self._parent}, name='{self._name}'>"
91
89
 
92
90
  def __bool__(self) -> bool:
93
- """The truthiness of the class. The handle used to be initiated
94
- to None, but this is no longer the case. It should always
95
- evaluate to True since 2.1-beta1, although unpack and the NWTree
96
- class can leave it as an empty string.
91
+ """Check the truthiness of the class. The handle used to be
92
+ initiated to None, but this is no longer the case. It should
93
+ always evaluate to True since 2.1-beta1, although unpack and the
94
+ NWTree class can leave it as an empty string.
97
95
  """
98
96
  return bool(self._handle)
99
97
 
@@ -206,15 +204,13 @@ class NWItem:
206
204
  meta["cursorPos"] = str(self._cursorPos)
207
205
  name["active"] = yesNo(self._active)
208
206
 
209
- data = {
207
+ return {
210
208
  "name": str(self._name),
211
209
  "itemAttr": item,
212
210
  "metaAttr": meta,
213
211
  "nameAttr": name,
214
212
  }
215
213
 
216
- return data
217
-
218
214
  def unpack(self, data: dict) -> bool:
219
215
  """Set the values from a data dictionary."""
220
216
  item = data.get("itemAttr", {})
@@ -298,13 +294,11 @@ class NWItem:
298
294
  def notifyToRefresh(self) -> None:
299
295
  """Notify GUI that item info needs to be refreshed."""
300
296
  self._project.tree.refreshItems([self._handle])
301
- return
302
297
 
303
298
  def notifyNovelStructureChange(self) -> None:
304
299
  """Notify that the structure of a novel has changed."""
305
300
  if self._root and self._class == nwItemClass.NOVEL:
306
301
  self._project.tree.novelStructureChanged(self._root)
307
- return
308
302
 
309
303
  ##
310
304
  # Lookup Methods
@@ -363,13 +357,13 @@ class NWItem:
363
357
  """
364
358
  if self.isFileType():
365
359
  key = "checked" if self._active else "unchecked"
366
- color = "green" if self._active else "red"
360
+ color = "active" if self._active else "inactive"
367
361
  text = trConst(nwLabels.ACTIVE_NAME[key])
368
- icon = SHARED.theme.getIcon(key, color)
369
362
  else:
363
+ key = "noncheckable"
364
+ color = "disabled"
370
365
  text = ""
371
- icon = SHARED.theme.getIcon("noncheckable", "faded")
372
- return text, icon
366
+ return text, SHARED.theme.getIcon(key, color)
373
367
 
374
368
  ##
375
369
  # Checker Methods
@@ -457,8 +451,6 @@ class NWItem:
457
451
  if self._import is None:
458
452
  self.setImport("New") # This forces a default value lookup
459
453
 
460
- return
461
-
462
454
  ##
463
455
  # Set Item Values
464
456
  ##
@@ -469,7 +461,6 @@ class NWItem:
469
461
  self._name = simplified(name)
470
462
  else:
471
463
  self._name = ""
472
- return
473
464
 
474
465
  def setParent(self, handle: Any) -> None:
475
466
  """Set the parent handle, and ensure it is valid."""
@@ -479,7 +470,6 @@ class NWItem:
479
470
  self._parent = handle
480
471
  else:
481
472
  self._parent = None
482
- return
483
473
 
484
474
  def setRoot(self, handle: Any) -> None:
485
475
  """Set the root handle, and ensure it is valid."""
@@ -489,7 +479,6 @@ class NWItem:
489
479
  self._root = handle
490
480
  else:
491
481
  self._root = None
492
- return
493
482
 
494
483
  def setOrder(self, order: Any) -> None:
495
484
  """Set the item order, and ensure that it is valid. This value
@@ -497,7 +486,6 @@ class NWItem:
497
486
  the moment.
498
487
  """
499
488
  self._order = checkInt(order, 0)
500
- return
501
489
 
502
490
  def setType(self, value: Any) -> None:
503
491
  """Set the item type from either a proper nwItemType, or set it
@@ -510,7 +498,6 @@ class NWItem:
510
498
  else:
511
499
  logger.error("Unrecognised item type '%s'", value)
512
500
  self._type = nwItemType.NO_TYPE
513
- return
514
501
 
515
502
  def setClass(self, value: Any) -> None:
516
503
  """Set the item class from either a proper nwItemClass, or set
@@ -523,7 +510,6 @@ class NWItem:
523
510
  else:
524
511
  logger.error("Unrecognised item class '%s'", value)
525
512
  self._class = nwItemClass.NO_CLASS
526
- return
527
513
 
528
514
  def setLayout(self, value: Any) -> None:
529
515
  """Set the item layout from either a proper nwItemLayout, or set
@@ -536,21 +522,18 @@ class NWItem:
536
522
  else:
537
523
  logger.error("Unrecognised item layout '%s'", value)
538
524
  self._layout = nwItemLayout.NO_LAYOUT
539
- return
540
525
 
541
526
  def setStatus(self, value: Any) -> None:
542
527
  """Set the item status by looking it up in the valid status
543
528
  items of the current project.
544
529
  """
545
530
  self._status = self._project.data.itemStatus.check(value)
546
- return
547
531
 
548
532
  def setImport(self, value: Any) -> None:
549
533
  """Set the item importance by looking it up in the valid import
550
534
  items of the current project.
551
535
  """
552
536
  self._import = self._project.data.itemImport.check(value)
553
- return
554
537
 
555
538
  def setActive(self, state: Any) -> None:
556
539
  """Set the active flag."""
@@ -558,7 +541,6 @@ class NWItem:
558
541
  self._active = state
559
542
  else:
560
543
  self._active = False
561
- return
562
544
 
563
545
  def setExpanded(self, state: Any) -> None:
564
546
  """Set the expanded status of an item in the project tree."""
@@ -566,7 +548,6 @@ class NWItem:
566
548
  self._expanded = state
567
549
  else:
568
550
  self._expanded = False
569
- return
570
551
 
571
552
  ##
572
553
  # Set Document Meta Data
@@ -576,7 +557,6 @@ class NWItem:
576
557
  """Set the main heading level."""
577
558
  if value in nwStyles.H_LEVEL:
578
559
  self._heading = value
579
- return
580
560
 
581
561
  def setCharCount(self, count: Any) -> None:
582
562
  """Set the character count, and ensure that it is an integer."""
@@ -584,7 +564,6 @@ class NWItem:
584
564
  self._charCount = max(0, count)
585
565
  else:
586
566
  self._charCount = 0
587
- return
588
567
 
589
568
  def setWordCount(self, count: Any) -> None:
590
569
  """Set the word count, and ensure that it is an integer."""
@@ -592,7 +571,6 @@ class NWItem:
592
571
  self._wordCount = max(0, count)
593
572
  else:
594
573
  self._wordCount = 0
595
- return
596
574
 
597
575
  def setParaCount(self, count: Any) -> None:
598
576
  """Set the paragraph count, and ensure that it is an integer."""
@@ -600,7 +578,6 @@ class NWItem:
600
578
  self._paraCount = max(0, count)
601
579
  else:
602
580
  self._paraCount = 0
603
- return
604
581
 
605
582
  def setCursorPos(self, position: Any) -> None:
606
583
  """Set the cursor position, and ensure that it is an integer."""
@@ -608,4 +585,3 @@ class NWItem:
608
585
  self._cursorPos = max(0, position)
609
586
  else:
610
587
  self._cursorPos = 0
611
- return
@@ -21,7 +21,7 @@ General Public License for more details.
21
21
 
22
22
  You should have received a copy of the GNU General Public License
23
23
  along with this program. If not, see <https://www.gnu.org/licenses/>.
24
- """
24
+ """ # noqa
25
25
  from __future__ import annotations
26
26
 
27
27
  import logging
@@ -66,7 +66,7 @@ T_NodeData = str | QIcon | QFont | Qt.AlignmentFlag | None
66
66
 
67
67
 
68
68
  class ProjectNode:
69
- """Core: Project Model Node Class
69
+ """Core: Project Model Node Class.
70
70
 
71
71
  The project tree structure is saved as nodes in a tree, starting
72
72
  from a root node. This class makes up these nodes.
@@ -103,7 +103,6 @@ class ProjectNode:
103
103
  self._count = 0
104
104
  self.refresh()
105
105
  self.updateCount()
106
- return
107
106
 
108
107
  def __repr__(self) -> str:
109
108
  return (
@@ -114,7 +113,7 @@ class ProjectNode:
114
113
  )
115
114
 
116
115
  def __bool__(self) -> bool:
117
- """A node should always evaluate to True."""
116
+ # A node should always evaluate to True.
118
117
  return True
119
118
 
120
119
  ##
@@ -162,15 +161,12 @@ class ProjectNode:
162
161
  self._cache[C_STATUS_TIP] = sText
163
162
  self._cache[C_STATUS_ACCESS] = sText
164
163
 
165
- return
166
-
167
164
  def updateCount(self, propagate: bool = True) -> None:
168
165
  """Update counts, and propagate upwards in the tree."""
169
166
  self._count = self._item.mainCount + sum(c._count for c in self._children) # noqa: SLF001
170
167
  self._cache[C_COUNT_TEXT] = f"{self._count:n}"
171
168
  if propagate and (parent := self._parent):
172
169
  parent.updateCount()
173
- return
174
170
 
175
171
  ##
176
172
  # Data Access
@@ -223,7 +219,6 @@ class ProjectNode:
223
219
  self._children.append(child)
224
220
  self._refreshChildrenPos()
225
221
  self._item.notifyNovelStructureChange()
226
- return
227
222
 
228
223
  def takeChild(self, pos: int) -> ProjectNode | None:
229
224
  """Remove a child item and return it."""
@@ -243,7 +238,6 @@ class ProjectNode:
243
238
  self._children.insert(target, node)
244
239
  self._refreshChildrenPos()
245
240
  self._item.notifyNovelStructureChange()
246
- return
247
241
 
248
242
  def setExpanded(self, state: bool) -> None:
249
243
  """Set the node's expanded state."""
@@ -251,7 +245,6 @@ class ProjectNode:
251
245
  self._item.setExpanded(True)
252
246
  else:
253
247
  self._item.setExpanded(False)
254
- return
255
248
 
256
249
  ##
257
250
  # Internal Functions
@@ -262,14 +255,12 @@ class ProjectNode:
262
255
  for node in self._children:
263
256
  children.append(node)
264
257
  node._recursiveAppendChildren(children) # noqa: SLF001
265
- return
266
258
 
267
259
  def _refreshChildrenPos(self) -> None:
268
260
  """Update the row value on all children."""
269
261
  for n, child in enumerate(self._children):
270
262
  child._row = n # noqa: SLF001
271
263
  child.item.setOrder(n)
272
- return
273
264
 
274
265
  def _updateRelationships(self, child: ProjectNode) -> None:
275
266
  """Update a child item's relationships."""
@@ -282,11 +273,10 @@ class ProjectNode:
282
273
  child.item.setParent(None)
283
274
  child.item.setRoot(child.item.itemHandle)
284
275
  child.item.setClassDefaults(child.item.itemClass)
285
- return
286
276
 
287
277
 
288
278
  class ProjectModel(QAbstractItemModel):
289
- """Core: Project Model Class
279
+ """Core: Project Model Class.
290
280
 
291
281
  This class provides the interface for the tree widget used on the
292
282
  GUI. It implements the QModelIndex based interface required, adds
@@ -302,11 +292,9 @@ class ProjectModel(QAbstractItemModel):
302
292
  self._root = ProjectNode(NWItem(tree.project, INV_ROOT))
303
293
  self._root.item.setName("Invisible Root")
304
294
  logger.debug("Ready: ProjectModel")
305
- return
306
295
 
307
296
  def __del__(self) -> None: # pragma: no cover
308
297
  logger.debug("Delete: ProjectModel")
309
- return
310
298
 
311
299
  ##
312
300
  # Properties
@@ -363,7 +351,7 @@ class ProjectModel(QAbstractItemModel):
363
351
  ##
364
352
 
365
353
  def supportedDropActions(self) -> Qt.DropAction:
366
- """Return supported drop actions"""
354
+ """Return supported drop actions."""
367
355
  return Qt.DropAction.MoveAction
368
356
 
369
357
  def mimeTypes(self) -> list[str]:
@@ -445,7 +433,6 @@ class ProjectModel(QAbstractItemModel):
445
433
  self.beginInsertRows(parent, row, row)
446
434
  node.addChild(child, row)
447
435
  self.endInsertRows()
448
- return
449
436
 
450
437
  def removeChild(self, parent: QModelIndex, pos: int) -> ProjectNode | None:
451
438
  """Remove a node from the model and return it."""
@@ -469,7 +456,6 @@ class ProjectModel(QAbstractItemModel):
469
456
  self.beginMoveRows(index.parent(), pos, pos, index.parent(), end)
470
457
  parent.moveChild(pos, new)
471
458
  self.endMoveRows()
472
- return
473
459
 
474
460
  def multiMove(self, indices: list[QModelIndex], target: QModelIndex, pos: int = -1) -> None:
475
461
  """Move multiple items to a new location."""
@@ -497,7 +483,6 @@ class ProjectModel(QAbstractItemModel):
497
483
  node._updateRelationships(child) # noqa: SLF001
498
484
  child.item.notifyToRefresh()
499
485
  node.item.notifyToRefresh()
500
- return
501
486
 
502
487
  ##
503
488
  # Other Methods
@@ -506,7 +491,6 @@ class ProjectModel(QAbstractItemModel):
506
491
  def clear(self) -> None:
507
492
  """Clear the project model."""
508
493
  self._root.children.clear()
509
- return
510
494
 
511
495
  def allExpanded(self) -> list[QModelIndex]:
512
496
  """Return a list of all expanded items."""
@@ -20,7 +20,7 @@ General Public License for more details.
20
20
 
21
21
  You should have received a copy of the GNU General Public License
22
22
  along with this program. If not, see <https://www.gnu.org/licenses/>.
23
- """
23
+ """ # noqa
24
24
  from __future__ import annotations
25
25
 
26
26
  import logging
@@ -54,21 +54,20 @@ T_NodeData = str | QIcon | QPixmap | Qt.AlignmentFlag | None
54
54
 
55
55
 
56
56
  class NovelModel(QAbstractTableModel):
57
+ """Core: Novel Model CLass."""
57
58
 
58
59
  __slots__ = ("_columns", "_extraKey", "_extraLabel", "_more", "_rows")
59
60
 
60
61
  def __init__(self) -> None:
61
62
  super().__init__()
62
63
  self._rows: list[dict[int, T_NodeData]] = []
63
- self._more = SHARED.theme.getIcon("more_arrow")
64
+ self._more = SHARED.theme.getIcon("more_arrow", "tool")
64
65
  self._columns = 3
65
66
  self._extraKey = ""
66
67
  self._extraLabel = ""
67
- return
68
68
 
69
69
  def __del__(self) -> None: # pragma: no cover
70
70
  logger.debug("Delete: NovelModel")
71
- return
72
71
 
73
72
  ##
74
73
  # Properties
@@ -102,7 +101,6 @@ class NovelModel(QAbstractTableModel):
102
101
  self._columns = 4
103
102
  self._extraKey = nwKeyWords.PLOT_KEY
104
103
  self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
105
- return
106
104
 
107
105
  ##
108
106
  # Model Interface
@@ -147,7 +145,6 @@ class NovelModel(QAbstractTableModel):
147
145
  def clear(self) -> None:
148
146
  """Clear the model."""
149
147
  self._rows.clear()
150
- return
151
148
 
152
149
  def append(self, node: IndexNode) -> None:
153
150
  """Append a node to the model."""
@@ -155,7 +152,6 @@ class NovelModel(QAbstractTableModel):
155
152
  for key, head in node.items():
156
153
  if key != "T0000":
157
154
  self._rows.append(self._generateEntry(handle, key, head))
158
- return
159
155
 
160
156
  def refresh(self, node: IndexNode) -> bool:
161
157
  """Refresh an index node."""
@@ -21,7 +21,7 @@ General Public License for more details.
21
21
 
22
22
  You should have received a copy of the GNU General Public License
23
23
  along with this program. If not, see <https://www.gnu.org/licenses/>.
24
- """
24
+ """ # noqa
25
25
  from __future__ import annotations
26
26
 
27
27
  import json
@@ -56,7 +56,7 @@ VALID_MAP: dict[str, set[str]] = {
56
56
  "GuiWordList": {"winWidth", "winHeight"},
57
57
  "GuiNovelView": {"lastCol", "lastColSize"},
58
58
  "GuiBuildSettings": {
59
- "winWidth", "winHeight", "treeWidth", "filterWidth",
59
+ "winWidth", "winHeight", "treeWidth", "filterWidth", "autoPreview",
60
60
  },
61
61
  "GuiManuscript": {
62
62
  "winWidth", "winHeight", "optsWidth", "viewWidth", "listHeight",
@@ -80,7 +80,7 @@ VALID_MAP: dict[str, set[str]] = {
80
80
 
81
81
 
82
82
  class OptionState:
83
- """Core: GUI Options Storage
83
+ """Core: GUI Options Storage.
84
84
 
85
85
  A class for storing the state of the GUI. The data is stored per
86
86
  project. Settings that should be project-independent are stored in
@@ -90,7 +90,6 @@ class OptionState:
90
90
  def __init__(self, project: NWProject) -> None:
91
91
  self._project = project
92
92
  self._state = {}
93
- return
94
93
 
95
94
  ##
96
95
  # Load and Save Cache
@@ -20,7 +20,7 @@ General Public License for more details.
20
20
 
21
21
  You should have received a copy of the GNU General Public License
22
22
  along with this program. If not, see <https://www.gnu.org/licenses/>.
23
- """
23
+ """ # noqa
24
24
  from __future__ import annotations
25
25
 
26
26
  import json
@@ -57,6 +57,7 @@ logger = logging.getLogger(__name__)
57
57
 
58
58
 
59
59
  class NWProjectState(Enum):
60
+ """The state of the loaded project."""
60
61
 
61
62
  UNKNOWN = 0
62
63
  LOCKED = 1
@@ -65,6 +66,11 @@ class NWProjectState(Enum):
65
66
 
66
67
 
67
68
  class NWProject:
69
+ """Core: novelWriter Project Class.
70
+
71
+ This class is the parent class of the project, and holds instances
72
+ of project data, the project tree, and the project index.
73
+ """
68
74
 
69
75
  __slots__ = (
70
76
  "_changed", "_data", "_index", "_langData", "_options", "_session",
@@ -92,17 +98,13 @@ class NWProject:
92
98
 
93
99
  logger.debug("Ready: NWProject")
94
100
 
95
- return
96
-
97
101
  def __del__(self) -> None: # pragma: no cover
98
102
  logger.debug("Delete: NWProject")
99
- return
100
103
 
101
104
  def clear(self) -> None:
102
105
  """Clear the project."""
103
106
  self._tree.clear()
104
107
  self._index.clear()
105
- return
106
108
 
107
109
  ##
108
110
  # Properties
@@ -224,7 +226,7 @@ class NWProject:
224
226
 
225
227
  return True
226
228
 
227
- def copyFileContent(self, tHandle: str, sHandle: str) -> bool:
229
+ def copyFileContent(self, tHandle: str, sHandle: str, newTitle: str | None = None) -> bool:
228
230
  """Copy content to a new document after it is created. This
229
231
  will not run if the file exists and is not empty.
230
232
  """
@@ -239,6 +241,14 @@ class NWProject:
239
241
 
240
242
  logger.debug("Populating '%s' with text from '%s'", tHandle, sHandle)
241
243
  text = self._storage.getDocumentText(sHandle)
244
+ if (
245
+ newTitle and (lines := text.split("\n")) and lines
246
+ and lines[0].startswith(("# ", "## ", "### ", "#### ", "#! ", "##! ", "###! "))
247
+ ):
248
+ prefix, _, _ = lines[0].partition(" ")
249
+ lines[0] = f"{prefix} {newTitle}"
250
+ text = "\n".join(lines)
251
+
242
252
  self._storage.getDocument(tHandle).writeDocument(text)
243
253
  sItem.setLayout(tItem.itemLayout)
244
254
  self._index.reIndexHandle(tHandle)
@@ -255,7 +265,6 @@ class NWProject:
255
265
  if rHandle and (tHandle := SHARED.project.newFile(tag.title(), rHandle)):
256
266
  self.writeNewFile(tHandle, 1, False, f"@tag: {tag}\n\n")
257
267
  self._tree.refreshItems([tHandle])
258
- return
259
268
 
260
269
  ##
261
270
  # Project Methods
@@ -433,7 +442,6 @@ class NWProject:
433
442
  self._tree.writeToCFile()
434
443
  self._session.appendSession(idleTime)
435
444
  self._storage.closeSession()
436
- return
437
445
 
438
446
  def backupProject(self, doNotify: bool) -> bool:
439
447
  """Create a zip file of the entire project."""
@@ -483,15 +491,14 @@ class NWProject:
483
491
 
484
492
  def setDefaultStatusImport(self) -> None:
485
493
  """Set the default status and importance values."""
486
- self._data.itemStatus.add(None, self.tr("New"), (120, 120, 120), "STAR", 0)
487
- self._data.itemStatus.add(None, self.tr("Note"), (205, 171, 143), "TRIANGLE", 0)
488
- self._data.itemStatus.add(None, self.tr("Draft"), (143, 240, 164), "CIRCLE_T", 0)
489
- self._data.itemStatus.add(None, self.tr("Finished"), (249, 240, 107), "STAR", 0)
490
- self._data.itemImport.add(None, self.tr("New"), (120, 120, 120), "SQUARE", 0)
491
- self._data.itemImport.add(None, self.tr("Minor"), (220, 138, 221), "BLOCK_2", 0)
492
- self._data.itemImport.add(None, self.tr("Major"), (220, 138, 221), "BLOCK_3", 0)
493
- self._data.itemImport.add(None, self.tr("Main"), (220, 138, 221), "BLOCK_4", 0)
494
- return
494
+ self._data.itemStatus.add(None, self.tr("New"), "faded", "STAR", 0)
495
+ self._data.itemStatus.add(None, self.tr("Note"), "red", "TRIANGLE", 0)
496
+ self._data.itemStatus.add(None, self.tr("Draft"), "yellow", "CIRCLE_T", 0)
497
+ self._data.itemStatus.add(None, self.tr("Finished"), "green", "STAR", 0)
498
+ self._data.itemImport.add(None, self.tr("New"), "purple", "SQUARE", 0)
499
+ self._data.itemImport.add(None, self.tr("Minor"), "purple", "BLOCK_2", 0)
500
+ self._data.itemImport.add(None, self.tr("Major"), "purple", "BLOCK_3", 0)
501
+ self._data.itemImport.add(None, self.tr("Main"), "purple", "BLOCK_4", 0)
495
502
 
496
503
  def setProjectLang(self, language: str | None) -> None:
497
504
  """Set the project-specific language."""
@@ -500,7 +507,6 @@ class NWProject:
500
507
  self._data.setLanguage(language)
501
508
  self._loadProjectLocalisation()
502
509
  self.setProjectChanged(True)
503
- return
504
510
 
505
511
  def setProjectChanged(self, status: bool) -> bool:
506
512
  """Toggle the project changed flag, and propagate the
@@ -519,7 +525,6 @@ class NWProject:
519
525
  """Update the total word and character count values."""
520
526
  wNovel, wNotes, cNovel, cNotes = self._tree.sumCounts()
521
527
  self._data.setCurrCounts(wNovel=wNovel, wNotes=wNotes, cNovel=cNovel, cNotes=cNotes)
522
- return
523
528
 
524
529
  def countStatus(self) -> None:
525
530
  """Count how many times the various status flags are used in the
@@ -533,7 +538,6 @@ class NWProject:
533
538
  self._data.itemStatus.increment(nwItem.itemStatus)
534
539
  else:
535
540
  self._data.itemImport.increment(nwItem.itemImport)
536
- return
537
541
 
538
542
  def updateStatus(self, kind: T_StatusKind, update: T_UpdateEntry) -> None:
539
543
  """Update status or import entries."""
@@ -545,7 +549,13 @@ class NWProject:
545
549
  self._data.itemImport.update(update)
546
550
  SHARED.emitStatusLabelsChanged(self, kind)
547
551
  self._tree.refreshAllItems()
548
- return
552
+
553
+ def updateTheme(self) -> None:
554
+ """Update theme elements."""
555
+ logger.debug("Theme Update: NWProject")
556
+
557
+ self._data.itemStatus.refreshIcons()
558
+ self._data.itemImport.refreshIcons()
549
559
 
550
560
  def localLookup(self, word: str | int) -> str:
551
561
  """Look up a word or number in the translation map for the