novelWriter 2.2b1__py3-none-any.whl → 2.2.1__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 (134) hide show
  1. {novelWriter-2.2b1.dist-info → novelWriter-2.2.1.dist-info}/METADATA +3 -3
  2. {novelWriter-2.2b1.dist-info → novelWriter-2.2.1.dist-info}/RECORD +128 -114
  3. {novelWriter-2.2b1.dist-info → novelWriter-2.2.1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +10 -5
  5. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  6. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  7. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  8. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  9. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  10. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  11. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  12. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  13. novelwriter/assets/i18n/project_de_DE.json +1 -0
  14. novelwriter/assets/i18n/project_en_GB.json +1 -0
  15. novelwriter/assets/i18n/project_en_US.json +1 -0
  16. novelwriter/assets/i18n/project_es_419.json +11 -0
  17. novelwriter/assets/i18n/project_fr_FR.json +11 -0
  18. novelwriter/assets/i18n/project_it_IT.json +11 -0
  19. novelwriter/assets/i18n/project_ja_JP.json +2 -1
  20. novelwriter/assets/i18n/project_nb_NO.json +1 -0
  21. novelwriter/assets/i18n/project_zh_CN.json +11 -0
  22. novelwriter/assets/icons/novelwriter.ico +0 -0
  23. novelwriter/assets/icons/typicons_dark/icons.conf +11 -3
  24. novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +4 -0
  25. novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +4 -0
  26. novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +4 -0
  27. novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +4 -0
  28. novelwriter/assets/icons/typicons_dark/nw_panel.svg +4 -0
  29. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
  30. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
  31. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
  32. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
  33. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
  34. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
  35. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
  36. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
  37. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
  38. novelwriter/assets/icons/typicons_dark/typ_eye.svg +4 -0
  39. novelwriter/assets/icons/typicons_light/icons.conf +11 -3
  40. novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +4 -0
  41. novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +4 -0
  42. novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +4 -0
  43. novelwriter/assets/icons/typicons_light/nw_deco-note.svg +4 -0
  44. novelwriter/assets/icons/typicons_light/nw_panel.svg +4 -0
  45. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
  46. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
  47. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
  48. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
  49. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
  50. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
  51. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
  52. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
  53. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
  54. novelwriter/assets/icons/typicons_light/typ_eye.svg +4 -0
  55. novelwriter/assets/icons/x-novelwriter-project.ico +0 -0
  56. novelwriter/assets/manual.pdf +0 -0
  57. novelwriter/assets/sample.zip +0 -0
  58. novelwriter/assets/text/release_notes.htm +50 -7
  59. novelwriter/common.py +35 -27
  60. novelwriter/config.py +13 -28
  61. novelwriter/constants.py +21 -4
  62. novelwriter/core/buildsettings.py +2 -2
  63. novelwriter/core/coretools.py +8 -2
  64. novelwriter/core/docbuild.py +1 -1
  65. novelwriter/core/document.py +1 -1
  66. novelwriter/core/index.py +102 -36
  67. novelwriter/core/item.py +2 -2
  68. novelwriter/core/options.py +6 -3
  69. novelwriter/core/project.py +5 -5
  70. novelwriter/core/projectdata.py +3 -3
  71. novelwriter/core/projectxml.py +1 -1
  72. novelwriter/core/sessions.py +2 -2
  73. novelwriter/core/spellcheck.py +4 -3
  74. novelwriter/core/status.py +3 -3
  75. novelwriter/core/storage.py +1 -1
  76. novelwriter/core/tohtml.py +11 -5
  77. novelwriter/core/tokenizer.py +28 -21
  78. novelwriter/core/tomd.py +6 -2
  79. novelwriter/core/toodt.py +12 -5
  80. novelwriter/core/tree.py +2 -2
  81. novelwriter/dialogs/about.py +30 -31
  82. novelwriter/dialogs/docmerge.py +24 -15
  83. novelwriter/dialogs/docsplit.py +27 -16
  84. novelwriter/dialogs/editlabel.py +19 -7
  85. novelwriter/dialogs/preferences.py +116 -131
  86. novelwriter/dialogs/projdetails.py +29 -36
  87. novelwriter/dialogs/projload.py +32 -36
  88. novelwriter/dialogs/projsettings.py +20 -15
  89. novelwriter/dialogs/quotes.py +32 -25
  90. novelwriter/dialogs/updates.py +17 -16
  91. novelwriter/dialogs/wordlist.py +34 -21
  92. novelwriter/enum.py +19 -8
  93. novelwriter/error.py +1 -1
  94. novelwriter/extensions/circularprogress.py +1 -1
  95. novelwriter/extensions/configlayout.py +3 -15
  96. novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
  97. novelwriter/extensions/novelselector.py +1 -1
  98. novelwriter/extensions/pageddialog.py +1 -1
  99. novelwriter/extensions/pagedsidebar.py +2 -5
  100. novelwriter/extensions/simpleprogress.py +8 -9
  101. novelwriter/extensions/statusled.py +1 -1
  102. novelwriter/extensions/switch.py +4 -4
  103. novelwriter/extensions/switchbox.py +1 -6
  104. novelwriter/gui/doceditor.py +349 -236
  105. novelwriter/gui/dochighlight.py +10 -11
  106. novelwriter/gui/docviewer.py +158 -360
  107. novelwriter/gui/docviewerpanel.py +502 -0
  108. novelwriter/gui/editordocument.py +4 -4
  109. novelwriter/gui/itemdetails.py +2 -2
  110. novelwriter/gui/mainmenu.py +50 -36
  111. novelwriter/gui/noveltree.py +44 -53
  112. novelwriter/gui/outline.py +12 -7
  113. novelwriter/gui/projtree.py +465 -381
  114. novelwriter/gui/sidebar.py +9 -7
  115. novelwriter/gui/statusbar.py +48 -5
  116. novelwriter/gui/theme.py +26 -8
  117. novelwriter/guimain.py +212 -208
  118. novelwriter/shared.py +76 -30
  119. novelwriter/tools/dictionaries.py +268 -0
  120. novelwriter/tools/lipsum.py +34 -28
  121. novelwriter/tools/manusbuild.py +20 -10
  122. novelwriter/tools/manuscript.py +20 -27
  123. novelwriter/tools/manussettings.py +2 -4
  124. novelwriter/tools/projwizard.py +3 -3
  125. novelwriter/tools/writingstats.py +18 -5
  126. novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
  127. novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
  128. novelwriter/assets/icons/typicons_dark/typ_at.svg +0 -4
  129. novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
  130. novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
  131. novelwriter/assets/icons/typicons_light/typ_at.svg +0 -4
  132. {novelWriter-2.2b1.dist-info → novelWriter-2.2.1.dist-info}/LICENSE.md +0 -0
  133. {novelWriter-2.2b1.dist-info → novelWriter-2.2.1.dist-info}/entry_points.txt +0 -0
  134. {novelWriter-2.2b1.dist-info → novelWriter-2.2.1.dist-info}/top_level.txt +0 -0
@@ -10,9 +10,11 @@ Created: 2020-04-25 [0.4.5] GuiDocEditHeader
10
10
  Rewritten: 2020-06-15 [0.9] GuiDocEditSearch
11
11
  Created: 2020-06-27 [0.10] GuiDocEditFooter
12
12
  Rewritten: 2020-10-07 [1.0b3] BackgroundWordCounter
13
+ Created: 2023-11-06 [2.2b1] MetaCompleter
14
+ Created: 2023-11-07 [2.2b1] GuiDocToolBar
13
15
 
14
16
  This file is a part of novelWriter
15
- Copyright 2018–2023, Veronica Berglyd Olsen
17
+ Copyright 2018–2024, Veronica Berglyd Olsen
16
18
 
17
19
  This program is free software: you can redistribute it and/or modify
18
20
  it under the terms of the GNU General Public License as published by
@@ -46,8 +48,8 @@ from PyQt5.QtGui import (
46
48
  )
47
49
  from PyQt5.QtWidgets import (
48
50
  QAction, QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QMenu,
49
- QPlainTextEdit, QPushButton, QShortcut, QToolBar, QToolButton, QVBoxLayout, QWidget,
50
- qApp
51
+ QPlainTextEdit, QPushButton, QShortcut, QToolBar, QToolButton, QVBoxLayout,
52
+ QWidget, qApp
51
53
  )
52
54
 
53
55
  from novelwriter import CONFIG, SHARED
@@ -56,10 +58,11 @@ from novelwriter.common import minmax, transferCase
56
58
  from novelwriter.constants import nwKeyWords, nwLabels, nwShortcode, nwUnicode, trConst
57
59
  from novelwriter.core.item import NWItem
58
60
  from novelwriter.core.index import countWords
61
+ from novelwriter.tools.lipsum import GuiLipsum
59
62
  from novelwriter.core.document import NWDocument
60
63
  from novelwriter.gui.dochighlight import GuiDocHighlighter
61
64
  from novelwriter.gui.editordocument import GuiTextDocument
62
- from novelwriter.extensions.wheeleventfilter import WheelEventFilter
65
+ from novelwriter.extensions.eventfilters import WheelEventFilter
63
66
 
64
67
  if TYPE_CHECKING: # pragma: no cover
65
68
  from novelwriter.guimain import GuiMain
@@ -67,12 +70,22 @@ if TYPE_CHECKING: # pragma: no cover
67
70
  logger = logging.getLogger(__name__)
68
71
 
69
72
 
73
+ class _SelectAction(Enum):
74
+
75
+ NO_DECISION = 0
76
+ KEEP_SELECTION = 1
77
+ KEEP_POSITION = 2
78
+ MOVE_AFTER = 3
79
+
80
+ # END Class _SelectAction
81
+
82
+
70
83
  class GuiDocEditor(QPlainTextEdit):
71
84
  """Gui Widget: Main Document Editor"""
72
85
 
73
86
  MOVE_KEYS = (
74
- Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down,
75
- Qt.Key_PageUp, Qt.Key_PageDown
87
+ Qt.Key.Key_Left, Qt.Key.Key_Right, Qt.Key.Key_Up, Qt.Key.Key_Down,
88
+ Qt.Key.Key_PageUp, Qt.Key.Key_PageDown
76
89
  )
77
90
 
78
91
  # Custom Signals
@@ -85,6 +98,8 @@ class GuiDocEditor(QPlainTextEdit):
85
98
  spellCheckStateChanged = pyqtSignal(bool)
86
99
  closeDocumentRequest = pyqtSignal()
87
100
  toggleFocusModeRequest = pyqtSignal()
101
+ requestProjectItemSelected = pyqtSignal(str, bool)
102
+ requestProjectItemRenamed = pyqtSignal(str, str)
88
103
 
89
104
  def __init__(self, mainGui: GuiMain) -> None:
90
105
  super().__init__(parent=mainGui)
@@ -145,29 +160,28 @@ class GuiDocEditor(QPlainTextEdit):
145
160
  self.docToolBar.requestDocAction.connect(self.docAction)
146
161
 
147
162
  # Context Menu
148
- self.setContextMenuPolicy(Qt.CustomContextMenu)
163
+ self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
149
164
  self.customContextMenuRequested.connect(self._openContextMenu)
150
165
 
151
166
  # Editor Settings
152
167
  self.setMinimumWidth(CONFIG.pxInt(300))
153
168
  self.setAutoFillBackground(True)
154
- self.setFrameStyle(QFrame.NoFrame)
155
- self.setCenterOnScroll(True)
169
+ self.setFrameStyle(QFrame.Shape.NoFrame)
156
170
 
157
171
  # Custom Shortcuts
158
172
  self.keyContext = QShortcut(self)
159
173
  self.keyContext.setKey("Ctrl+.")
160
- self.keyContext.setContext(Qt.WidgetShortcut)
174
+ self.keyContext.setContext(Qt.ShortcutContext.WidgetShortcut)
161
175
  self.keyContext.activated.connect(self._openContextFromCursor)
162
176
 
163
177
  self.followTag1 = QShortcut(self)
164
- self.followTag1.setKey(Qt.Key_Return | Qt.ControlModifier)
165
- self.followTag1.setContext(Qt.WidgetShortcut)
178
+ self.followTag1.setKey(Qt.Key.Key_Return | Qt.KeyboardModifier.ControlModifier)
179
+ self.followTag1.setContext(Qt.ShortcutContext.WidgetShortcut)
166
180
  self.followTag1.activated.connect(self._processTag)
167
181
 
168
182
  self.followTag2 = QShortcut(self)
169
- self.followTag2.setKey(Qt.Key_Enter | Qt.ControlModifier)
170
- self.followTag2.setContext(Qt.WidgetShortcut)
183
+ self.followTag2.setKey(Qt.Key.Key_Enter | Qt.KeyboardModifier.ControlModifier)
184
+ self.followTag2.setContext(Qt.ShortcutContext.WidgetShortcut)
171
185
  self.followTag2.activated.connect(self._processTag)
172
186
 
173
187
  # Set Up Document Word Counter
@@ -263,14 +277,14 @@ class GuiDocEditor(QPlainTextEdit):
263
277
  def updateSyntaxColours(self) -> None:
264
278
  """Update the syntax highlighting theme."""
265
279
  mainPalette = self.palette()
266
- mainPalette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack))
267
- mainPalette.setColor(QPalette.Base, QColor(*SHARED.theme.colBack))
268
- mainPalette.setColor(QPalette.Text, QColor(*SHARED.theme.colText))
280
+ mainPalette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
281
+ mainPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
282
+ mainPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
269
283
  self.setPalette(mainPalette)
