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
@@ -96,21 +96,22 @@ class GuiProjectSettings(NPagedDialog):
96
96
 
97
97
  return
98
98
 
99
- def __del__(self): # pragma: no cover
99
+ def __del__(self) -> None: # pragma: no cover
100
100
  logger.debug("Delete: GuiProjectSettings")
101
101
  return
102
102
 
103
103
  ##
104
- # Slots
104
+ # Private Slots
105
105
  ##
106
106
 
107
- def _doSave(self):
108
- """Save settings and close dialog.
109
- """
110
- project = SHARED.project
107
+ @pyqtSlot()
108
+ def _doSave(self) -> None:
109
+ """Save settings and close dialog."""
110
+ project = SHARED.project
111
111
  projName = self.tabMain.editName.text()
112
112
  bookTitle = self.tabMain.editTitle.text()
113
113
  bookAuthor = self.tabMain.editAuthor.text()
114
+ projLang = self.tabMain.projLang.currentData()
114
115
  spellLang = self.tabMain.spellLang.currentData()
115
116
  doBackup = not self.tabMain.doBackup.isChecked()
116
117
 
@@ -119,6 +120,7 @@ class GuiProjectSettings(NPagedDialog):
119
120
  project.data.setAuthor(bookAuthor)
120
121
  project.data.setDoBackup(doBackup)
121
122
  project.data.setSpellLang(spellLang)
123
+ project.setProjectLang(projLang)
122
124
 
123
125
  if self.tabStatus.colChanged:
124
126
  newList, delList = self.tabStatus.getNewList()
@@ -140,9 +142,9 @@ class GuiProjectSettings(NPagedDialog):
140
142
 
141
143
  return
142
144
 
143
- def _doClose(self):
144
- """Save settings and close the dialog.
145
- """
145
+ @pyqtSlot()
146
+ def _doClose(self) -> None:
147
+ """Save settings and close the dialog."""
146
148
  self._saveGuiSettings()
147
149
  self.reject()
148
150
  return
@@ -151,9 +153,8 @@ class GuiProjectSettings(NPagedDialog):
151
153
  # Internal Functions
152
154
  ##
153
155
 
154
- def _focusTab(self, tab):
155
- """Change which is the focused tab.
156
- """
156
+ def _focusTab(self, tab: int) -> None:
157
+ """Change which is the focused tab."""
157
158
  if tab == self.TAB_MAIN:
158
159
  self.setCurrentWidget(self.tabMain)
159
160
  elif tab == self.TAB_STATUS:
@@ -164,9 +165,8 @@ class GuiProjectSettings(NPagedDialog):
164
165
  self.setCurrentWidget(self.tabReplace)
165
166
  return
166
167
 
167
- def _saveGuiSettings(self):
168
- """Save GUI settings.
169
- """
168
+ def _saveGuiSettings(self) -> None:
169
+ """Save GUI settings."""
170
170
  winWidth = CONFIG.rpxInt(self.width())
171
171
  winHeight = CONFIG.rpxInt(self.height())
172
172
  replaceColW = CONFIG.rpxInt(self.tabReplace.listBox.columnWidth(0))
@@ -187,8 +187,8 @@ class GuiProjectSettings(NPagedDialog):
187
187
 
188
188
  class GuiProjectEditMain(QWidget):
189
189
 
190
- def __init__(self, projGui):
191
- super().__init__(parent=projGui)
190
+ def __init__(self, parent: QWidget) -> None:
191
+ super().__init__(parent=parent)
192
192
 
193
193
  # The Form
194
194
  self.mainForm = NConfigLayout()
@@ -230,24 +230,41 @@ class GuiProjectEditMain(QWidget):
230
230
  self.tr("Change whenever you want!")
231
231
  )
232
232
 
233
+ self.projLang = QComboBox(self)
234
+ self.projLang.setMaximumWidth(xW)
235
+ for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
236
+ self.projLang.addItem(language, tag)
237
+ self.mainForm.addRow(
238
+ self.tr("Project language"),
239
+ self.projLang,
240
+ self.tr("Used when building the manuscript.")
241
+ )
242
+
243
+ langIdx = 0
244
+ if pData.language is not None:
245
+ langIdx = self.projLang.findData(pData.language)
246
+ if langIdx == -1:
247
+ langIdx = self.projLang.findData("en_GB")
248
+ if langIdx != -1:
249
+ self.projLang.setCurrentIndex(langIdx)
250
+
233
251
  self.spellLang = QComboBox(self)
234
252
  self.spellLang.setMaximumWidth(xW)
