novelWriter 2.7.5__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 +160 -225
  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 +23 -13
  169. {novelwriter-2.7.5.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.5.dist-info/RECORD +0 -163
  192. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/WHEEL +0 -0
  193. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/entry_points.txt +0 -0
  194. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/licenses/LICENSE.md +0 -0
  195. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/licenses/setup/LICENSE-Apache-2.0.txt +0 -0
  196. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/top_level.txt +0 -0
@@ -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
@@ -49,6 +49,10 @@ logger = logging.getLogger(__name__)
49
49
 
50
50
 
51
51
  class GuiDocViewerPanel(QWidget):
52
+ """GUI: Document Viewer Panel.
53
+
54
+ The panel of project meta data below the viewer.
55
+ """
52
56
 
53
57
  openDocumentRequest = pyqtSignal(str, Enum, str, bool)
54
58
  loadDocumentTagRequest = pyqtSignal(str, Enum)
@@ -96,15 +100,15 @@ class GuiDocViewerPanel(QWidget):
96
100
 
97
101
  logger.debug("Ready: GuiDocViewerPanel")
98
102
 
99
- return
100
-
101
103
  ##
102
104
  # Methods
103
105
  ##
104
106
 
105
107
  def updateTheme(self, updateTabs: bool = True) -> None:
106
108
  """Update theme elements."""
107
- self.optsButton.setThemeIcon("more_vertical")
109
+ logger.debug("Theme Update: GuiDocViewerPanel")
110
+
111
+ self.optsButton.setThemeIcon("more_vertical", "default")
108
112
  self.optsButton.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON))
109
113
  self.mainTabs.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_FLAT_TABS))
110
114
  if updateTabs:
@@ -113,7 +117,6 @@ class GuiDocViewerPanel(QWidget):
113
117
  for tab in self.kwTabs.values():
114
118
  tab.updateTheme()
115
119
  self._loadAllTags()
116
- return
117
120
 
118
121
  def openProjectTasks(self) -> None:
119
122
  """Run open project tasks."""
@@ -124,7 +127,6 @@ class GuiDocViewerPanel(QWidget):
124
127
  for key, value in colWidths.items():
125
128
  if key in self.kwTabs and isinstance(value, list):
126
129
  self.kwTabs[key].setColumnWidths(value)
127
- return
128
130
 
129
131
  def closeProjectTasks(self) -> None:
130
132
  """Run close project tasks."""
@@ -133,7 +135,6 @@ class GuiDocViewerPanel(QWidget):
133
135
  hideInactive = self.aInactive.isChecked()
134
136
  SHARED.project.options.setValue("GuiDocViewerPanel", "colWidths", colWidths)
135
137
  SHARED.project.options.setValue("GuiDocViewerPanel", "hideInactive", hideInactive)
136
- return
137
138
 
138
139
  ##
139
140
  # Public Slots
@@ -145,7 +146,6 @@ class GuiDocViewerPanel(QWidget):
145
146
  self.tabBackRefs.clearContent()
146
147
  for cTab in self.kwTabs.values():
147
148
  cTab.clearContent()
148
- return
149
149
 
150
150
  @pyqtSlot()
151
151
  def indexHasAppeared(self) -> None:
@@ -153,7 +153,6 @@ class GuiDocViewerPanel(QWidget):
153
153
  self._loadAllTags()
154
154
  self._updateTabVisibility()
155
155
  self.updateHandle(self._lastHandle)
156
- return
157
156
 
158
157
  @pyqtSlot(str, Enum)
159
158
  def onProjectItemChanged(self, tHandle: str, change: nwChange) -> None:
@@ -168,14 +167,12 @@ class GuiDocViewerPanel(QWidget):
168
167
  else:
169
168
  self.kwTabs[tClass].removeEntry(key)
170
169
  self._updateTabVisibility()
171
- return
172
170
 
173
171
  @pyqtSlot(str)
174
172
  def updateHandle(self, tHandle: str | None) -> None:
175
173
  """Update the document handle."""
176
174
  self._lastHandle = tHandle
177
175
  self.tabBackRefs.refreshContent(tHandle or None)
178
- return
179
176
 
180
177
  @pyqtSlot(list, list)
181
178
  def updateChangedTags(self, updated: list[str], deleted: list[str]) -> None:
@@ -191,14 +188,12 @@ class GuiDocViewerPanel(QWidget):
191
188
  else:
192
189
  logger.warning("Could not remove tag '%s' from view panel", key)
