novelWriter 2.1.1__py3-none-any.whl → 2.2rc1__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 (109) hide show
  1. {novelWriter-2.1.1.dist-info → novelWriter-2.2rc1.dist-info}/METADATA +3 -3
  2. {novelWriter-2.1.1.dist-info → novelWriter-2.2rc1.dist-info}/RECORD +105 -76
  3. novelwriter/__init__.py +6 -24
  4. novelwriter/assets/i18n/project_de_DE.json +10 -0
  5. novelwriter/assets/i18n/project_en_GB.json +11 -0
  6. novelwriter/assets/i18n/project_en_US.json +10 -0
  7. novelwriter/assets/i18n/project_ja_JP.json +11 -1
  8. novelwriter/assets/i18n/project_nb_NO.json +10 -0
  9. novelwriter/assets/i18n/project_nn_NO.json +10 -0
  10. novelwriter/assets/icons/novelwriter.ico +0 -0
  11. novelwriter/assets/icons/novelwriter.svg +8 -183
  12. novelwriter/assets/icons/typicons_dark/icons.conf +17 -2
  13. novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +4 -0
  14. novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +4 -0
  15. novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +4 -0
  16. novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +4 -0
  17. novelwriter/assets/icons/typicons_dark/nw_panel.svg +4 -0
  18. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +4 -0
  19. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +4 -0
  20. novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +8 -0
  21. novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +8 -0
  22. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +4 -0
  23. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +5 -0
  24. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +5 -0
  25. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +5 -0
  26. novelwriter/assets/icons/typicons_dark/typ_eye.svg +4 -0
  27. novelwriter/assets/icons/typicons_dark/typ_th-dot-menu.svg +4 -0
  28. novelwriter/assets/icons/typicons_light/icons.conf +17 -2
  29. novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +4 -0
  30. novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +4 -0
  31. novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +4 -0
  32. novelwriter/assets/icons/typicons_light/nw_deco-note.svg +4 -0
  33. novelwriter/assets/icons/typicons_light/nw_panel.svg +4 -0
  34. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +4 -0
  35. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +4 -0
  36. novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +8 -0
  37. novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +8 -0
  38. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +4 -0
  39. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +5 -0
  40. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +5 -0
  41. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +5 -0
  42. novelwriter/assets/icons/typicons_light/typ_eye.svg +4 -0
  43. novelwriter/assets/icons/typicons_light/typ_th-dot-menu.svg +4 -0
  44. novelwriter/assets/icons/x-novelwriter-project.ico +0 -0
  45. novelwriter/assets/icons/x-novelwriter-project.svg +7 -206
  46. novelwriter/assets/manual.pdf +0 -0
  47. novelwriter/assets/sample.zip +0 -0
  48. novelwriter/assets/syntax/default_dark.conf +1 -0
  49. novelwriter/assets/syntax/default_light.conf +1 -0
  50. novelwriter/assets/syntax/grey_dark.conf +1 -0
  51. novelwriter/assets/syntax/grey_light.conf +1 -0
  52. novelwriter/assets/syntax/light_owl.conf +1 -0
  53. novelwriter/assets/syntax/night_owl.conf +1 -0
  54. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  55. novelwriter/assets/syntax/solarized_light.conf +1 -0
  56. novelwriter/assets/syntax/tomorrow.conf +1 -0
  57. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  58. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  59. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  60. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  61. novelwriter/assets/text/credits_en.htm +7 -0
  62. novelwriter/assets/text/release_notes.htm +7 -37
  63. novelwriter/common.py +22 -1
  64. novelwriter/config.py +27 -42
  65. novelwriter/constants.py +45 -7
  66. novelwriter/core/buildsettings.py +40 -24
  67. novelwriter/core/coretools.py +8 -1
  68. novelwriter/core/docbuild.py +2 -6
  69. novelwriter/core/index.py +264 -175
  70. novelwriter/core/options.py +8 -3
  71. novelwriter/core/project.py +2 -2
  72. novelwriter/core/projectdata.py +3 -3
  73. novelwriter/core/tohtml.py +60 -59
  74. novelwriter/core/tokenizer.py +110 -70
  75. novelwriter/core/tomd.py +51 -38
  76. novelwriter/core/toodt.py +184 -147
  77. novelwriter/dialogs/preferences.py +75 -106
  78. novelwriter/dialogs/projsettings.py +101 -110
  79. novelwriter/dialogs/updates.py +25 -14
  80. novelwriter/enum.py +28 -3
  81. novelwriter/extensions/novelselector.py +1 -1
  82. novelwriter/gui/doceditor.py +1345 -1235
  83. novelwriter/gui/dochighlight.py +98 -62
  84. novelwriter/gui/docviewer.py +151 -340
  85. novelwriter/gui/docviewerpanel.py +457 -0
  86. novelwriter/gui/editordocument.py +126 -0
  87. novelwriter/gui/mainmenu.py +350 -300
  88. novelwriter/gui/noveltree.py +101 -125
  89. novelwriter/gui/outline.py +154 -171
  90. novelwriter/gui/projtree.py +480 -380
  91. novelwriter/gui/sidebar.py +106 -75
  92. novelwriter/gui/statusbar.py +1 -1
  93. novelwriter/gui/theme.py +114 -75
  94. novelwriter/guimain.py +353 -254
  95. novelwriter/shared.py +36 -3
  96. novelwriter/tools/dictionaries.py +268 -0
  97. novelwriter/tools/manusbuild.py +17 -6
  98. novelwriter/tools/manuscript.py +11 -3
  99. novelwriter/tools/manussettings.py +0 -14
  100. novelwriter/tools/projwizard.py +16 -2
  101. novelwriter/tools/writingstats.py +1 -1
  102. novelwriter/assets/icons/typicons_dark/typ_at.svg +0 -4
  103. novelwriter/assets/icons/typicons_dark/typ_th-menu.svg +0 -4
  104. novelwriter/assets/icons/typicons_light/typ_at.svg +0 -4
  105. novelwriter/assets/icons/typicons_light/typ_th-menu.svg +0 -4
  106. {novelWriter-2.1.1.dist-info → novelWriter-2.2rc1.dist-info}/LICENSE.md +0 -0
  107. {novelWriter-2.1.1.dist-info → novelWriter-2.2rc1.dist-info}/WHEEL +0 -0
  108. {novelWriter-2.1.1.dist-info → novelWriter-2.2rc1.dist-info}/entry_points.txt +0 -0
  109. {novelWriter-2.1.1.dist-info → novelWriter-2.2rc1.dist-info}/top_level.txt +0 -0