235
253
  self.spellLang.addItem(self.tr("Default"), "None")
236
254
  if CONFIG.hasEnchant:
237
255
  for tag, language in SHARED.spelling.listDictionaries():
238
256
  self.spellLang.addItem(language, tag)
239
-
240
257
  self.mainForm.addRow(
241
258
  self.tr("Spell check language"),
242
259
  self.spellLang,
243
260
  self.tr("Overrides main preferences.")
244
261
  )
245
262
 
246
- spellIdx = 0
263
+ langIdx = 0
247
264
  if pData.spellLang is not None:
248
- spellIdx = self.spellLang.findData(pData.spellLang)
249
- if spellIdx != -1:
250
- self.spellLang.setCurrentIndex(spellIdx)
265
+ langIdx = self.spellLang.findData(pData.spellLang)
266
+ if langIdx != -1:
267
+ self.spellLang.setCurrentIndex(langIdx)
251
268
 
252
269
  self.doBackup = NSwitch(self)
253
270
  self.doBackup.setChecked(not pData.doBackup)
@@ -271,8 +288,8 @@ class GuiProjectEditStatus(QWidget):
271
288
  COL_ROLE = Qt.ItemDataRole.UserRole + 1
272
289
  NUM_ROLE = Qt.ItemDataRole.UserRole + 2
273
290
 
274
- def __init__(self, projGui, isStatus):
275
- super().__init__(parent=projGui)
291
+ def __init__(self, parent: QWidget, isStatus: bool) -> None:
292
+ super().__init__(parent=parent)
276
293
 
277
294
  if isStatus:
278
295
  self.theStatus = SHARED.project.data.itemStatus
@@ -372,9 +389,8 @@ class GuiProjectEditStatus(QWidget):
372
389
 
373
390
  return
374
391
 
375
- def getNewList(self):
376
- """Return list of entries.
377
- """
392
+ def getNewList(self) -> tuple[list, list]:
393
+ """Return list of entries."""
378
394
  if self.colChanged:
379
395
  newList = []
380
396
  for n in range(self.listBox.topLevelItemCount()):
@@ -394,9 +410,8 @@ class GuiProjectEditStatus(QWidget):
394
410
  ##
395
411
 
396
412
  @pyqtSlot()
397
- def _selectColour(self):
398
- """Open a dialog to select the status icon colour.
399
- """
413
+ def _selectColour(self) -> None:
414
+ """Open a dialog to select the status icon colour."""
400
415
  if self.selColour is not None:
401
416
  newCol = QColorDialog.getColor(
402
417
  self.selColour, self, self.tr("Select Colour")
@@ -410,17 +425,15 @@ class GuiProjectEditStatus(QWidget):
410
425
  return
411
426
 
412
427
  @pyqtSlot()
413
- def _newItem(self):
414
- """Create a new status item.
415
- """
428
+ def _newItem(self) -> None:
429
+ """Create a new status item."""
416
430
  self._addItem(None, self.tr("New Item"), (100, 100, 100), 0)
417
431
  self.colChanged = True
418
432
  return
419
433
 
420
434
  @pyqtSlot()
421
- def _delItem(self):
422
- """Delete a status item.
423
- """
435
+ def _delItem(self) -> None:
436
+ """Delete a status item."""
424
437
  selItem = self._getSelectedItem()
425
438
  if isinstance(selItem, QTreeWidgetItem):
426
439
  iRow = self.listBox.indexOfTopLevelItem(selItem)
@@ -433,9 +446,8 @@ class GuiProjectEditStatus(QWidget):
433
446
  return
434
447
 
435
448
  @pyqtSlot()
436
- def _saveItem(self):
437
- """Save changes made to a status item.
438
- """
449
+ def _saveItem(self) -> None:
450
+ """Save changes made to a status item."""
439
451
  selItem = self._getSelectedItem()
440
452
  if isinstance(selItem, QTreeWidgetItem):
441
453
  selItem.setText(self.COL_LABEL, simplified(self.editName.text()))
@@ -444,11 +456,10 @@ class GuiProjectEditStatus(QWidget):
444
456
  self.selColour.red(), self.selColour.green(), self.selColour.blue()
445
457
  ))
446
458
  self.colChanged = True
447
-
448
459
  return
449
460
 
450
461
  @pyqtSlot()
451
- def _selectedItem(self):
462
+ def _selectedItem(self) -> None:
452
463
  """Extract the info of a selected item and populate the settings
453
464
  boxes and button. If no item is selected, clear the form.
454
465
  """