270
284
 
271
285
  docPalette = self.viewport().palette()
272
- docPalette.setColor(QPalette.Base, QColor(*SHARED.theme.colBack))
273
- docPalette.setColor(QPalette.Text, QColor(*SHARED.theme.colText))
286
+ docPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
287
+ docPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
274
288
  self.viewport().setPalette(docPalette)
275
289
 
276
290
  self.docHeader.matchColours()
@@ -322,24 +336,25 @@ class GuiDocEditor(QPlainTextEdit):
322
336
  options = QTextOption()
323
337
 
324
338
  if CONFIG.doJustify:
325
- options.setAlignment(Qt.AlignJustify)
339
+ options.setAlignment(Qt.AlignmentFlag.AlignJustify)
326
340
  if CONFIG.showTabsNSpaces:
327
- options.setFlags(options.flags() | QTextOption.ShowTabsAndSpaces)
341
+ options.setFlags(options.flags() | QTextOption.Flag.ShowTabsAndSpaces)
328
342
  if CONFIG.showLineEndings:
329
- options.setFlags(options.flags() | QTextOption.ShowLineAndParagraphSeparators)
343
+ options.setFlags(options.flags() | QTextOption.Flag.ShowLineAndParagraphSeparators)
330
344
 
331
345
  self._qDocument.setDefaultTextOption(options)
332
346
 
333
- # Scroll bars
347
+ # Scrolling
348
+ self.setCenterOnScroll(CONFIG.scrollPastEnd)
334
349
  if CONFIG.hideVScroll:
335
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
350
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
336
351
  else:
337
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
352
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
338
353
 
339
354
  if CONFIG.hideHScroll:
340
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
355
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
341
356
  else:
342
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
357
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
343
358
 
344
359
  # Refresh the tab stops
345
360
  self.setTabStopDistance(CONFIG.getTabWidth())
@@ -358,7 +373,7 @@ class GuiDocEditor(QPlainTextEdit):
358
373
 
359
374
  return
360
375
 
361
- def loadText(self, tHandle, tLine=None) -> bool:
376
+ def loadText(self, tHandle: str, tLine=None) -> bool:
362
377
  """Load text from a document into the editor. If we have an I/O
363
378
  error, we must handle this and clear the editor so that we don't
364
379
  risk overwriting the file if it exists. This can for instance
@@ -376,7 +391,7 @@ class GuiDocEditor(QPlainTextEdit):
376
391
  self.clearEditor()
377
392
  return False
378
393
 
379
- qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
394
+ qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
380
395
  self._docHandle = tHandle
381
396
 
382
397
  self._allowAutoReplace(False)
@@ -429,7 +444,7 @@ class GuiDocEditor(QPlainTextEdit):
429
444
  """Replace the text of the current document with the provided
430
445
  text. This also clears undo history.
431
446
  """
432
- qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
447
+ qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
433
448
  self.setPlainText(text)
434
449
  self.updateDocMargins()
435
450
  self.setDocumentChanged(True)
@@ -488,10 +503,7 @@ class GuiDocEditor(QPlainTextEdit):
488
503
  else:
489
504
  self.novelStructureChanged.emit()
490
505
 
491
- # ToDo: This should be a signal
492
506
  if oldHeader != newHeader:
493
- self.mainGui.projView.setTreeItemValues(tHandle)
494
- self.mainGui.itemDetails.updateViewBox(tHandle)
495
507
  self.docFooter.updateInfo()
496
508
 
497
509
  # Update the status bar
@@ -499,6 +511,33 @@ class GuiDocEditor(QPlainTextEdit):
499
511
 
500
512
  return True
501
513
 
514
+ def cursorIsVisible(self) -> bool:
515
+ """Check if the cursor is visible in the editor."""
516
+ return (
517
+ 0 < self.cursorRect().top()
518
+ and self.cursorRect().bottom() < self.viewport().height()
519
+ )
520
+
521
+ def ensureCursorVisibleNoCentre(self) -> None:
522
+ """Ensure cursor is visible, but don't force it to centre."""
523
+ cT = self.cursorRect().top()
524
+ cB = self.cursorRect().bottom()
525
+ vH = self.viewport().height()
526
+ if cT < 0:
527
+ count = 0
528
+ vBar = self.verticalScrollBar()
529
+ while self.cursorRect().top() < 0 and count < 100000:
530
+ vBar.setValue(vBar.value() - 1)
531
+ count += 1
532
+ elif cB > vH:
533
+ count = 0
534
+ vBar = self.verticalScrollBar()
535
+ while self.cursorRect().bottom() > vH and count < 100000:
536
+ vBar.setValue(vBar.value() + 1)
537
+ count += 1
538
+ qApp.processEvents()
539
+ return
540
+
502
541
  def updateDocMargins(self) -> None:
503
542
  """Automatically adjust the margins so the text is centred if
504
543
  we have a text width set or we're in Focus Mode. Otherwise, just
@@ -640,7 +679,7 @@ class GuiDocEditor(QPlainTextEdit):
640
679
  """
641
680
  logger.debug("Running spell checker")
642
681
  start = time()
643
- qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
682
+ qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
644
683
  self._qDocument.syntaxHighlighter.rehighlight()
645
684
  qApp.restoreOverrideCursor()
646
685
  logger.debug("Document highlighted in %.3f ms", 1000*(time() - start))
@@ -679,20 +718,20 @@ class GuiDocEditor(QPlainTextEdit):
679
718
  self.copy()
680
719
  elif action == nwDocAction.PASTE:
681
720
  self.paste()
682
- elif action == nwDocAction.EMPH:
721
+ elif action == nwDocAction.MD_ITALIC:
683
722
  self._toggleFormat(1, "_")
684
- elif action == nwDocAction.STRONG:
723
+ elif action == nwDocAction.MD_BOLD:
685
724
  self._toggleFormat(2, "*")
686
- elif action == nwDocAction.STRIKE:
725
+ elif action == nwDocAction.MD_STRIKE:
687
726
  self._toggleFormat(2, "~")
688
727
  elif action == nwDocAction.S_QUOTE:
689
728
  self._wrapSelection(self._typSQuoteO, self._typSQuoteC)
690
729
  elif action == nwDocAction.D_QUOTE:
691
730
  self._wrapSelection(self._typDQuoteO, self._typDQuoteC)
692
731
  elif action == nwDocAction.SEL_ALL:
693
- self._makeSelection(QTextCursor.Document)
732
+ self._makeSelection(QTextCursor.SelectionType.Document)
694
733
  elif action == nwDocAction.SEL_PARA:
695
- self._makeSelection(QTextCursor.BlockUnderCursor)
734
+ self._makeSelection(QTextCursor.SelectionType.BlockUnderCursor)
696
735
  elif action == nwDocAction.BLOCK_H1:
697
736
  self._formatBlock(nwDocAction.BLOCK_H1)
698
737
  elif action == nwDocAction.BLOCK_H2:
@@ -793,7 +832,11 @@ class GuiDocEditor(QPlainTextEdit):
793
832
  elif insert == nwDocInsert.QUOTE_RD:
794
833
  text = self._typDQuoteC
795
834
  elif insert == nwDocInsert.SYNOPSIS:
796
- text = "% Synopsis: "
835
+ text = "%Synopsis: "
836
+ newBlock = True
837
+ goAfter = True
838
+ elif insert == nwDocInsert.SHORT:
839
+ text = "%Short: "
797
840
  newBlock = True
798
841
  goAfter = True
799
842
  elif insert == nwDocInsert.NEW_PAGE:
@@ -808,18 +851,23 @@ class GuiDocEditor(QPlainTextEdit):
808
851
  text = "[vspace:2]"
809
852
  newBlock = True
810
853
  goAfter = False
854
+ elif insert == nwDocInsert.LIPSUM:
855
+ text = GuiLipsum.getLipsum(self)
856
+ newBlock = True
857
+ goAfter = False
811
858
  else:
812
859
  return False
813
860
  else:
814
861
  return False
815
862
 
816
- if newBlock:
817
- self.insertNewBlock(text, defaultAfter=goAfter)
818
- else:
819
- cursor = self.textCursor()
820
- cursor.beginEditBlock()
821
- cursor.insertText(text)
822
- cursor.endEditBlock()
863
+ if text:
864
+ if newBlock:
865
+ self.insertNewBlock(text, defaultAfter=goAfter)
866
+ else:
867
+ cursor = self.textCursor()
868
+ cursor.beginEditBlock()
869
+ cursor.insertText(text)
870
+ cursor.endEditBlock()
823
871
 
824
872
  return True
825
873
 
@@ -873,17 +921,17 @@ class GuiDocEditor(QPlainTextEdit):
873
921
  * We also handle automatic scrolling here.
874
922
  """
875
923
  self._lastActive = time()
876
- isReturn = event.key() == Qt.Key_Return
877
- isReturn |= event.key() == Qt.Key_Enter
924
+ isReturn = event.key() == Qt.Key.Key_Return
925
+ isReturn |= event.key() == Qt.Key.Key_Enter
878
926
  if isReturn and self.docSearch.anyFocus():
879
927
  return
880
- elif event == QKeySequence.Redo:
928
+ elif event == QKeySequence.StandardKey.Redo:
881
929
  self.docAction(nwDocAction.REDO)
882
930
  return
883
- elif event == QKeySequence.Undo:
931
+ elif event == QKeySequence.StandardKey.Undo:
884
932
  self.docAction(nwDocAction.UNDO)
885
933
  return
886
- elif event == QKeySequence.SelectAll:
934
+ elif event == QKeySequence.StandardKey.SelectAll:
887
935
  self.docAction(nwDocAction.SEL_ALL)
888
936
  return
889
937
 
@@ -892,7 +940,7 @@ class GuiDocEditor(QPlainTextEdit):
892
940
  super().keyPressEvent(event)
893
941
  nPos = self.cursorRect().topLeft().y()
894
942
  kMod = event.modifiers()
895
- okMod = kMod == Qt.NoModifier or kMod == Qt.ShiftModifier
943
+ okMod = kMod in (Qt.KeyboardModifier.NoModifier, Qt.KeyboardModifier.ShiftModifier)
896
944
  okKey = event.key() not in self.MOVE_KEYS
897
945
  if nPos != cPos and okMod and okKey:
898
946
  mPos = CONFIG.autoScrollPos*0.01 * self.viewport().height()
@@ -923,7 +971,7 @@ class GuiDocEditor(QPlainTextEdit):
923
971
  pressed, check if we're clicking on a tag, and trigger the
924
972
  follow tag function.
925
973
  """
926
- if qApp.keyboardModifiers() == Qt.ControlModifier:
974
+ if qApp.keyboardModifiers() == Qt.KeyboardModifier.ControlModifier:
927
975
  self._processTag(self.cursorForPosition(event.pos()))
928
976
  super().mouseReleaseEvent(event)
929
977
  self.docFooter.updateLineCount()
@@ -996,7 +1044,9 @@ class GuiDocEditor(QPlainTextEdit):
996
1044
  return
997
1045
 
998
1046
  text = block.text()
999
- if text.startswith("@"):
1047
+ if text.startswith("@") and added + removed == 1:
1048
+ # Only run on single keypresses, otherwise it will trigger
1049
+ # at unwanted times when other changes are made to the document
1000
1050
  cursor = self.textCursor()
1001
1051
  bPos = cursor.positionInBlock()
1002
1052
  if bPos > 0:
@@ -1004,8 +1054,10 @@ class GuiDocEditor(QPlainTextEdit):
1004
1054
  point = self.cursorRect().bottomRight()
1005
1055
  self._completer.move(self.viewport().mapToGlobal(point))
1006
1056
  self._completer.setVisible(show)
1057
+ else:
1058
+ self._completer.setVisible(False)
1007
1059
 
1008
- elif self._doReplace and added == 1:
1060
+ if self._doReplace and added == 1:
1009
1061
  self._docAutoReplace(text)
1010
1062
 
1011
1063
  return
@@ -1025,43 +1077,50 @@ class GuiDocEditor(QPlainTextEdit):
1025
1077
 
1026
1078
  @pyqtSlot("QPoint")
1027
1079
  def _openContextMenu(self, pos: QPoint) -> None:
1028
- """Triggered by right click to open the context menu. Also
1029
- triggered by the Ctrl+. shortcut.
1030
- """
1080
+ """Open the editor context menu at a given coordinate."""
1031
1081
  uCursor = self.textCursor()
1032
1082
  pCursor = self.cursorForPosition(pos)
1083
+ pBlock = pCursor.block()
1033
1084
 
1034
1085
  ctxMenu = QMenu(self)
1086
+ ctxMenu.setObjectName("ContextMenu")
1087
+ if pBlock.userState() == GuiDocHighlighter.BLOCK_TITLE:
1088
+ action = ctxMenu.addAction(self.tr("Set as Document Name"))
1089
+ action.triggered.connect(lambda: self._emitRenameItem(pBlock))
1035
1090
 