@@ -29,9 +29,10 @@ import logging
29
29
 
30
30
  from enum import Enum
31
31
  from time import time
32
+ from typing import TYPE_CHECKING
32
33
 
33
- from PyQt5.QtGui import QFont, QPalette
34
- from PyQt5.QtCore import Qt, QSize, pyqtSlot, pyqtSignal
34
+ from PyQt5.QtGui import QFocusEvent, QFont, QMouseEvent, QPalette, QResizeEvent
35
+ from PyQt5.QtCore import QModelIndex, QPoint, Qt, QSize, pyqtSlot, pyqtSignal
35
36
  from PyQt5.QtWidgets import (
36
37
  QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView,
37
38
  QInputDialog, QMenu, QSizePolicy, QToolButton, QToolTip, QTreeWidget,
@@ -42,8 +43,12 @@ from novelwriter import CONFIG, SHARED
42
43
  from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
43
44
  from novelwriter.common import minmax
44
45
  from novelwriter.constants import nwHeaders, nwKeyWords, nwLabels, trConst
46
+ from novelwriter.core.index import IndexHeading
45
47
  from novelwriter.extensions.novelselector import NovelSelector
46
48
 
49
+ if TYPE_CHECKING: # pragma: no cover
50
+ from novelwriter.guimain import GuiMain
51
+
47
52
  logger = logging.getLogger(__name__)
48
53
 
49
54
 
@@ -63,7 +68,7 @@ class GuiNovelView(QWidget):
63
68
  selectedItemChanged = pyqtSignal(str)
64
69
  openDocumentRequest = pyqtSignal(str, Enum, str, bool)
65
70
 
66
- def __init__(self, mainGui):
71
+ def __init__(self, mainGui: GuiMain) -> None:
67
72
  super().__init__(parent=mainGui)
68
73
 
69
74
  self.mainGui = mainGui
@@ -92,33 +97,29 @@ class GuiNovelView(QWidget):
92
97
  # Methods
93
98
  ##
94
99
 
95
- def updateTheme(self):
96
- """Update theme elements.
97
- """
100
+ def updateTheme(self) -> None:
101
+ """Update theme elements."""
98
102
  self.novelBar.updateTheme()
99
103
  self.novelTree.updateTheme()
100
104
  self.refreshTree()
101
105
  return
102
106
 
103
- def initSettings(self):
104
- """Initialise GUI elements that depend on specific settings.
105
- """
107
+ def initSettings(self) -> None:
108
+ """Initialise GUI elements that depend on specific settings."""
106
109
  self.novelTree.initSettings()
107
110
  return
108
111
 
109
- def clearNovelView(self):
110
- """Clear project-related GUI content.
111
- """
112
+ def clearNovelView(self) -> None:
113
+ """Clear project-related GUI content."""
112
114
  self.novelTree.clearContent()
113
115
  self.novelBar.clearContent()
114
116
  self.novelBar.setEnabled(False)
115
117
  return
116
118
 
117
- def openProjectTasks(self):
118
- """Run open project tasks.
119
- """
119
+ def openProjectTasks(self) -> None:
120
+ """Run open project tasks."""
120
121
  lastNovel = SHARED.project.data.getLastHandle("novelTree")
121
- if lastNovel not in SHARED.project.tree:
122
+ if lastNovel and lastNovel not in SHARED.project.tree:
122
123
  lastNovel = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
123
124
 
124
125
  logger.debug("Setting novel tree to root item '%s'", lastNovel)
@@ -140,7 +141,7 @@ class GuiNovelView(QWidget):
140
141
 
141
142
  return
142
143
 
143
- def closeProjectTasks(self):
144
+ def closeProjectTasks(self) -> None:
144
145
  """Run closing project tasks."""
145
146
  lastColType = self.novelTree.lastColType
146
147
  lastColSize = self.novelTree.lastColSize
@@ -150,15 +151,13 @@ class GuiNovelView(QWidget):
150
151
  self.clearNovelView()
151
152
  return
152
153
 
153
- def setTreeFocus(self):
154
- """Set the focus to the tree widget.
155
- """
154
+ def setTreeFocus(self) -> None:
155
+ """Set the focus to the tree widget."""
156
156
  self.novelTree.setFocus()
157
157
  return
158
158
 
159
- def treeHasFocus(self):
160
- """Check if the novel tree has focus.
161
- """
159
+ def treeHasFocus(self) -> bool:
160
+ """Check if the novel tree has focus."""
162
161
  return self.novelTree.hasFocus()
163
162
 
164
163
  ##
@@ -166,21 +165,19 @@ class GuiNovelView(QWidget):
166
165
  ##
167
166
 
168
167
  @pyqtSlot()
169
- def refreshTree(self):
170
- """Refresh the current tree.
171
- """
168
+ def refreshTree(self) -> None:
169
+ """Refresh the current tree."""
172
170
  self.novelTree.refreshTree(rootHandle=SHARED.project.data.getLastHandle("novelTree"))
173
171
  return
174
172
 
175
173
  @pyqtSlot(str)
176
- def updateRootItem(self, tHandle):
177
- """If any root item changes, rebuild the novel root menu.
178
- """
174
+ def updateRootItem(self, tHandle: str) -> None:
175
+ """If any root item changes, rebuild the novel root menu."""
179
176
  self.novelBar.buildNovelRootMenu()
180
177
  return
181
178
 
182
179
  @pyqtSlot(str)
183
- def updateNovelItemMeta(self, tHandle):
180
+ def updateNovelItemMeta(self, tHandle: str) -> None:
184
181
  """The meta data of a novel item has changed, and the tree item
185
182
  needs to be refreshed.
186
183
  """
@@ -192,7 +189,7 @@ class GuiNovelView(QWidget):
192
189
 
193
190
  class GuiNovelToolBar(QWidget):
194
191
 
195
- def __init__(self, novelView):
192
+ def __init__(self, novelView: GuiNovelView) -> None:
196
193
  super().__init__(parent=novelView)
197
194
 
198
195
  logger.debug("Create: GuiNovelToolBar")
@@ -269,9 +266,8 @@ class GuiNovelToolBar(QWidget):
269
266
  # Methods
270
267
  ##
271
268
 
272
- def updateTheme(self):
273
- """Update theme elements.
274
- """
269
+ def updateTheme(self) -> None:
270
+ """Update theme elements."""
275
271
  # Icons
276
272
  self.tbNovel.setIcon(SHARED.theme.getIcon("cls_novel"))
277
273
  self.tbRefresh.setIcon(SHARED.theme.getIcon("refresh"))
@@ -287,10 +283,11 @@ class GuiNovelToolBar(QWidget):
287
283
  "QToolButton {{padding: {0}px; border: none; background: transparent;}} "
288
284
  "QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
289
285
  ).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