@@ -456,7 +467,6 @@ class GuiProjectEditStatus(QWidget):
456
467
  if isinstance(selItem, QTreeWidgetItem):
457
468
  cols = selItem.data(self.COL_LABEL, self.COL_ROLE)
458
469
  name = selItem.text(self.COL_LABEL)
459
-
460
470
  pixmap = QPixmap(self.iPx, self.iPx)
461
471
  pixmap.fill(QColor(*cols))
462
472
  self.selColour = QColor(*cols)
@@ -464,31 +474,27 @@ class GuiProjectEditStatus(QWidget):
464
474
  self.colButton.setIcon(QIcon(pixmap))
465
475
  self.editName.selectAll()
466
476
  self.editName.setFocus()
467
-
468
477
  self.editName.setEnabled(True)
469
478
  self.colButton.setEnabled(True)
470
479
  self.saveButton.setEnabled(True)
471
-
472
480
  else:
473
481
  pixmap = QPixmap(self.iPx, self.iPx)
474
482
  pixmap.fill(QColor(100, 100, 100))
475
483
  self.selColour = QColor(100, 100, 100)
476
484
  self.editName.setText("")
477
485
  self.colButton.setIcon(QIcon(pixmap))
478
-
479
486
  self.editName.setEnabled(False)
480
487
  self.colButton.setEnabled(False)
481
488
  self.saveButton.setEnabled(False)
482
-
483
489
  return
484
490
 
485
491
  ##
486
492
  # Internal Functions
487
493
  ##
488
494
 
489
- def _addItem(self, key, name, cols, count):
490
- """Add a status item to the list.
491
- """
495
+ def _addItem(self, key: str | None, name: str,
496
+ cols: tuple[int, int, int], count: int) -> None:
497
+ """Add a status item to the list."""
492
498
  pixmap = QPixmap(self.iPx, self.iPx)
493
499
  pixmap.fill(QColor(*cols))
494
500
 
@@ -504,9 +510,8 @@ class GuiProjectEditStatus(QWidget):
504
510
 
505
511
  return
506
512
 
507
- def _moveItem(self, step):
508
- """Move and item up or down step.
509
- """
513
+ def _moveItem(self, step: int) -> None:
514
+ """Move and item up or down step."""
510
515
  selItem = self._getSelectedItem()
511
516
  if selItem is None:
512
517
  return
@@ -527,17 +532,15 @@ class GuiProjectEditStatus(QWidget):
527
532
 
528
533
  return
529
534
 
530
- def _getSelectedItem(self):
531
- """Get the currently selected item.
532
- """
535
+ def _getSelectedItem(self) -> QTreeWidgetItem | None:
536
+ """Get the currently selected item."""
533
537
  selItem = self.listBox.selectedItems()
534
538
  if len(selItem) > 0:
535
539
  return selItem[0]
536
540
  return None
537
541
 
538
- def _usageString(self, nUse):
539
- """Generate usage string.
540
- """
542
+ def _usageString(self, nUse: int) -> str:
543
+ """Generate usage string."""
541
544
  if nUse == 0:
542
545
  return self.tr("Not in use")
543
546
  elif nUse == 1:
@@ -553,8 +556,8 @@ class GuiProjectEditReplace(QWidget):
553
556
  COL_KEY = 0
554
557
  COL_REPL = 1
555
558
 
556
- def __init__(self, projGui):
557
- super().__init__(parent=projGui)
559
+ def __init__(self, parent: QWidget) -> None:
560
+ super().__init__(parent=parent)
558
561
 
559
562
  self.arChanged = False
560
563
 
@@ -635,25 +638,23 @@ class GuiProjectEditReplace(QWidget):
635
638
 
636
639
  return
637
640
 
638
- def getNewList(self):
639
- """Extract the list from the widget.
640
- """
641
- newList = {}
641
+ def getNewList(self) -> dict:
642
+ """Extract the list from the widget."""
643
+ new = {}
642
644
  for n in range(self.listBox.topLevelItemCount()):
643
645
  tItem = self.listBox.topLevelItem(n)
644
646
  if tItem is not None:
645
647
  aKey = self._stripNotAllowed(tItem.text(0))
646
648
  aVal = tItem.text(1)
647
649
  if len(aKey) > 0:
648
- newList[aKey] = aVal
649
-
650
- return newList
650
+ new[aKey] = aVal
651
+ return new
651
652
 
652
653
  ##
653
654
  # Internal Functions
654
655
  ##
655
656
 