1036
1091
  # Follow
1037
1092
  status = self._processTag(cursor=pCursor, follow=False)
1038
1093
  if status == nwTrinary.POSITIVE:
1039
- aTag = ctxMenu.addAction(self.tr("Follow Tag"))
1040
- aTag.triggered.connect(lambda: self._processTag(cursor=pCursor, follow=True))
1094
+ action = ctxMenu.addAction(self.tr("Follow Tag"))
1095
+ action.triggered.connect(lambda: self._processTag(cursor=pCursor, follow=True))
1041
1096
  ctxMenu.addSeparator()
1042
1097
  elif status == nwTrinary.NEGATIVE:
1043
- aTag = ctxMenu.addAction(self.tr("Create Note for Tag"))
1044
- aTag.triggered.connect(lambda: self._processTag(cursor=pCursor, create=True))
1098
+ action = ctxMenu.addAction(self.tr("Create Note for Tag"))
1099
+ action.triggered.connect(lambda: self._processTag(cursor=pCursor, create=True))
1045
1100
  ctxMenu.addSeparator()
1046
1101
 
1047
1102
  # Cut, Copy and Paste
1048
1103
  if uCursor.hasSelection():
1049
- aCut = ctxMenu.addAction(self.tr("Cut"))
1050
- aCut.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
1051
- aCopy = ctxMenu.addAction(self.tr("Copy"))
1052
- aCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
1104
+ action = ctxMenu.addAction(self.tr("Cut"))
1105
+ action.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
1106
+ action = ctxMenu.addAction(self.tr("Copy"))
1107
+ action.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
1053
1108
 
1054
- aPaste = ctxMenu.addAction(self.tr("Paste"))
1055
- aPaste.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
1109
+ action = ctxMenu.addAction(self.tr("Paste"))
1110
+ action.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
1056
1111
  ctxMenu.addSeparator()
1057
1112
 
1058
1113
  # Selections
1059
- aSAll = ctxMenu.addAction(self.tr("Select All"))
1060
- aSAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
1061
- aSWrd = ctxMenu.addAction(self.tr("Select Word"))
1062
- aSWrd.triggered.connect(lambda: self._makePosSelection(QTextCursor.WordUnderCursor, pos))
1063
- aSPar = ctxMenu.addAction(self.tr("Select Paragraph"))
1064
- aSPar.triggered.connect(lambda: self._makePosSelection(QTextCursor.BlockUnderCursor, pos))
1114
+ action = ctxMenu.addAction(self.tr("Select All"))
1115
+ action.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
1116
+ action = ctxMenu.addAction(self.tr("Select Word"))
1117
+ action.triggered.connect(
1118
+ lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, pos)
1119
+ )
1120
+ action = ctxMenu.addAction(self.tr("Select Paragraph"))
1121
+ action.triggered.connect(lambda: self._makePosSelection(
1122
+ QTextCursor.SelectionType.BlockUnderCursor, pos)
1123
+ )
1065
1124
 
1066
1125
  # Spell Checking
1067
1126
  if SHARED.project.data.spellCheck:
@@ -1071,24 +1130,27 @@ class GuiDocEditor(QPlainTextEdit):
1071
1130
  block = pCursor.block()
1072
1131
  sCursor = self.textCursor()
1073
1132
  sCursor.setPosition(block.position() + cPos)
1074
- sCursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, cLen)
1133
+ sCursor.movePosition(
1134
+ QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor, cLen
1135
+ )
1075
1136
  if suggest:
1076
1137
  ctxMenu.addSeparator()
1077
1138
  ctxMenu.addAction(self.tr("Spelling Suggestion(s)"))
1078
1139
  for option in suggest[:15]:
1079
- aFix = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
1080
- aFix.triggered.connect(
1140
+ action = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
1141
+ action.triggered.connect(
1081
1142
  lambda _, option=option: self._correctWord(sCursor, option)
1082
1143
  )
1083
1144
  else:
1084
1145
  ctxMenu.addAction("%s %s" % (nwUnicode.U_ENDASH, self.tr("No Suggestions")))
1085
1146
 
1086
1147
  ctxMenu.addSeparator()
1087
- aAdd = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
1088
- aAdd.triggered.connect(lambda: self._addWord(word, block))
1148
+ action = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
1149
+ action.triggered.connect(lambda: self._addWord(word, block))
1089
1150
 
1090
1151
  # Execute the context menu
1091
1152
  ctxMenu.exec_(self.viewport().mapToGlobal(pos))
1153
+ ctxMenu.deleteLater()
1092
1154
 
1093
1155
  return
1094
1156
 
@@ -1276,8 +1338,8 @@ class GuiDocEditor(QPlainTextEdit):
1276
1338
  else:
1277
1339
  resIdx = 0 if doLoop else maxIdx
1278
1340
 
1279
- cursor.setPosition(resS[resIdx], QTextCursor.MoveAnchor)
1280
- cursor.setPosition(resE[resIdx], QTextCursor.KeepAnchor)
1341
+ cursor.setPosition(resS[resIdx], QTextCursor.MoveMode.MoveAnchor)
1342
+ cursor.setPosition(resE[resIdx], QTextCursor.MoveMode.KeepAnchor)
1281
1343
  self.setTextCursor(cursor)
1282
1344
 
1283
1345
  self.docFooter.updateLineCount()
@@ -1303,9 +1365,9 @@ class GuiDocEditor(QPlainTextEdit):
1303
1365
 
1304
1366
  findOpt = QTextDocument.FindFlag(0)
1305
1367
  if self.docSearch.isCaseSense:
1306
- findOpt |= QTextDocument.FindCaseSensitively
1368
+ findOpt |= QTextDocument.FindFlag.FindCaseSensitively
1307
1369
  if self.docSearch.isWholeWord:
1308
- findOpt |= QTextDocument.FindWholeWords
1370
+ findOpt |= QTextDocument.FindFlag.FindWholeWords
1309
1371
 
1310
1372
  searchFor = self.docSearch.getSearchObject()
1311
1373
  cursor.setPosition(0)
@@ -1323,8 +1385,8 @@ class GuiDocEditor(QPlainTextEdit):
1323
1385
  break
1324
1386
 
1325
1387
  if hasSelection:
1326
- cursor.setPosition(origA, QTextCursor.MoveAnchor)
1327
- cursor.setPosition(origB, QTextCursor.KeepAnchor)
1388
+ cursor.setPosition(origA, QTextCursor.MoveMode.MoveAnchor)
1389
+ cursor.setPosition(origB, QTextCursor.MoveMode.KeepAnchor)
1328
1390
  else:
1329
1391
  cursor.setPosition(origA)
1330
1392
 
@@ -1410,22 +1472,33 @@ class GuiDocEditor(QPlainTextEdit):
1410
1472
  If more than one block is selected, the formatting is applied to
1411
1473
  the first block.
1412
1474
  """
1413
- cursor = self._autoSelect()
1414
- if not cursor.hasSelection():
1415
- logger.warning("No selection made, nothing to do")
1416
- return False
1475
+ cursor = self.textCursor()
1476
+ posO = cursor.position()
1477
+ if cursor.hasSelection():
1478
+ select = _SelectAction.KEEP_SELECTION
1479
+ else:
1480
+ cursor = self._autoSelect()
1481
+ if cursor.hasSelection() and posO == cursor.selectionEnd():
1482
+ select = _SelectAction.MOVE_AFTER
1483
+ else:
1484
+ select = _SelectAction.KEEP_POSITION
1417
1485
 
1418
1486
  posS = cursor.selectionStart()
1419
1487
  posE = cursor.selectionEnd()
1488
+ if self._qDocument.characterAt(posO - 1) == fChar:
1489
+ logger.warning("Format repetition, cancelling action")
1490
+ cursor.clearSelection()
1491
+ cursor.setPosition(posO)
1492
+ self.setTextCursor(cursor)
1493
+ return False
1420
1494
 
1421
1495
  blockS = self._qDocument.findBlock(posS)
1422
1496
  blockE = self._qDocument.findBlock(posE)
1423
-
1424
1497
  if blockS != blockE:
1425
1498
  posE = blockS.position() + blockS.length() - 1
1426
1499
  cursor.clearSelection()
1427
- cursor.setPosition(posS, QTextCursor.MoveAnchor)
1428
- cursor.setPosition(posE, QTextCursor.KeepAnchor)
1500
+ cursor.setPosition(posS, QTextCursor.MoveMode.MoveAnchor)
1501
+ cursor.setPosition(posE, QTextCursor.MoveMode.KeepAnchor)
1429
1502
  self.setTextCursor(cursor)
1430
1503
 
1431
1504
  numB = 0
@@ -1443,34 +1516,26 @@ class GuiDocEditor(QPlainTextEdit):
1443
1516
  break
1444
1517
 
1445
1518
  if fLen == min(numA, numB):
1446
- self._clearSurrounding(cursor, fLen)
1447
- else:
1448
- self._wrapSelection(fChar*fLen)
1449
-
1450
- return True
1451
-
1452
- def _clearSurrounding(self, cursor: QTextCursor, nChars: int) -> bool:
1453
- """Clear n characters before and after the cursor."""
1454
- if not cursor.hasSelection():
1455
- logger.warning("No selection made, nothing to do")
1456
- return False
1519
+ cursor.clearSelection()
1520
+ cursor.beginEditBlock()
1521
+ cursor.setPosition(posS)
1522
+ for i in range(fLen):
1523
+ cursor.deletePreviousChar()
1524
+ cursor.setPosition(posE)
1525
+ for i in range(fLen):
1526
+ cursor.deletePreviousChar()
1527
+ cursor.endEditBlock()
1528
+ cursor.clearSelection()
1529
+ cursor.setPosition(posO - fLen)
1530
+ self.setTextCursor(cursor)
1457
1531
 
1458
- posS = cursor.selectionStart()
1459
- posE = cursor.selectionEnd()
1460
- cursor.clearSelection()
1461
- cursor.beginEditBlock()
1462
- cursor.setPosition(posS)
1463
- for i in range(nChars):
1464
- cursor.deletePreviousChar()
1465
- cursor.setPosition(posE)
1466
- for i in range(nChars):
1467
- cursor.deletePreviousChar()
1468
- cursor.endEditBlock()
1469
- cursor.clearSelection()
1532
+ else:
1533
+ self._wrapSelection(fChar*fLen, pos=posO, select=select)
1470
1534
 
1471
1535
  return True
1472
1536
 
1473
- def _wrapSelection(self, before: str, after: str | None = None) -> bool:
1537
+ def _wrapSelection(self, before: str, after: str | None = None, pos: int | None = None,
1538
+ select: _SelectAction = _SelectAction.NO_DECISION) -> bool:
1474
1539
  """Wrap the selected text in whatever is in tBefore and tAfter.
1475
1540
  If there is no selection, the autoSelect setting decides the
1476
1541
  action. AutoSelect will select the word under the cursor before
@@ -1479,10 +1544,17 @@ class GuiDocEditor(QPlainTextEdit):
1479
1544
  if after is None:
1480
1545
  after = before
1481
1546
 
1482
- cursor = self._autoSelect()
1483
- if not cursor.hasSelection():
1484
- logger.warning("No selection made, nothing to do")
1485
- return False
1547
+ cursor = self.textCursor()
1548
+ posO = pos if isinstance(pos, int) else cursor.position()
1549
+ if select == _SelectAction.NO_DECISION:
1550
+ if cursor.hasSelection():
1551
+ select = _SelectAction.KEEP_SELECTION
1552
+ else:
1553
+ cursor = self._autoSelect()
1554
+ if cursor.hasSelection() and posO == cursor.selectionEnd():
1555
+ select = _SelectAction.MOVE_AFTER
1556
+ else:
1557
+ select = _SelectAction.KEEP_POSITION
1486
1558
 
1487
1559
  posS = cursor.selectionStart()
1488
1560
  posE = cursor.selectionEnd()
@@ -1500,8 +1572,14 @@ class GuiDocEditor(QPlainTextEdit):
1500
1572
  cursor.insertText(before)
1501
1573
  cursor.endEditBlock()
1502
1574
 
1503
- cursor.setPosition(posE + len(before), QTextCursor.MoveAnchor)
1504
- cursor.setPosition(posS + len(before), QTextCursor.KeepAnchor)
1575
+ if select == _SelectAction.MOVE_AFTER:
1576
+ cursor.setPosition(posE + len(before + after))
1577
+ elif select == _SelectAction.KEEP_SELECTION:
1578
+ cursor.setPosition(posE + len(before), QTextCursor.MoveMode.MoveAnchor)
1579
+ cursor.setPosition(posS + len(before), QTextCursor.MoveMode.KeepAnchor)
1580
+ elif select == _SelectAction.KEEP_POSITION:
1581
+ cursor.setPosition(posO + len(before))
1582
+
1505
1583
  self.setTextCursor(cursor)
1506
1584
 
1507
1585
  return True
@@ -1522,7 +1600,9 @@ class GuiDocEditor(QPlainTextEdit):
1522
1600
  self._allowAutoReplace(False)
1523
1601
  for posC in range(posS, posE+1):
1524
1602
  cursor.setPosition(posC)
1525
- cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2)
1603
+ cursor.movePosition(
1604
+ QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 2
1605
+ )
1526
1606
  selText = cursor.selectedText()
1527
1607
 
1528
1608
  nS = len(selText)
@@ -1542,12 +1622,16 @@ class GuiDocEditor(QPlainTextEdit):
1542
1622
  cursor.setPosition(posC)
1543
1623
  if pC in closeCheck:
1544
1624
  cursor.beginEditBlock()
1545
- cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
1625
+ cursor.movePosition(
1626
+ QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 1
1627
+ )
1546
1628
  cursor.insertText(oQuote)
1547
1629
  cursor.endEditBlock()
1548
1630
  else:
1549
1631
  cursor.beginEditBlock()
1550
- cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
1632
+ cursor.movePosition(
1633
+ QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 1
1634
+ )
1551
1635
  cursor.insertText(cQuote)
1552
1636
  cursor.endEditBlock()
1553
1637
 
@@ -1667,7 +1751,7 @@ class GuiDocEditor(QPlainTextEdit):
1667
1751
  # Replace the block text
1668
1752
  cursor.beginEditBlock()
1669
1753
  posO = cursor.position()
1670
- cursor.select(QTextCursor.BlockUnderCursor)
1754
+ cursor.select(QTextCursor.SelectionType.BlockUnderCursor)
1671
1755
  posS = cursor.selectionStart()
1672
1756
  cursor.removeSelectedText()
1673
1757
  cursor.setPosition(posS)
@@ -1727,7 +1811,9 @@ class GuiDocEditor(QPlainTextEdit):
1727
1811
  cursor.beginEditBlock()
1728
1812
  cursor.clearSelection()
1729
1813
  cursor.setPosition(rS)
1730
- cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, rE-rS)
1814
+ cursor.movePosition(
1815
+ QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor, rE-rS
1816
+ )
1731
1817
  cursor.insertText(cleanText.rstrip() + "\n")
1732
1818
  cursor.endEditBlock()
1733
1819
 
@@ -1751,13 +1837,13 @@ class GuiDocEditor(QPlainTextEdit):
1751
1837
  block = cursor.block()
1752
1838
  text = block.text()
1753
1839
  if len(text) == 0:
1754
- return nwTrinary.UNKNOWN
1840
+ return nwTrinary.NEUTRAL
1755
1841
 
1756
1842
  if text.startswith("@") and isinstance(self._nwItem, NWItem):
1757
1843
 
1758
1844
  isGood, tBits, tPos = SHARED.project.index.scanThis(text)
1759
1845
  if not isGood:
1760
- return nwTrinary.UNKNOWN
1846
+ return nwTrinary.NEUTRAL
1761
1847
 
1762
1848
  tag = ""
1763
1849
  exist = False
@@ -1774,7 +1860,7 @@ class GuiDocEditor(QPlainTextEdit):
1774
1860
 
1775
1861
  if not tag or tag.startswith("@"):
1776
1862
  # The keyword cannot be looked up, so we ignore that
1777
- return nwTrinary.UNKNOWN
1863
+ return nwTrinary.NEUTRAL
1778
1864
 
1779
1865
  if follow and exist:
1780
1866
  logger.debug("Attempting to follow tag '%s'", tag)
@@ -1794,7 +1880,14 @@ class GuiDocEditor(QPlainTextEdit):
1794
1880
 
1795
1881
  return nwTrinary.POSITIVE if exist else nwTrinary.NEGATIVE
1796
1882
 
1797
- return nwTrinary.UNKNOWN
1883
+ return nwTrinary.NEUTRAL
1884
+
1885
+ def _emitRenameItem(self, block: QTextBlock) -> None:
1886
+ """Emit a signal to request an item be renamed."""
1887
+ if self._docHandle:
1888
+ text = block.text().lstrip("#").lstrip("!").strip()
1889
+ self.requestProjectItemRenamed.emit(self._docHandle, text)
1890
+ return
1798
1891
 
1799
1892
  def _openContextFromCursor(self) -> None:
1800
1893
  """Open the spell check context menu at the cursor."""
@@ -1887,7 +1980,9 @@ class GuiDocEditor(QPlainTextEdit):
1887
1980
  tInsert = tInsert + self._typPadChar
1888
1981
 
1889
1982
  if nDelete > 0:
1890
- cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, nDelete)
1983
+ cursor.movePosition(
1984
+ QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, nDelete
1985
+ )
1891
1986
  cursor.insertText(tInsert)
1892
1987
 
1893
1988
  return
@@ -1908,27 +2003,43 @@ class GuiDocEditor(QPlainTextEdit):
1908
2003
 
1909
2004
  def _autoSelect(self) -> QTextCursor:
1910
2005
  """Return a cursor which may or may not have a selection based
1911
- on user settings and document action.
2006
+ on user settings and document action. The selection will be the
2007
+ word closest to the cursor consisting of alphanumerical unicode
2008
+ characters.
1912
2009
  """
1913
2010
  cursor = self.textCursor()
1914
2011
  if CONFIG.autoSelect and not cursor.hasSelection():
1915
- cursor.select(QTextCursor.WordUnderCursor)
1916
- posS = cursor.selectionStart()
1917
- posE = cursor.selectionEnd()
2012
+ cPos = cursor.position()
2013
+ bPos = cursor.block().position()
2014
+ bLen = cursor.block().length()
2015
+ apos = nwUnicode.U_APOS + nwUnicode.U_RSQUO
2016
+
2017
+ # Scan backward
2018
+ sPos = cPos
2019
+ for i in range(cPos - bPos):
2020
+ sPos = cPos - i - 1
2021
+ cOne = self._qDocument.characterAt(sPos)
2022
+ cTwo = self._qDocument.characterAt(sPos - 1)
2023
+ if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
2024
+ sPos += 1
2025
+ break
1918
2026
 
1919
- # Underscore counts as a part of the word, so check that the
1920
- # selection isn't wrapped in italics markers.
1921
- reSelect = False
1922
- if self._qDocument.characterAt(posS) == "_":
1923
- posS += 1
1924
- reSelect = True
1925
- if self._qDocument.characterAt(posE) == "_":
1926
- posE -= 1
1927
- reSelect = True
1928
- if reSelect:
1929
- cursor.clearSelection()
1930
- cursor.setPosition(posS, QTextCursor.MoveAnchor)
1931
- cursor.setPosition(posE-1, QTextCursor.KeepAnchor)
2027
+ # Scan forward
2028
+ ePos = cPos
2029
+ for i in range(bPos + bLen - cPos):
2030
+ ePos = cPos + i
2031
+ cOne = self._qDocument.characterAt(ePos)
2032
+ cTwo = self._qDocument.characterAt(ePos + 1)
2033
+ if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
2034
+ break
2035
+
2036
+ if ePos - sPos <= 0:
2037
+ # No selection possible
2038
+ return cursor
2039
+
2040
+ cursor.clearSelection()
2041
+ cursor.setPosition(sPos, QTextCursor.MoveMode.MoveAnchor)
2042
+ cursor.setPosition(ePos, QTextCursor.MoveMode.KeepAnchor)
1932
2043
 
1933
2044
  self.setTextCursor(cursor)
1934
2045
 
@@ -1940,18 +2051,18 @@ class GuiDocEditor(QPlainTextEdit):
1940
2051
  cursor.clearSelection()
1941
2052
  cursor.select(mode)
1942
2053
 
1943
- if mode == QTextCursor.WordUnderCursor:
2054
+ if mode == QTextCursor.SelectionType.WordUnderCursor:
1944
2055
  cursor = self._autoSelect()
1945
2056
 
1946
- elif mode == QTextCursor.BlockUnderCursor:
2057
+ elif mode == QTextCursor.SelectionType.BlockUnderCursor:
1947
2058
  # This selection mode also selects the preceding paragraph
1948
2059
  # separator, which we want to avoid.
1949
2060
  posS = cursor.selectionStart()
1950
2061
  posE = cursor.selectionEnd()
1951
2062
  selTxt = cursor.selectedText()
1952
2063
  if selTxt.startswith(nwUnicode.U_PSEP):
1953
- cursor.setPosition(posS+1, QTextCursor.MoveAnchor)
1954
- cursor.setPosition(posE, QTextCursor.KeepAnchor)
2064
+ cursor.setPosition(posS+1, QTextCursor.MoveMode.MoveAnchor)
2065
+ cursor.setPosition(posE, QTextCursor.MoveMode.KeepAnchor)
1955
2066
 
1956
2067
  self.setTextCursor(cursor)
1957
2068
 
@@ -2011,7 +2122,7 @@ class MetaCompleter(QMenu):
2011
2122
  length = len(lookup)
2012
2123
  suffix = ""
2013
2124
  options = list(filter(
2014
- lambda x: lookup in x.lower(), SHARED.project.index.getTags(
2125
+ lambda x: lookup in x.lower(), SHARED.project.index.getClassTags(
2015
2126
  nwKeyWords.KEY_CLASS.get(kw.strip(), nwItemClass.NO_CLASS)
2016
2127
  )
2017
2128
  ))[:15]
@@ -2033,7 +2144,10 @@ class MetaCompleter(QMenu):
2033
2144
  def keyPressEvent(self, event: QKeyEvent) -> None:
2034
2145
  """Capture keypresses and forward most of them to the editor."""
2035
2146
  parent = self.parent()
2036
- if event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_Return, Qt.Key_Enter, Qt.Key_Escape):
2147
+ if event.key() in (
2148
+ Qt.Key.Key_Up, Qt.Key.Key_Down, Qt.Key.Key_Return,
2149
+ Qt.Key.Key_Enter, Qt.Key.Key_Escape
2150
+ ):
2037
2151
  super().keyPressEvent(event)
2038
2152
  elif isinstance(parent, GuiDocEditor):
2039
2153
  parent.keyPressEvent(event)
@@ -2123,39 +2237,65 @@ class GuiDocToolBar(QWidget):
2123
2237
  # General Buttons
2124
2238
  # ===============
2125
2239
 
2126
- self.tbMode = QToolButton(self)
2127
- self.tbMode.setToolTip(self.tr("Toggle Markdown or Shortcodes Mode"))
2128
- self.tbMode.setIconSize(iconSize)
2129
- self.tbMode.setCheckable(True)
2130
- self.tbMode.setChecked(CONFIG.useShortcodes)
2131
- self.tbMode.toggled.connect(self._toggleFormatMode)
2240
+ self.tbBoldMD = QToolButton(self)
2241
+ self.tbBoldMD.setIconSize(iconSize)
2242
+ self.tbBoldMD.setToolTip(self.tr("Markdown Bold"))
2243
+ self.tbBoldMD.clicked.connect(
2244
+ lambda: self.requestDocAction.emit(nwDocAction.MD_BOLD)
2245
+ )
2246
+
2247
+ self.tbItalicMD = QToolButton(self)
2248
+ self.tbItalicMD.setIconSize(iconSize)
2249
+ self.tbItalicMD.setToolTip(self.tr("Markdown Italic"))
2250
+ self.tbItalicMD.clicked.connect(
2251
+ lambda: self.requestDocAction.emit(nwDocAction.MD_ITALIC)
2252
+ )
2253
+
2254
+ self.tbStrikeMD = QToolButton(self)
2255
+ self.tbStrikeMD.setIconSize(iconSize)
2256
+ self.tbStrikeMD.setToolTip(self.tr("Markdown Strikethrough"))
2257
+ self.tbStrikeMD.clicked.connect(
2258
+ lambda: self.requestDocAction.emit(nwDocAction.MD_STRIKE)
2259
+ )
2132
2260
 
2133
2261
  self.tbBold = QToolButton(self)
2134
2262
  self.tbBold.setIconSize(iconSize)
2135
- self.tbBold.clicked.connect(self._formatBold)
2263
+ self.tbBold.setToolTip(self.tr("Shortcode Bold"))
2264
+ self.tbBold.clicked.connect(
2265
+ lambda: self.requestDocAction.emit(nwDocAction.SC_BOLD)
2266
+ )
2136
2267
 
2137
2268
  self.tbItalic = QToolButton(self)
2138
2269
  self.tbItalic.setIconSize(iconSize)
2139
- self.tbItalic.clicked.connect(self._formatItalic)
2270
+ self.tbItalic.setToolTip(self.tr("Shortcode Italic"))
2271
+ self.tbItalic.clicked.connect(
2272
+ lambda: self.requestDocAction.emit(nwDocAction.SC_ITALIC)
2273
+ )
2140
2274
 
2141
2275
  self.tbStrike = QToolButton(self)
2142
2276
  self.tbStrike.setIconSize(iconSize)
2143
- self.tbStrike.clicked.connect(self._formatStrike)
2277
+ self.tbStrike.setToolTip(self.tr("Shortcode Strikethrough"))
2278
+ self.tbStrike.clicked.connect(
2279
+ lambda: self.requestDocAction.emit(nwDocAction.SC_STRIKE)
2280
+ )
2144
2281
 
2145
2282
  self.tbUnderline = QToolButton(self)
2146
2283
  self.tbUnderline.setIconSize(iconSize)