286
+ buttonStyleMenu = f"{buttonStyle} QToolButton::menu-indicator {{image: none;}}"
290
287
 
291
288
  self.tbNovel.setStyleSheet(buttonStyle)
292
289
  self.tbRefresh.setStyleSheet(buttonStyle)
293
- self.tbMore.setStyleSheet(buttonStyle)
290
+ self.tbMore.setStyleSheet(buttonStyleMenu)
294
291
 
295
292
  self.novelValue.setStyleSheet(
296
293
  "QComboBox {border-style: none; padding-left: 0;} "
@@ -301,30 +298,26 @@ class GuiNovelToolBar(QWidget):
301
298
 
302
299
  return
303
300
 
304
- def clearContent(self):
305
- """Run clearing project tasks.
306
- """
301
+ def clearContent(self) -> None:
302
+ """Run clearing project tasks."""
307
303
  self.novelValue.clear()
308
304
  self.novelValue.setToolTip("")
309
305
  return
310
306
 
311
- def buildNovelRootMenu(self):
312
- """Build the novel root menu.
313
- """
307
+ def buildNovelRootMenu(self) -> None:
308
+ """Build the novel root menu."""
314
309
  self.novelValue.updateList(prefix=self.novelPrefix)
315
310
  self.tbNovel.setVisible(self.novelValue.count() > 1)
316
311
  return
317
312
 
318
- def setCurrentRoot(self, rootHandle):
319
- """Set the current active root handle.
320
- """
313
+ def setCurrentRoot(self, rootHandle: str | None) -> None:
314
+ """Set the current active root handle."""
321
315
  self.novelValue.setHandle(rootHandle)
322
316
  self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
323
317
  return
324
318
 
325
- def setLastColType(self, colType, doRefresh=True):
326
- """Set the last column type.
327
- """
319
+ def setLastColType(self, colType: NovelTreeColumn, doRefresh: bool = True) -> None:
320
+ """Set the last column type."""
328
321
  self.aLastCol[colType].setChecked(True)
329
322
  self.novelView.novelTree.setLastColType(colType, doRefresh=doRefresh)
330
323
  return
@@ -334,24 +327,21 @@ class GuiNovelToolBar(QWidget):
334
327
  ##
335
328
 
336
329
  @pyqtSlot()
337
- def _openNovelSelector(self):
338
- """Trigger the dropdown list of the novel selector.
339
- """
330
+ def _openNovelSelector(self) -> None:
331
+ """Trigger the dropdown list of the novel selector."""
340
332
  self.novelValue.showPopup()
341
333
  return
342
334
 
343
335
  @pyqtSlot()
344
- def _refreshNovelTree(self):
345
- """Rebuild the current tree.
346
- """
336
+ def _refreshNovelTree(self) -> None:
337
+ """Rebuild the current tree."""
347
338
  rootHandle = SHARED.project.data.getLastHandle("novelTree")
348
339
  self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
349
340
  return
350
341
 
351
342
  @pyqtSlot()
352
- def _selectLastColumnSize(self):
353
- """Set the maximum width for the last column.
354
- """
343
+ def _selectLastColumnSize(self) -> None:
344
+ """Set the maximum width for the last column."""
355
345
  oldSize = self.novelView.novelTree.lastColSize
356
346
  newSize, isOk = QInputDialog.getInt(
357
347
  self, self.tr("Column Size"), self.tr("Maximum column size in %"), oldSize, 15, 75, 5
@@ -365,9 +355,8 @@ class GuiNovelToolBar(QWidget):
365
355
  # Internal Functions
366
356
  ##
367
357
 
368
- def _addLastColAction(self, colType, actionLabel):
369
- """Add a column selection entry to the last column menu.
370
- """
358
+ def _addLastColAction(self, colType, actionLabel) -> None:
359
+ """Add a column selection entry to the last column menu."""
371
360
  aLast = self.mLastCol.addAction(actionLabel)
372
361
  aLast.setCheckable(True)
373
362
  aLast.setActionGroup(self.gLastCol)
@@ -391,7 +380,7 @@ class GuiNovelTree(QTreeWidget):
391
380
  D_KEY = Qt.ItemDataRole.UserRole + 2
392
381
  D_EXTRA = Qt.ItemDataRole.UserRole + 3
393
382
 
394
- def __init__(self, novelView):
383
+ def __init__(self, novelView: GuiNovelView) -> None:
395
384
  super().__init__(parent=novelView)
396
385
 
397
386
  logger.debug("Create: GuiNovelTree")
@@ -461,9 +450,8 @@ class GuiNovelTree(QTreeWidget):
461
450
 
462
451
  return
463
452
 
464
- def initSettings(self):
465
- """Set or update tree widget settings.
466
- """
453
+ def initSettings(self) -> None:
454
+ """Set or update tree widget settings."""
467
455
  # Scroll bars
468
456
  if CONFIG.hideVScroll:
469
457
  self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
@@ -477,11 +465,10 @@ class GuiNovelTree(QTreeWidget):
477
465
 
478
466
  return
479
467
 
480
- def updateTheme(self):
481
- """Update theme elements.
482
- """
468
+ def updateTheme(self) -> None:
469
+ """Update theme elements."""
483
470
  iPx = SHARED.theme.baseIconSize
484
- self._pMore = SHARED.theme.loadDecoration("deco_doc_more", pxH=iPx)
471
+ self._pMore = SHARED.theme.loadDecoration("deco_doc_more", h=iPx)
485
472
  return
486
473
 
487
474
  ##
@@ -489,28 +476,26 @@ class GuiNovelTree(QTreeWidget):
489
476
  ##
490
477
 
491
478
  @property
492
- def lastColType(self):
479
+ def lastColType(self) -> NovelTreeColumn:
493
480
  return self._lastCol
494
481
 
495
482
  @property
496
- def lastColSize(self):
483
+ def lastColSize(self) -> int:
497
484
  return int(self._lastColSize * 100)
498
485
 
499
486
  ##
500
487
  # Class Methods
501
488
  ##
502
489
 
503
- def clearContent(self):
504
- """Clear the GUI content and the related maps.
505
- """
490
+ def clearContent(self) -> None:
491
+ """Clear the GUI content and the related maps."""
506
492
  self.clear()
507
493
  self._treeMap = {}
508
494
  self._lastBuild = 0
509
495
  return
510
496
 
511
- def refreshTree(self, rootHandle=None, overRide=False):
512
- """Refresh the tree if it has been changed.
513
- """
497
+ def refreshTree(self, rootHandle: str | None = None, overRide: bool = False) -> None:
498
+ """Refresh the tree if it has been changed."""
514
499
  logger.debug("Requesting refresh of the novel tree")
515
500
  if rootHandle is None:
516
501
  rootHandle = SHARED.project.tree.findRoot(nwItemClass.NOVEL)
@@ -534,9 +519,8 @@ class GuiNovelTree(QTreeWidget):
534
519
 
535
520
  return
536
521
 
537
- def refreshHandle(self, tHandle):
538
- """Refresh the data for a given handle.
539
- """
522
+ def refreshHandle(self, tHandle: str) -> None:
523
+ """Refresh the data for a given handle."""
540
524
  idxData = SHARED.project.index.getItemData(tHandle)
541
525
  if idxData is None:
542
526
  return
@@ -554,7 +538,7 @@ class GuiNovelTree(QTreeWidget):
554
538
 
555
539
  return
556
540
 
557
- def getSelectedHandle(self):
541
+ def getSelectedHandle(self) -> tuple[str | None, str | None]:
558
542
  """Get the currently selected or active handle. If multiple
559
543
  items are selected, return the first.
560
544
  """
@@ -566,9 +550,8 @@ class GuiNovelTree(QTreeWidget):
566
550
  return tHandle, sTitle
567
551
  return None, None
568
552
 
569
- def setLastColType(self, colType, doRefresh=True):
570
- """Change the content type of the last column and rebuild.
571
- """
553
+ def setLastColType(self, colType: NovelTreeColumn, doRefresh: bool = True) -> None:
554
+ """Change the content type of the last column and rebuild."""
572
555
  if self._lastCol != colType:
573
556
  logger.debug("Changing last column to %s", colType.name)
574
557
  self._lastCol = colType
@@ -578,15 +561,13 @@ class GuiNovelTree(QTreeWidget):
578
561
  self.refreshTree(rootHandle=lastNovel, overRide=True)
579
562
  return
580
563
 
581
- def setLastColSize(self, colSize):
582
- """Set the column size in integer values between 15 and 75.
583
- """
564
+ def setLastColSize(self, colSize: int) -> None:
565
+ """Set the column size in integer values between 15 and 75."""
584
566
  self._lastColSize = minmax(colSize, 15, 75)/100.0
585
567
  return
586
568
 
587
- def setActiveHandle(self, tHandle):
588
- """Highlight the rows associated with a given handle.
589
- """
569
+ def setActiveHandle(self, tHandle: str | None) -> None:
570
+ """Highlight the rows associated with a given handle."""
590
571
  tStart = time()
591
572
 
592
573
  self._actHandle = tHandle
@@ -612,20 +593,20 @@ class GuiNovelTree(QTreeWidget):
612
593
  # Events
613
594
  ##
614
595
 
615
- def mousePressEvent(self, theEvent):
596
+ def mousePressEvent(self, event: QMouseEvent) -> None:
616
597
  """Overload mousePressEvent to clear selection if clicking the
617
598
  mouse in a blank area of the tree view, and to load a document
618
599
  for viewing if the user middle-clicked.
619
600
  """
620
- super().mousePressEvent(theEvent)
601
+ super().mousePressEvent(event)
621
602
 
622
- if theEvent.button() == Qt.LeftButton:
623
- selItem = self.indexAt(theEvent.pos())
603
+ if event.button() == Qt.LeftButton:
604
+ selItem = self.indexAt(event.pos())
624
605
  if not selItem.isValid():
625
606
  self.clearSelection()
626
607
 
627
- elif theEvent.button() == Qt.MiddleButton:
628
- selItem = self.itemAt(theEvent.pos())
608
+ elif event.button() == Qt.MiddleButton:
609
+ selItem = self.itemAt(event.pos())
629
610
  if not isinstance(selItem, QTreeWidgetItem):
630
611
  return
631
612
 
@@ -637,16 +618,14 @@ class GuiNovelTree(QTreeWidget):
637
618
 
638
619
  return
639
620
 
640
- def focusOutEvent(self, theEvent):
641
- """Clear the selection when the tree no longer has focus.
642
- """
643
- super().focusOutEvent(theEvent)
621
+ def focusOutEvent(self, event: QFocusEvent) -> None:
622
+ """Clear the selection when the tree no longer has focus."""
623
+ super().focusOutEvent(event)
644
624
  self.clearSelection()
645
625
  return
646
626
 
647
- def resizeEvent(self, event):
648
- """Elide labels in the extra column.
649
- """
627
+ def resizeEvent(self, event: QResizeEvent) -> None:
628
+ """Elide labels in the extra column."""
650
629
  super().resizeEvent(event)
651
630
  newW = event.size().width()
652
631
  oldW = event.oldSize().width()
@@ -665,18 +644,17 @@ class GuiNovelTree(QTreeWidget):
665
644
  ##
666
645
 
667
646
  @pyqtSlot("QModelIndex")
668
- def _treeItemClicked(self, mIndex):
669
- """The user clicked on an item in the tree.
670
- """
671
- if mIndex.column() == self.C_MORE:
672
- tHandle = mIndex.siblingAtColumn(self.C_DATA).data(self.D_HANDLE)
673
- sTitle = mIndex.siblingAtColumn(self.C_DATA).data(self.D_TITLE)
674
- tipPos = self.mapToGlobal(self.visualRect(mIndex).topRight())
647
+ def _treeItemClicked(self, index: QModelIndex) -> None:
648
+ """The user clicked on an item in the tree."""
649
+ if index.column() == self.C_MORE:
650
+ tHandle = index.siblingAtColumn(self.C_DATA).data(self.D_HANDLE)
651
+ sTitle = index.siblingAtColumn(self.C_DATA).data(self.D_TITLE)
652
+ tipPos = self.mapToGlobal(self.visualRect(index).topRight())
675
653
  self._popMetaBox(tipPos, tHandle, sTitle)
676
654
  return
677
655
 
678
656
  @pyqtSlot()
679
- def _treeSelectionChange(self):
657
+ def _treeSelectionChange(self) -> None:
680
658
  """Extract the handle and line number of the currently selected
681
659
  title, and send it to the tree meta panel.
682
660
  """
@@ -686,7 +664,7 @@ class GuiNovelTree(QTreeWidget):
686
664
  return
687
665
 
688
666
  @pyqtSlot("QTreeWidgetItem*", int)
689
- def _treeDoubleClick(self, tItem, colNo):
667
+ def _treeDoubleClick(self, item: QTreeWidgetItem, column: int) -> None:
690
668
  """Extract the handle and line number of the title double-
691
669
  clicked, and send it to the main gui class for opening in the
692
670
  document editor.
@@ -699,9 +677,8 @@ class GuiNovelTree(QTreeWidget):
699
677
  # Internal Functions
700
678
  ##
701
679
 
702
- def _populateTree(self, rootHandle):
703
- """Build the tree based on the project index.
704
- """
680
+ def _populateTree(self, rootHandle: str | None) -> None:
681
+ """Build the tree based on the project index."""
705
682
  self.clearContent()
706
683
  tStart = time()
707
684
  logger.debug("Building novel tree for root item '%s'", rootHandle)
@@ -728,9 +705,9 @@ class GuiNovelTree(QTreeWidget):
728
705
 
729
706
  return
730
707
 
731
- def _updateTreeItemValues(self, trItem, idxItem, tHandle, sTitle):
732
- """Set the tree item values from the index entry.
733
- """
708
+ def _updateTreeItemValues(self, trItem: QTreeWidgetItem, idxItem: IndexHeading,
709
+ tHandle: str, sTitle: str) -> None:
710
+ """Set the tree item values from the index entry."""
734
711
  iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0)
735
712
  hDec = SHARED.theme.getHeaderDecoration(iLevel)
736
713
 
@@ -750,9 +727,8 @@ class GuiNovelTree(QTreeWidget):
750
727
 
751
728
  return
752
729
 
753
- def _getLastColumnText(self, tHandle, sTitle):
754
- """Generate the text for the last column based on user settings.
755
- """
730
+ def _getLastColumnText(self, tHandle: str, sTitle: str) -> tuple[str, str]:
731
+ """Generate text for the last column based on user settings."""
756
732
  if self._lastCol == NovelTreeColumn.HIDDEN:
757
733
  return "", ""
758
734
 
@@ -777,14 +753,15 @@ class GuiNovelTree(QTreeWidget):
777
753
 
778
754
  return "", ""
779
755
 
780
- def _popMetaBox(self, qPos, tHandle, sTitle):
781
- """Show the novel meta data box.
782
- """
756
+ def _popMetaBox(self, qPos: QPoint, tHandle: str, sTitle: str) -> None:
757
+ """Show the novel meta data box."""
783
758
  logger.debug("Generating meta data tooltip for '%s:%s'", tHandle, sTitle)
784
759
 
785
760
  pIndex = SHARED.project.index
786
761
  novIdx = pIndex.getItemHeader(tHandle, sTitle)
787
762
  refTags = pIndex.getReferences(tHandle, sTitle)
763
+ if not novIdx:
764
+ return
788
765
 
789
766
  synopText = novIdx.synopsis
790
767
  if synopText:
@@ -814,9 +791,8 @@ class GuiNovelTree(QTreeWidget):
814
791
  return
815
792
 
816
793
  @staticmethod
817
- def _appendMetaTag(refs, key, lines):
818
- """Generate a reference list for a given reference key.
819
- """
794
+ def _appendMetaTag(refs: dict, key: str, lines: list[str]) -> list[str]:
795
+ """Generate a reference list for a given reference key."""
820
796
  tags = ", ".join(refs.get(key, []))
821
797
  if tags:
822
798
  lines.append(f"<b>{trConst(nwLabels.KEY_NAME[key])}</b>: {tags}")