656
- def _selectedItem(self):
657
+ def _selectedItem(self) -> bool:
657
658
  """Extract the details from the selected item and populate the
658
659
  edit form.
659
660
  """
@@ -670,63 +671,53 @@ class GuiProjectEditReplace(QWidget):
670
671
  self.editKey.setFocus()
671
672
  return True
672
673
 
673
- def _saveEntry(self):
674
- """Save the form data into the list widget.
675
- """
674
+ def _saveEntry(self) -> None:
675
+ """Save the form data into the list widget."""
676
676
  selItem = self._getSelectedItem()
677
- if selItem is None:
678
- return False
679
-
680
- newKey = self.editKey.text()
681
- newVal = self.editValue.text()
682
- saveKey = self._stripNotAllowed(newKey)
683
-
684
- if len(saveKey) > 0 and len(newVal) > 0:
685
- selItem.setText(self.COL_KEY, "<%s>" % saveKey)
686
- selItem.setText(self.COL_REPL, newVal)
687
- self.editKey.clear()
688
- self.editValue.clear()
689
- self.editKey.setEnabled(False)
690
- self.editValue.setEnabled(False)
691
- self.listBox.clearSelection()
692
- self.arChanged = True
693
-
677
+ if selItem:
678
+ newKey = self.editKey.text()
679
+ newVal = self.editValue.text()
680
+ saveKey = self._stripNotAllowed(newKey)
681
+ if len(saveKey) > 0 and len(newVal) > 0:
682
+ selItem.setText(self.COL_KEY, "<%s>" % saveKey)
683
+ selItem.setText(self.COL_REPL, newVal)
684
+ self.editKey.clear()
685
+ self.editValue.clear()
686
+ self.editKey.setEnabled(False)
687
+ self.editValue.setEnabled(False)
688
+ self.listBox.clearSelection()
689
+ self.arChanged = True
694
690
  return
695
691
 
696
- def _addEntry(self):
697
- """Add a new list entry.
698
- """
692
+ def _addEntry(self) -> None:
693
+ """Add a new list entry."""
699
694
  saveKey = "<keyword%d>" % (self.listBox.topLevelItemCount() + 1)
700
695
  newVal = ""
701
696
  newItem = QTreeWidgetItem([saveKey, newVal])
702
697
  self.listBox.addTopLevelItem(newItem)
703
- return True
698
+ return
704
699
 
705
- def _delEntry(self):
706
- """Delete the selected entry.
707
- """
700
+ def _delEntry(self) -> None:
701
+ """Delete the selected entry."""
708
702
  selItem = self._getSelectedItem()
709
- if selItem is None:
710
- return False
711
- self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem))
712
- self.arChanged = True
713
- return True
703
+ if selItem:
704
+ self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem))
705
+ self.arChanged = True
706
+ return
714
707
 
715
- def _getSelectedItem(self):
716
- """Extract the currently selected item.
717
- """
708
+ def _getSelectedItem(self) -> QTreeWidgetItem | None:
709
+ """Extract the currently selected item."""
718
710
  selItem = self.listBox.selectedItems()
719
711
  if len(selItem) == 0:
720
712
  return None
721
713
  return selItem[0]
722
714
 
723
- def _stripNotAllowed(self, theKey):
724
- """Clean up the replace key string.
725
- """
726
- retKey = ""
727
- for c in theKey:
715
+ def _stripNotAllowed(self, key: str) -> str:
716
+ """Clean up the replace key string."""
717
+ result = ""
718
+ for c in key:
728
719
  if c.isalnum():
729
- retKey += c
730
- return retKey
720
+ result += c
721
+ return result
731
722
 
732
723
  # END Class GuiProjectEditReplace
@@ -29,10 +29,10 @@ import logging
29
29
  from datetime import datetime
30
30
  from urllib.request import Request, urlopen
31
31
 
32
- from PyQt5.QtGui import QCursor
33
- from PyQt5.QtCore import Qt
32
+ from PyQt5.QtGui import QCloseEvent, QCursor
33
+ from PyQt5.QtCore import Qt, pyqtSlot
34
34
  from PyQt5.QtWidgets import (
35
- qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QLabel
35
+ QWidget, qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QLabel
36
36
  )
37
37
 
38
38
  from novelwriter import CONFIG, SHARED, __version__, __date__
@@ -44,8 +44,8 @@ logger = logging.getLogger(__name__)
44
44
 
45
45
  class GuiUpdates(QDialog):
46
46
 