2284
+ self.tbUnderline.setToolTip(self.tr("Shortcode Underline"))
2147
2285
  self.tbUnderline.clicked.connect(
2148
2286
  lambda: self.requestDocAction.emit(nwDocAction.SC_ULINE)
2149
2287
  )
2150
2288
 
2151
2289
  self.tbSuperscript = QToolButton(self)
2152
2290
  self.tbSuperscript.setIconSize(iconSize)
2291
+ self.tbSuperscript.setToolTip(self.tr("Shortcode Superscript"))
2153
2292
  self.tbSuperscript.clicked.connect(
2154
2293
  lambda: self.requestDocAction.emit(nwDocAction.SC_SUP)
2155
2294
  )
2156
2295
 
2157
2296
  self.tbSubscript = QToolButton(self)
2158
2297
  self.tbSubscript.setIconSize(iconSize)
2298
+ self.tbSubscript.setToolTip(self.tr("Shortcode Subscript"))
2159
2299
  self.tbSubscript.clicked.connect(
2160
2300
  lambda: self.requestDocAction.emit(nwDocAction.SC_SUB)
2161
2301
  )
@@ -2164,7 +2304,10 @@ class GuiDocToolBar(QWidget):
2164
2304
  # ========
2165
2305
 
2166
2306
  self.outerBox = QVBoxLayout()
2167
- self.outerBox.addWidget(self.tbMode)
2307
+ self.outerBox.addWidget(self.tbBoldMD)
2308
+ self.outerBox.addWidget(self.tbItalicMD)
2309
+ self.outerBox.addWidget(self.tbStrikeMD)
2310
+ self.outerBox.addSpacing(cM)
2168
2311
  self.outerBox.addWidget(self.tbBold)
2169
2312
  self.outerBox.addWidget(self.tbItalic)
2170
2313
  self.outerBox.addWidget(self.tbStrike)
@@ -2187,13 +2330,14 @@ class GuiDocToolBar(QWidget):
2187
2330
  def updateTheme(self) -> None:
2188
2331
  """Initialise GUI elements that depend on specific settings."""
2189
2332
  palette = QPalette()
2190
- palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack))
2191
- palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText))
2192
- palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText))
2333
+ palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
2334
+ palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
2335
+ palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
2193
2336
  self.setPalette(palette)
2194
2337
 
2195
- tPx = int(0.8*SHARED.theme.fontPixelSize)
2196
- self.tbMode.setIcon(SHARED.theme.getToggleIcon("fmt_mode", (tPx, tPx)))
2338
+ self.tbBoldMD.setIcon(SHARED.theme.getIcon("fmt_bold-md"))
2339
+ self.tbItalicMD.setIcon(SHARED.theme.getIcon("fmt_italic-md"))
2340
+ self.tbStrikeMD.setIcon(SHARED.theme.getIcon("fmt_strike-md"))
2197
2341
  self.tbBold.setIcon(SHARED.theme.getIcon("fmt_bold"))
2198
2342
  self.tbItalic.setIcon(SHARED.theme.getIcon("fmt_italic"))
2199
2343
  self.tbStrike.setIcon(SHARED.theme.getIcon("fmt_strike"))
@@ -2203,40 +2347,6 @@ class GuiDocToolBar(QWidget):
2203
2347
 
2204
2348
  return
2205
2349
 
2206
- ##
2207
- # Private Slots
2208
- ##
2209
-
2210
- @pyqtSlot(bool)
2211
- def _toggleFormatMode(self, checked: bool) -> None:
2212
- """Toggle the formatting mode."""
2213
- CONFIG.useShortcodes = checked
2214
- return
2215
-
2216
- @pyqtSlot()
2217
- def _formatBold(self):
2218
- """Call the bold format action."""
2219
- self.requestDocAction.emit(
2220
- nwDocAction.SC_BOLD if self.tbMode.isChecked() else nwDocAction.STRONG
2221
- )
2222
- return
2223
-
2224
- @pyqtSlot()
2225
- def _formatItalic(self):
2226
- """Call the italic format action."""
2227
- self.requestDocAction.emit(
2228
- nwDocAction.SC_ITALIC if self.tbMode.isChecked() else nwDocAction.EMPH
2229
- )
2230
- return
2231
-
2232
- @pyqtSlot()
2233
- def _formatStrike(self):
2234
- """Call the strikethrough format action."""
2235
- self.requestDocAction.emit(
2236
- nwDocAction.SC_STRIKE if self.tbMode.isChecked() else nwDocAction.STRIKE
2237
- )
2238
- return
2239
-
2240
2350
  # END Class GuiDocToolBar
2241
2351
 
2242
2352
 
@@ -2269,7 +2379,7 @@ class GuiDocEditSearch(QFrame):
2269
2379
 
2270
2380
  self.setContentsMargins(0, 0, 0, 0)
2271
2381
  self.setAutoFillBackground(True)
2272
- self.setFrameStyle(QFrame.StyledPanel | QFrame.Plain)
2382
+ self.setFrameStyle(QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
2273
2383
 
2274
2384
  self.mainBox = QGridLayout(self)
2275
2385
  self.setLayout(self.mainBox)
@@ -2288,7 +2398,7 @@ class GuiDocEditSearch(QFrame):
2288
2398
  self.replaceBox.returnPressed.connect(self._doReplace)
2289
2399
 
2290
2400
  self.searchOpt = QToolBar(self)
2291
- self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
2401
+ self.searchOpt.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2292
2402
  self.searchOpt.setIconSize(QSize(tPx, tPx))
2293
2403
  self.searchOpt.setContentsMargins(0, 0, 0, 0)
2294
2404
 
@@ -2350,7 +2460,7 @@ class GuiDocEditSearch(QFrame):
2350
2460
  bPx = self.searchBox.sizeHint().height()
2351
2461
 
2352
2462
  self.showReplace = QToolButton(self)
2353
- self.showReplace.setArrowType(Qt.RightArrow)
2463
+ self.showReplace.setArrowType(Qt.ArrowType.RightArrow)
2354
2464
  self.showReplace.setCheckable(True)
2355
2465
  self.showReplace.toggled.connect(self._doToggleReplace)
2356
2466
 
@@ -2364,8 +2474,8 @@ class GuiDocEditSearch(QFrame):
2364
2474
  self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
2365
2475
  self.replaceButton.clicked.connect(self._doReplace)
2366
2476
 
2367
- self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft)
2368
- self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 3, Qt.AlignRight)
2477
+ self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignmentFlag.AlignLeft)
2478
+ self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 3, Qt.AlignmentFlag.AlignRight)
2369
2479
  self.mainBox.addWidget(self.showReplace, 1, 0, 1, 1)
2370
2480
  self.mainBox.addWidget(self.searchBox, 1, 1, 1, 2)
2371
2481
  self.mainBox.addWidget(self.searchButton, 1, 3, 1, 1)
@@ -2423,18 +2533,18 @@ class GuiDocEditSearch(QFrame):
2423
2533
  # only added in Qt 5.13. Otherwise, 5.3 and up supports
2424
2534
  # only the QRegExp class.
2425
2535
  if CONFIG.verQtValue >= 0x050d00:
2426
- rxOpt = QRegularExpression.UseUnicodePropertiesOption
2536
+ rxOpt = QRegularExpression.PatternOption.UseUnicodePropertiesOption
2427
2537
  if not self.isCaseSense:
2428
- rxOpt |= QRegularExpression.CaseInsensitiveOption
2538
+ rxOpt |= QRegularExpression.PatternOption.CaseInsensitiveOption
2429
2539
  regEx = QRegularExpression(text, rxOpt)
2430
2540
  self._alertSearchValid(regEx.isValid())
2431
2541
  return regEx
2432
2542
  else: # pragma: no cover
2433
2543
  # >= 50300 to < 51300
2434
2544
  if self.isCaseSense:
2435
- rxOpt = Qt.CaseSensitive
2545
+ rxOpt = Qt.CaseSensitivity.CaseSensitive
2436
2546
  else:
2437
- rxOpt = Qt.CaseInsensitive
2547
+ rxOpt = Qt.CaseSensitivity.CaseInsensitive
2438
2548
  regEx = QRegExp(text, rxOpt)
2439
2549
  self._alertSearchValid(regEx.isValid())
2440
2550
  return regEx
@@ -2570,7 +2680,7 @@ class GuiDocEditSearch(QFrame):
2570
2680
  def _doSearch(self) -> None:
2571
2681
  """Call the search action function for the document editor."""
2572
2682
  modKey = qApp.keyboardModifiers()
2573
- if modKey == Qt.ShiftModifier:
2683
+ if modKey == Qt.KeyboardModifier.ShiftModifier:
2574
2684
  self.docEditor.findNext(goBack=True)
2575
2685
  else:
2576
2686
  self.docEditor.findNext()
@@ -2586,9 +2696,9 @@ class GuiDocEditSearch(QFrame):
2586
2696
  def _doToggleReplace(self, state: bool) -> None:
2587
2697
  """Toggle the show/hide of the replace box."""
2588
2698
  if state:
2589
- self.showReplace.setArrowType(Qt.DownArrow)
2699
+ self.showReplace.setArrowType(Qt.ArrowType.DownArrow)
2590
2700
  else:
2591
- self.showReplace.setArrowType(Qt.RightArrow)
2701
+ self.showReplace.setArrowType(Qt.ArrowType.RightArrow)
2592
2702
  self.replaceBox.setVisible(state)
2593
2703
  self.replaceButton.setVisible(state)
2594
2704
  self.repVisible = state
@@ -2641,7 +2751,7 @@ class GuiDocEditSearch(QFrame):
2641
2751
  isn't valid. Take the colour from the replace box.
2642
2752
  """
2643
2753
  qPalette = self.replaceBox.palette()
2644
- qPalette.setColor(QPalette.Base, self.rxCol[isValid])
2754
+ qPalette.setColor(QPalette.ColorRole.Base, self.rxCol[isValid])
2645
2755
  self.searchBox.setPalette(qPalette)
2646
2756
  return
2647
2757
 
@@ -2682,7 +2792,7 @@ class GuiDocEditHeader(QWidget):
2682
2792
  self.itemTitle.setMargin(0)
2683
2793
  self.itemTitle.setContentsMargins(0, 0, 0, 0)
2684
2794
  self.itemTitle.setAutoFillBackground(True)
2685
- self.itemTitle.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
2795
+ self.itemTitle.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
2686
2796
  self.itemTitle.setFixedHeight(fPx)
2687
2797
 
2688
2798
  lblFont = self.itemTitle.font()
@@ -2694,7 +2804,7 @@ class GuiDocEditHeader(QWidget):
2694
2804
  self.tbButton.setContentsMargins(0, 0, 0, 0)
2695
2805
  self.tbButton.setIconSize(iconSize)
2696
2806
  self.tbButton.setFixedSize(fPx, fPx)
2697
- self.tbButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
2807
+ self.tbButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2698
2808
  self.tbButton.setVisible(False)
2699
2809
  self.tbButton.setToolTip(self.tr("Toggle Tool Bar"))
2700
2810
  self.tbButton.clicked.connect(lambda: self.toggleToolBarRequest.emit())
@@ -2703,7 +2813,7 @@ class GuiDocEditHeader(QWidget):
2703
2813
  self.searchButton.setContentsMargins(0, 0, 0, 0)
2704
2814
  self.searchButton.setIconSize(iconSize)
2705
2815
  self.searchButton.setFixedSize(fPx, fPx)
2706
- self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
2816
+ self.searchButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2707
2817
  self.searchButton.setVisible(False)
2708
2818
  self.searchButton.setToolTip(self.tr("Search"))
2709
2819
  self.searchButton.clicked.connect(self.docEditor.toggleSearch)
@@ -2712,7 +2822,7 @@ class GuiDocEditHeader(QWidget):
2712
2822
  self.minmaxButton.setContentsMargins(0, 0, 0, 0)
2713
2823
  self.minmaxButton.setIconSize(iconSize)
2714
2824
  self.minmaxButton.setFixedSize(fPx, fPx)
2715
- self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
2825
+ self.minmaxButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2716
2826
  self.minmaxButton.setVisible(False)
2717
2827
  self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
2718
2828
  self.minmaxButton.clicked.connect(lambda: self.docEditor.toggleFocusModeRequest.emit())
@@ -2721,7 +2831,7 @@ class GuiDocEditHeader(QWidget):
2721
2831
  self.closeButton.setContentsMargins(0, 0, 0, 0)
2722
2832
  self.closeButton.setIconSize(iconSize)
2723
2833
  self.closeButton.setFixedSize(fPx, fPx)
2724
- self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
2834
+ self.closeButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2725
2835
  self.closeButton.setVisible(False)
2726
2836
  self.closeButton.setToolTip(self.tr("Close"))
2727
2837
  self.closeButton.clicked.connect(self._closeDocument)
@@ -2779,9 +2889,9 @@ class GuiDocEditHeader(QWidget):
2779
2889
  theme rather than the main GUI.
2780
2890
  """
2781
2891
  palette = QPalette()
2782
- palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack))
2783
- palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText))
2784
- palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText))
2892
+ palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
2893
+ palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
2894
+ palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
2785
2895
 
2786
2896
  self.setPalette(palette)
2787
2897
  self.itemTitle.setPalette(palette)
@@ -2857,7 +2967,8 @@ class GuiDocEditHeader(QWidget):
2857
2967
  """Capture a click on the title and ensure that the item is
2858
2968
  selected in the project tree.
2859
2969
  """
2860
- self.mainGui.projView.setSelectedHandle(self._docHandle, doScroll=True)
2970
+ if event.button() == Qt.MouseButton.LeftButton:
2971
+ self.docEditor.requestProjectItemSelected.emit(self._docHandle, True)
2861
2972
  return
2862
2973
 
2863
2974
  # END Class GuiDocEditHeader
@@ -2894,11 +3005,13 @@ class GuiDocEditFooter(QWidget):
2894
3005
  self.setContentsMargins(0, 0, 0, 0)
2895
3006
  self.setAutoFillBackground(True)
2896
3007
 
3008
+ alLeftTop = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
3009
+
2897
3010
  # Status
2898
3011
  self.statusIcon = QLabel("")
2899
3012
  self.statusIcon.setContentsMargins(0, 0, 0, 0)
2900
3013
  self.statusIcon.setFixedHeight(self.sPx)
2901
- self.statusIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3014
+ self.statusIcon.setAlignment(alLeftTop)
2902
3015
 
2903
3016
  self.statusText = QLabel(self.tr("Status"))
2904
3017
  self.statusText.setIndent(0)
@@ -2906,14 +3019,14 @@ class GuiDocEditFooter(QWidget):
2906
3019
  self.statusText.setContentsMargins(0, 0, 0, 0)
2907
3020
  self.statusText.setAutoFillBackground(True)
2908
3021
  self.statusText.setFixedHeight(fPx)
2909
- self.statusText.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3022
+ self.statusText.setAlignment(alLeftTop)
2910
3023
  self.statusText.setFont(lblFont)
2911
3024
 
2912
3025
  # Lines
2913
3026
  self.linesIcon = QLabel("")
2914
3027
  self.linesIcon.setContentsMargins(0, 0, 0, 0)
2915
3028
  self.linesIcon.setFixedHeight(self.sPx)
2916
- self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3029
+ self.linesIcon.setAlignment(alLeftTop)
2917
3030
 
2918
3031
  self.linesText = QLabel("")
2919
3032
  self.linesText.setIndent(0)
@@ -2921,14 +3034,14 @@ class GuiDocEditFooter(QWidget):
2921
3034
  self.linesText.setContentsMargins(0, 0, 0, 0)
2922
3035
  self.linesText.setAutoFillBackground(True)
2923
3036
  self.linesText.setFixedHeight(fPx)
2924
- self.linesText.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3037
+ self.linesText.setAlignment(alLeftTop)
2925
3038
  self.linesText.setFont(lblFont)
2926
3039
 
2927
3040
  # Words
2928
3041
  self.wordsIcon = QLabel("")
2929
3042
  self.wordsIcon.setContentsMargins(0, 0, 0, 0)
2930
3043
  self.wordsIcon.setFixedHeight(self.sPx)
2931
- self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3044
+ self.wordsIcon.setAlignment(alLeftTop)
2932
3045
 
2933
3046
  self.wordsText = QLabel("")
2934
3047
  self.wordsText.setIndent(0)
@@ -2936,7 +3049,7 @@ class GuiDocEditFooter(QWidget):
2936
3049
  self.wordsText.setContentsMargins(0, 0, 0, 0)
2937
3050
  self.wordsText.setAutoFillBackground(True)
2938
3051
  self.wordsText.setFixedHeight(fPx)
2939
- self.wordsText.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3052
+ self.wordsText.setAlignment(alLeftTop)
2940
3053
  self.wordsText.setFont(lblFont)
2941
3054
 
2942
3055
  # Assemble Layout
@@ -2984,9 +3097,9 @@ class GuiDocEditFooter(QWidget):
2984
3097
  theme rather than the main GUI.
2985
3098
  """
2986
3099
  palette = QPalette()
2987
- palette.setColor(QPalette.Window, QColor(*SHARED.theme.colBack))
2988
- palette.setColor(QPalette.WindowText, QColor(*SHARED.theme.colText))
2989
- palette.setColor(QPalette.Text, QColor(*SHARED.theme.colText))
3100
+ palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
3101
+ palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
3102
+ palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
2990
3103
 
2991
3104
  self.setPalette(palette)
2992
3105
  self.statusText.setPalette(palette)