193
190
  self._updateTabVisibility()
194
- return
195
191
 
196
192
  @pyqtSlot(str)
197
193
  def updateStatusLabels(self, kind: str) -> None:
198
194
  """Update the importance labels."""
199
195
  if kind == "i":
200
196
  self._loadAllTags()
201
- return
202
197
 
203
198
  ##
204
199
  # Private Slots
@@ -212,7 +207,6 @@ class GuiDocViewerPanel(QWidget):
212
207
  cTab.clearContent()
213
208
  self._loadAllTags()
214
209
  self._updateTabVisibility()
215
- return
216
210
 
217
211
  ##
218
212
  # Internal Functions
@@ -222,7 +216,6 @@ class GuiDocViewerPanel(QWidget):
222
216
  """Hide class tabs with no content."""
223
217
  for tClass, cTab in self.kwTabs.items():
224
218
  self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
225
- return
226
219
 
227
220
  def _loadAllTags(self) -> None:
228
221
  """Load all tags into the tabs."""
@@ -230,7 +223,6 @@ class GuiDocViewerPanel(QWidget):
230
223
  for key, name, tClass, iItem, hItem in data:
231
224
  if tClass in self.kwTabs and iItem and hItem:
232
225
  self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
233
- return
234
226
 
235
227
 
236
228
  class _ViewPanelBackRefs(QTreeWidget):
@@ -271,30 +263,28 @@ class _ViewPanelBackRefs(QTreeWidget):
271
263
  header.setSectionsMovable(False)
272
264
 
273
265
  # Cache Icons Locally
274
- self._editIcon = SHARED.theme.getIcon("edit", "green")
275
- self._viewIcon = SHARED.theme.getIcon("view", "blue")
266
+ self._editIcon = SHARED.theme.getIcon("edit", "change")
267
+ self._viewIcon = SHARED.theme.getIcon("view", "action")
276
268
 
277
269
  # Signals
278
270
  self.clicked.connect(self._treeItemClicked)
279
271
  self.doubleClicked.connect(self._treeItemDoubleClicked)
280
272
 
281
- return
282
-
283
273
  def updateTheme(self) -> None:
284
274
  """Update theme elements."""
285
- self._editIcon = SHARED.theme.getIcon("edit", "green")
286
- self._viewIcon = SHARED.theme.getIcon("view", "blue")
275
+ logger.debug("Theme Update: _ViewPanelBackRefs")
276
+
277
+ self._editIcon = SHARED.theme.getIcon("edit", "change")
278
+ self._viewIcon = SHARED.theme.getIcon("view", "action")
287
279
  for i in range(self.topLevelItemCount()):
288
280
  if item := self.topLevelItem(i):
289
281
  item.setIcon(self.C_EDIT, self._editIcon)
290
282
  item.setIcon(self.C_VIEW, self._viewIcon)
291
- return
292
283
 
293
284
  def clearContent(self) -> None:
294
285
  """Clear the widget."""
295
286
  self.clear()
296
287
  self._treeMap = {}
297
- return
298
288
 
299
289
  def refreshContent(self, dHandle: str | None) -> None:
300
290
  """Update the content."""
@@ -303,7 +293,6 @@ class _ViewPanelBackRefs(QTreeWidget):
303
293
  refs = SHARED.project.index.getBackReferenceList(dHandle)
304
294
  for tHandle, (sTitle, hItem) in refs.items():
305
295
  self._setTreeItemValues(tHandle, sTitle, hItem)
306
- return
307
296
 
308
297
  def refreshDocument(self, tHandle: str) -> None:
309
298
  """Refresh document meta data."""
@@ -311,7 +300,6 @@ class _ViewPanelBackRefs(QTreeWidget):
311
300
  for sTitle, hItem in iItem.items():
312
301
  if f"{tHandle}:{sTitle}" in self._treeMap:
313
302
  self._setTreeItemValues(tHandle, sTitle, hItem)
314
- return
315
303
 
316
304
  ##
317
305
  # Private Slots
@@ -325,7 +313,6 @@ class _ViewPanelBackRefs(QTreeWidget):
325
313
  self._parent.openDocumentRequest.emit(tHandle, nwDocMode.EDIT, "", True)
326
314
  elif index.column() == self.C_VIEW:
327
315
  self._parent.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, "", True)
328
- return
329
316
 
330
317
  @pyqtSlot("QModelIndex")
331
318
  def _treeItemDoubleClicked(self, index: QModelIndex) -> None:
@@ -333,7 +320,6 @@ class _ViewPanelBackRefs(QTreeWidget):
333
320
  tHandle = index.siblingAtColumn(self.C_DATA).data(self.D_HANDLE)
334
321
  if index.column() not in (self.C_EDIT, self.C_VIEW):
335
322
  self._parent.openDocumentRequest.emit(tHandle, nwDocMode.VIEW, "", True)
336
- return
337
323
 
338
324
  ##
339
325
  # Internal Functions
@@ -362,8 +348,6 @@ class _ViewPanelBackRefs(QTreeWidget):
362
348
  self.addTopLevelItem(trItem)
363
349
  self._treeMap[tKey] = trItem
364
350
 
365
- return
366
-
367
351
 
368
352
  class _ViewPanelKeyWords(QTreeWidget):
369
353
 
@@ -418,14 +402,13 @@ class _ViewPanelKeyWords(QTreeWidget):
418
402
  self.clicked.connect(self._treeItemClicked)
419
403
  self.doubleClicked.connect(self._treeItemDoubleClicked)
420
404
 
421
- return
422
-
423
405
  def updateTheme(self) -> None:
424
406
  """Update theme elements."""
407
+ logger.debug("Theme Update: _ViewPanelKeyWords")
408
+
425
409
  self._classIcon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[self._class], "root")
426
- self._editIcon = SHARED.theme.getIcon("edit", "green")
427
- self._viewIcon = SHARED.theme.getIcon("view", "blue")
428
- return
410
+ self._editIcon = SHARED.theme.getIcon("edit", "change")
411
+ self._viewIcon = SHARED.theme.getIcon("view", "action")
429
412
 
430
413
  def countEntries(self) -> int:
431
414
  """Return the number of items in the list."""
@@ -435,7 +418,6 @@ class _ViewPanelKeyWords(QTreeWidget):
435
418
  """Clear the list."""
436
419
  self._treeMap = {}
437
420
  self.clear()
438
- return
439
421
 
440
422
  def addUpdateEntry(self, tag: str, name: str, iItem: IndexNode, hItem: IndexHeading) -> None:
441
423
  """Add a new entry, or update an existing one."""
@@ -470,8 +452,6 @@ class _ViewPanelKeyWords(QTreeWidget):
470
452
  self.addTopLevelItem(trItem)
471
453
  self._treeMap[tag] = trItem
472
454
 
473
- return
474
-
475
455
  def removeEntry(self, tag: str) -> bool:
476
456
  """Remove a tag from the list."""
477
457
  if tag in self._treeMap:
@@ -487,7 +467,6 @@ class _ViewPanelKeyWords(QTreeWidget):
487
467
  self.setColumnWidth(self.C_IMPORT, checkInt(widths[1], 100))
488
468
  self.setColumnWidth(self.C_DOC, checkInt(widths[2], 100))
489
469
  self.setColumnWidth(self.C_TITLE, checkInt(widths[3], 100))
490
- return
491
470
 
492
471
  def getColumnWidths(self) -> list[int]:
493
472
  """Get the widths of the user-adjustable columns."""
@@ -510,7 +489,6 @@ class _ViewPanelKeyWords(QTreeWidget):
510
489
  self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.EDIT)
511
490
  elif index.column() == self.C_VIEW:
512
491
  self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.VIEW)
513
- return
514
492
 
515
493
  @pyqtSlot("QModelIndex")
516
494
  def _treeItemDoubleClicked(self, index: QModelIndex) -> None:
@@ -518,4 +496,3 @@ class _ViewPanelKeyWords(QTreeWidget):
518
496
  tag = index.siblingAtColumn(self.C_DATA).data(self.D_TAG)
519
497
  if index.column() not in (self.C_EDIT, self.C_VIEW):
520
498
  self._parent.loadDocumentTagRequest.emit(tag, nwDocMode.VIEW)
521
- return
@@ -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
@@ -42,6 +42,11 @@ logger = logging.getLogger(__name__)
42
42
 
43
43
 
44
44
  class GuiTextDocument(QTextDocument):
45
+ """Custom: Modified QTextDocument.
46
+
47
+ A special text document format that incorporates a few additional
48
+ features including spell checking.
49
+ """
45
50
 
46
51
  def __init__(self, parent: QObject) -> None:
47
52
  super().__init__(parent=parent)
@@ -52,11 +57,8 @@ class GuiTextDocument(QTextDocument):
52
57
 
53
58
  logger.debug("Ready: GuiTextDocument")
54
59
 
55
- return
56
-
57
60
  def __del__(self) -> None: # pragma: no cover
58
61
  logger.debug("Delete: GuiTextDocument")
59
- return
60
62
 
61
63
  ##
62
64
  # Properties
@@ -96,8 +98,6 @@ class GuiTextDocument(QTextDocument):
96
98
  logger.debug("Loaded %d text blocks in %.3f ms", count, 1000*(tMid - tStart))
97
99
  logger.debug("Highlighted document in %.3f ms", 1000*(tEnd - tMid))
98
100
 
99
- return
100
-
101
101
  def metaDataAtPos(self, pos: int) -> tuple[str, str]:
102
102
  """Check if there is meta data available at a given position in
103
103
  the document, and if so, return it.
@@ -113,7 +113,7 @@ class GuiTextDocument(QTextDocument):
113
113
  return cData, cType
114
114
  return "", ""
115
115
 
116
- def spellErrorAtPos(self, pos: int) -> tuple[str, int, int, list[str]]:
116
+ def spellErrorAtPos(self, pos: int) -> tuple[str, int, list[str]]:
117
117
  """Check if there is a misspelled word at a given position in
118
118
  the document, and if so, return it.
119
119
  """
@@ -122,15 +122,11 @@ class GuiTextDocument(QTextDocument):
122
122
  block = cursor.block()
123
123
  data = block.userData()
124
124
  if block.isValid() and isinstance(data, TextBlockData):
125
- text = block.text()
126
- check = pos - block.position()
127
- if check >= 0:
128
- for cPos, cEnd in data.spellErrors:
129
- cLen = cEnd - cPos
130
- if cPos <= check <= cEnd:
131
- word = text[cPos:cEnd]
132
- return word, cPos, cLen, SHARED.spelling.suggestWords(word)
133
- return "", -1, -1, []
125
+ if (check := pos - block.position()) >= 0:
126
+ for start, end, word in data.spellErrors:
127
+ if start <= check <= end:
128
+ return word, start, SHARED.spelling.suggestWords(word)
129
+ return "", -1, []
134
130
 
135
131
  def iterBlockByType(self, cType: int, maxCount: int = 1000) -> Iterable[QTextBlock]:
136
132
  """Iterate over all text blocks of a given type."""
@@ -150,4 +146,3 @@ class GuiTextDocument(QTextDocument):
150
146
  def setSpellCheckState(self, state: bool) -> None:
151
147
  """Set the spell check state of the syntax highlighter."""
152
148
  self._syntax.setSpellCheck(state)
153
- return
@@ -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
@@ -43,6 +43,7 @@ logger = logging.getLogger(__name__)
43
43
 
44
44
 
45
45
  class GuiItemDetails(QWidget):
46
+ """GUI: Project Item Details Panel."""
46
47
 
47
48
  def __init__(self, parent: QWidget) -> None:
48
49
  super().__init__(parent=parent)
@@ -190,8 +191,6 @@ class GuiItemDetails(QWidget):
190
191
 
191
192
  logger.debug("Ready: GuiItemDetails")
192
193
 
193
- return
194
-
195
194
  ###
196
195
  # Class Methods
197
196
  ##
@@ -210,7 +209,6 @@ class GuiItemDetails(QWidget):
210
209
  self.cCountData.clear()
211
210
  self.wCountData.clear()
212
211
  self.pCountData.clear()
213
- return
214
212
 
215
213
  def refreshDetails(self) -> None:
216
214
  """Reload the content of the details panel."""
@@ -218,8 +216,8 @@ class GuiItemDetails(QWidget):
218
216
 
219
217
  def updateTheme(self) -> None:
220
218
  """Update theme elements."""
219
+ logger.debug("Theme Update: GuiItemDetails")
221
220
  self.updateViewBox(self._handle)
222
- return
223
221
 
224
222
  def updateViewBox(self, tHandle: str | None) -> None:
225
223
  """Populate the details box from a given handle."""
@@ -233,14 +231,8 @@ class GuiItemDetails(QWidget):
233
231
  # Label
234
232
  # =====
235
233
 
236
- if nwItem.isFileType():
237
- if nwItem.isActive:
238
- self.labelIcon.setPixmap(SHARED.theme.getPixmap("checked", (iPx, iPx), "green"))
239
- else:
240
- self.labelIcon.setPixmap(SHARED.theme.getPixmap("unchecked", (iPx, iPx), "red"))
241
- else:
242
- self.labelIcon.setPixmap(SHARED.theme.getPixmap("noncheckable", (iPx, iPx), "faded"))
243
-
234
+ _, icon = nwItem.getActiveStatus()
235
+ self.labelIcon.setPixmap(icon.pixmap(iPx, iPx))
244
236
  self.labelData.setText(elide(nwItem.itemName, 100))
245
237
 
246
238
  # Status
@@ -289,4 +281,3 @@ class GuiItemDetails(QWidget):
289
281
  self.updateViewBox(tHandle)
290
282
  elif change == nwChange.DELETE:
291
283
  self.updateViewBox(None)
292
- return
@@ -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
@@ -82,8 +82,6 @@ class GuiMainMenu(QMenuBar):
82
82
 
83
83
  logger.debug("Ready: GuiMainMenu")
84
84
 
85
- return
86
-
87
85
  ##
88
86
  # Public Slots
89
87
  ##
@@ -92,7 +90,18 @@ class GuiMainMenu(QMenuBar):
92
90
  def setSpellCheckState(self, state: bool) -> None:
93
91
  """Forward spell check check state to its action."""
94
92
  self.aSpellCheck.setChecked(state)
95
- return
93
+
94
+ @pyqtSlot()
95
+ def updateSpellCheckLanguages(self) -> None:
96
+ """Update the list of available spell check languages."""
97
+ self.mSelectLanguage.clear()
98
+ languages = SHARED.spelling.listDictionaries()
99
+ languages.insert(0, ("None", self.tr("Default")))
100
+ for tag, language in languages:
101
+ aSpell = QAction(self.mSelectLanguage)
102
+ aSpell.setText(language)
103
+ aSpell.triggered.connect(qtLambda(self._changeSpelling, tag))
104
+ self.mSelectLanguage.addAction(aSpell)
96
105
 
97
106
  ##
98
107
  # Private Slots
@@ -105,21 +114,18 @@ class GuiMainMenu(QMenuBar):
105
114
  decision, just pass a None to the function and let it decide.
106
115
  """
107
116
  self.mainGui.docEditor.toggleSpellCheck(None)
108
- return
109
117
 
110
118
  @pyqtSlot()
111
119
  def _openUserManualFile(self) -> None:
112
120
  """Open the documentation in PDF format."""
113
121
  if isinstance(CONFIG.pdfDocs, Path):
114
122
  openExternalPath(CONFIG.pdfDocs)
115
- return
116
123
 
117
124
  @pyqtSlot(str)
118
125
  def _changeSpelling(self, language: str) -> None:
119
126
  """Change the spell check language."""
120
127
  SHARED.project.data.setSpellLang(language)
121
128
  SHARED.updateSpellCheckLanguage()
122
- return
123
129
 
124
130
  ##
125
131
  # Internal Functions
@@ -188,8 +194,6 @@ class GuiMainMenu(QMenuBar):
188
194
  self.aExitNW.triggered.connect(qtLambda(self.mainGui.closeMain))
189
195
  self.mainGui.addAction(self.aExitNW)
190
196
 
191
- return
192
-
193
197
  def _buildDocumentMenu(self) -> None:
194
198
  """Assemble the Document menu."""
195
199
  # Document
@@ -236,8 +240,6 @@ class GuiMainMenu(QMenuBar):
236
240
  self.aImportFile = qtAddAction(self.docuMenu, self.tr("Import Text from File"))
237
241
  self.aImportFile.triggered.connect(qtLambda(self.mainGui.importDocument))
238
242
 
239
- return
240
-
241
243
  def _buildEditMenu(self) -> None:
242
244
  """Assemble the Edit menu."""
243
245
  # Edit
@@ -305,8 +307,6 @@ class GuiMainMenu(QMenuBar):
305
307
  )
306
308
  self.mainGui.addAction(self.aSelectPar)
307
309
 
308
- return
309
-
310
310
  def _buildViewMenu(self) -> None:
311
311
  """Assemble the View menu."""
312
312
  # View
@@ -318,6 +318,7 @@ class GuiMainMenu(QMenuBar):
318
318
  self.aFocusTree.triggered.connect(
319
319
  lambda: self.requestFocusChange.emit(nwFocus.TREE)
320
320
  )
321
+ self.mainGui.addAction(self.aFocusTree)
321
322
 
322
323
  # View > Document Editor
323
324
  self.aFocusDocument = qtAddAction(self.viewMenu, self.tr("Go to Document"))
@@ -332,6 +333,7 @@ class GuiMainMenu(QMenuBar):
332
333
  self.aFocusOutline.triggered.connect(
333
334
  lambda: self.requestFocusChange.emit(nwFocus.OUTLINE)
334
335
  )
336
+ self.mainGui.addAction(self.aFocusOutline)
335
337
 
336
338
  # View > Separator
337
339
  self.viewMenu.addSeparator()
@@ -365,8 +367,6 @@ class GuiMainMenu(QMenuBar):
365
367
  self.aFullScreen.triggered.connect(self.mainGui.toggleFullScreenMode)
366
368
  self.mainGui.addAction(self.aFullScreen)
367
369
 
368
- return
369
-
370
370
  def _buildInsertMenu(self) -> None:
371
371
  """Assemble the Insert menu."""
372
372
  # Insert
@@ -644,8 +644,6 @@ class GuiMainMenu(QMenuBar):
644
644
  lambda: self.requestDocInsert.emit(nwDocInsert.FOOTNOTE)
645
645
  )
646
646
 
647
- return
648
-
649
647
  def _buildFormatMenu(self) -> None:
650
648
  """Assemble the Format menu."""
651
649
  # Format
@@ -675,6 +673,14 @@ class GuiMainMenu(QMenuBar):
675
673
  )
676
674
  self.mainGui.addAction(self.aFmtStrike)
677
675
 
676
+ # Format > Highlight
677
+ self.aFmtMark = qtAddAction(self.fmtMenu, self.tr("Highlight"))
678
+ self.aFmtMark.setShortcut("Ctrl+M")
679
+ self.aFmtMark.triggered.connect(
680
+ lambda: self.requestDocAction.emit(nwDocAction.MD_MARK)
681
+ )
682
+ self.mainGui.addAction(self.aFmtStrike)
683
+
678
684
  # Edit > Separator
679
685
  self.fmtMenu.addSeparator()
680
686
 
@@ -891,8 +897,6 @@ class GuiMainMenu(QMenuBar):
891
897
  lambda: self.requestDocAction.emit(nwDocAction.RM_BREAKS)
892
898
  )
893
899
 
894
- return
895
-
896
900
  def _buildSearchMenu(self) -> None:
897
901
  """Assemble the Search menu."""
898
902
  # Search
@@ -938,8 +942,6 @@ class GuiMainMenu(QMenuBar):
938
942
  self.aFindProj.setShortcut("Ctrl+Shift+F")
939
943
  self.aFindProj.triggered.connect(qtLambda(self.requestViewChange.emit, nwView.SEARCH))
940
944
 
941
- return
942
-
943
945
  def _buildToolsMenu(self) -> None:
944
946
  """Assemble the Tools menu."""
945
947
  # Tools
@@ -954,13 +956,7 @@ class GuiMainMenu(QMenuBar):
954
956
  self.mainGui.addAction(self.aSpellCheck)
955
957
 
956
958
  self.mSelectLanguage = qtAddMenu(self.toolsMenu, self.tr("Spell Check Language"))
957
- languages = SHARED.spelling.listDictionaries()
958
- languages.insert(0, ("None", self.tr("Default")))
959
- for tag, language in languages:
960
- aSpell = QAction(self.mSelectLanguage)
961
- aSpell.setText(language)
962
- aSpell.triggered.connect(qtLambda(self._changeSpelling, tag))
963
- self.mSelectLanguage.addAction(aSpell)
959
+ self.updateSpellCheckLanguages()
964
960
 
965
961
  # Tools > Re-Run Spell Check
966
962
  self.aReRunSpell = qtAddAction(self.toolsMenu, self.tr("Re-Run Spell Check"))
@@ -1009,8 +1005,6 @@ class GuiMainMenu(QMenuBar):
1009
1005
  self.aPreferences.triggered.connect(self.mainGui.showPreferencesDialog)
1010
1006
  self.mainGui.addAction(self.aPreferences)
1011
1007
 
1012
- return
1013
-
1014
1008
  def _buildHelpMenu(self) -> None:
1015
1009
  """Assemble the Help menu."""
1016
1010
  # Help
@@ -1056,5 +1050,3 @@ class GuiMainMenu(QMenuBar):
1056
1050
  # Document > Main Website
1057
1051
  self.aWebsite = qtAddAction(self.helpMenu, self.tr("The novelWriter Website"))
1058
1052
  self.aWebsite.triggered.connect(qtLambda(SHARED.openWebsite, nwConst.URL_WEB))
1059
-
1060
- return