47
- def __init__(self, mainGui):
48
- super().__init__(parent=mainGui)
47
+ def __init__(self, parent: QWidget) -> None:
48
+ super().__init__(parent=parent)
49
49
 
50
50
  logger.debug("Create: GuiUpdates")
51
51
  self.setObjectName("GuiUpdates")
@@ -94,8 +94,8 @@ class GuiUpdates(QDialog):
94
94
  self.latestLabel.setFont(hFont)
95
95
 
96
96
  # Buttons
97
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
98
- self.buttonBox.accepted.connect(self._doClose)
97
+ self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
98
+ self.buttonBox.rejected.connect(self._doClose)
99
99
 
100
100
  # Assemble
101
101
  self.innerBox = QHBoxLayout()
@@ -114,17 +114,16 @@ class GuiUpdates(QDialog):
114
114
 
115
115
  return
116
116
 
117
- def __del__(self): # pragma: no cover
117
+ def __del__(self) -> None: # pragma: no cover
118
118
  logger.debug("Delete: GuiUpdates")
119
119
  return
120
120
 
121
- def checkLatest(self):
122
- """Check for latest release.
123
- """
121
+ def checkLatest(self) -> None:
122
+ """Check for latest release."""
124
123
  qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
125
124
 
126
125
  urlReq = Request("https://api.github.com/repos/vkbo/novelwriter/releases/latest")
127
- urlReq.add_header("User-Agent", "Mozilla/5.0 (compatible; novelWriter (Python))")
126
+ urlReq.add_header("User-Agent", nwConst.USER_AGENT)
128
127
  urlReq.add_header("Accept", "application/vnd.github.v3+json")
129
128
 
130
129
  rawData = {}
@@ -161,10 +160,22 @@ class GuiUpdates(QDialog):
161
160
  return
162
161
 
163
162
  ##
164
- # Internal Functions
163
+ # Events
165
164
  ##
166
165
 
167
- def _doClose(self):
166
+ def closeEvent(self, event: QCloseEvent) -> None:
167
+ """Capture the user closing the window."""
168
+ event.accept()
169
+ self.deleteLater()
170
+ return
171
+
172
+ ##
173
+ # Private Slots
174
+ ##
175
+
176
+ @pyqtSlot()
177
+ def _doClose(self) -> None:
178
+ """Close the dialog."""
168
179
  self.close()
169
180
  return
170
181
 
novelwriter/enum.py CHANGED
@@ -61,6 +61,24 @@ class nwItemLayout(Enum):
61
61
  # END Enum nwItemLayout
62
62
 
63
63
 
64
+ class nwComment(Enum):
65
+
66
+ PLAIN = 0
67
+ SYNOPSIS = 1
68
+ SHORT = 2
69
+
70
+ # END Enum nwComment
71
+
72
+
73
+ class nwTrinary(Enum):
74
+
75
+ NEGATIVE = -1
76
+ NEUTRAL = 0
77
+ POSITIVE = 1
78
+
79
+ # END Enum nwTrinary
80
+
81
+
64
82
  class nwDocMode(Enum):
65
83
 
66
84
  VIEW = 0
@@ -100,6 +118,12 @@ class nwDocAction(Enum):
100
118
  ALIGN_R = 26
101
119
  INDENT_L = 27
102
120
  INDENT_R = 28
121
+ SC_ITALIC = 29
122
+ SC_BOLD = 30
123
+ SC_STRIKE = 31
124
+ SC_ULINE = 32
125
+ SC_SUP = 33
126
+ SC_SUB = 34
103
127
 
104
128
  # END Enum nwDocAction
105
129
 
@@ -112,9 +136,10 @@ class nwDocInsert(Enum):
112
136
  QUOTE_LD = 3
113
137
  QUOTE_RD = 4
114
138
  SYNOPSIS = 5
115
- NEW_PAGE = 6
116
- VSPACE_S = 7
117
- VSPACE_M = 8
139
+ SHORT = 6
140
+ NEW_PAGE = 7
141
+ VSPACE_S = 8
142
+ VSPACE_M = 9
118
143
 
119
144
  # END Enum nwDocInsert
120
145
 
@@ -62,7 +62,7 @@ class NovelSelector(QComboBox):
62
62
  # Methods
63
63
  ##
64
64
 
65
- def setHandle(self, tHandle: str, blockSignal: bool = True) -> None:
65
+ def setHandle(self, tHandle: str | None, blockSignal: bool = True) -> None:
66
66
  """Set the currently selected handle."""
67
67
  self._blockSignal = blockSignal
68
68
  if tHandle is None: