novelWriter 2.2b1__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 (62) hide show
  1. {novelWriter-2.2b1.dist-info → novelWriter-2.2rc1.dist-info}/METADATA +3 -3
  2. {novelWriter-2.2b1.dist-info → novelWriter-2.2rc1.dist-info}/RECORD +60 -48
  3. novelwriter/__init__.py +3 -3
  4. novelwriter/assets/i18n/project_en_GB.json +1 -0
  5. novelwriter/assets/icons/novelwriter.ico +0 -0
  6. novelwriter/assets/icons/typicons_dark/icons.conf +8 -1
  7. novelwriter/assets/icons/typicons_dark/nw_deco-h2-narrow.svg +4 -0
  8. novelwriter/assets/icons/typicons_dark/nw_deco-h3-narrow.svg +4 -0
  9. novelwriter/assets/icons/typicons_dark/nw_deco-h4-narrow.svg +4 -0
  10. novelwriter/assets/icons/typicons_dark/nw_deco-note.svg +4 -0
  11. novelwriter/assets/icons/typicons_dark/nw_panel.svg +4 -0
  12. novelwriter/assets/icons/typicons_dark/typ_eye.svg +4 -0
  13. novelwriter/assets/icons/typicons_light/icons.conf +8 -1
  14. novelwriter/assets/icons/typicons_light/nw_deco-h2-narrow.svg +4 -0
  15. novelwriter/assets/icons/typicons_light/nw_deco-h3-narrow.svg +4 -0
  16. novelwriter/assets/icons/typicons_light/nw_deco-h4-narrow.svg +4 -0
  17. novelwriter/assets/icons/typicons_light/nw_deco-note.svg +4 -0
  18. novelwriter/assets/icons/typicons_light/nw_panel.svg +4 -0
  19. novelwriter/assets/icons/typicons_light/typ_eye.svg +4 -0
  20. novelwriter/assets/icons/x-novelwriter-project.ico +0 -0
  21. novelwriter/assets/manual.pdf +0 -0
  22. novelwriter/assets/sample.zip +0 -0
  23. novelwriter/assets/text/release_notes.htm +4 -4
  24. novelwriter/common.py +22 -1
  25. novelwriter/config.py +12 -27
  26. novelwriter/constants.py +20 -3
  27. novelwriter/core/buildsettings.py +1 -1
  28. novelwriter/core/coretools.py +6 -1
  29. novelwriter/core/index.py +100 -34
  30. novelwriter/core/options.py +3 -0
  31. novelwriter/core/project.py +2 -2
  32. novelwriter/core/projectdata.py +1 -1
  33. novelwriter/core/tohtml.py +9 -3
  34. novelwriter/core/tokenizer.py +27 -20
  35. novelwriter/core/tomd.py +4 -0
  36. novelwriter/core/toodt.py +11 -4
  37. novelwriter/dialogs/preferences.py +80 -82
  38. novelwriter/dialogs/updates.py +25 -14
  39. novelwriter/enum.py +14 -4
  40. novelwriter/gui/doceditor.py +282 -177
  41. novelwriter/gui/dochighlight.py +7 -9
  42. novelwriter/gui/docviewer.py +142 -319
  43. novelwriter/gui/docviewerpanel.py +457 -0
  44. novelwriter/gui/editordocument.py +1 -1
  45. novelwriter/gui/mainmenu.py +16 -7
  46. novelwriter/gui/outline.py +10 -6
  47. novelwriter/gui/projtree.py +461 -376
  48. novelwriter/gui/sidebar.py +3 -3
  49. novelwriter/gui/statusbar.py +1 -1
  50. novelwriter/gui/theme.py +21 -2
  51. novelwriter/guimain.py +86 -32
  52. novelwriter/shared.py +23 -1
  53. novelwriter/tools/dictionaries.py +268 -0
  54. novelwriter/tools/manusbuild.py +17 -6
  55. novelwriter/tools/manuscript.py +1 -1
  56. novelwriter/tools/writingstats.py +1 -1
  57. novelwriter/assets/icons/typicons_dark/typ_at.svg +0 -4
  58. novelwriter/assets/icons/typicons_light/typ_at.svg +0 -4
  59. {novelWriter-2.2b1.dist-info → novelWriter-2.2rc1.dist-info}/LICENSE.md +0 -0
  60. {novelWriter-2.2b1.dist-info → novelWriter-2.2rc1.dist-info}/WHEEL +0 -0
  61. {novelWriter-2.2b1.dist-info → novelWriter-2.2rc1.dist-info}/entry_points.txt +0 -0
  62. {novelWriter-2.2b1.dist-info → novelWriter-2.2rc1.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,8 @@ 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
17
  Copyright 2018–2023, Veronica Berglyd Olsen
@@ -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
@@ -67,12 +69,22 @@ if TYPE_CHECKING: # pragma: no cover
67
69
  logger = logging.getLogger(__name__)
68
70
 
69
71
 
72
+ class _SelectAction(Enum):
73
+
74
+ NO_DECISION = 0
75
+ KEEP_SELECTION = 1
76
+ KEEP_POSITION = 2
77
+ MOVE_AFTER = 3
78
+
79
+ # END Class _SelectAction
80
+
81
+
70
82
  class GuiDocEditor(QPlainTextEdit):
71
83
  """Gui Widget: Main Document Editor"""
72
84
 
73
85
  MOVE_KEYS = (
74
- Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down,
75
- Qt.Key_PageUp, Qt.Key_PageDown
86
+ Qt.Key.Key_Left, Qt.Key.Key_Right, Qt.Key.Key_Up, Qt.Key.Key_Down,
87
+ Qt.Key.Key_PageUp, Qt.Key.Key_PageDown
76
88
  )
77
89
 
78
90
  # Custom Signals
@@ -85,6 +97,8 @@ class GuiDocEditor(QPlainTextEdit):
85
97
  spellCheckStateChanged = pyqtSignal(bool)
86
98
  closeDocumentRequest = pyqtSignal()
87
99
  toggleFocusModeRequest = pyqtSignal()
100
+ requestProjectItemSelected = pyqtSignal(str, bool)
101
+ requestProjectItemRenamed = pyqtSignal(str, str)
88
102
 
89
103
  def __init__(self, mainGui: GuiMain) -> None:
90
104
  super().__init__(parent=mainGui)
@@ -145,29 +159,28 @@ class GuiDocEditor(QPlainTextEdit):
145
159
  self.docToolBar.requestDocAction.connect(self.docAction)
146
160
 
147
161
  # Context Menu
148
- self.setContextMenuPolicy(Qt.CustomContextMenu)
162
+ self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
149
163
  self.customContextMenuRequested.connect(self._openContextMenu)
150
164
 
151
165
  # Editor Settings
152
166
  self.setMinimumWidth(CONFIG.pxInt(300))
153
167
  self.setAutoFillBackground(True)
154
- self.setFrameStyle(QFrame.NoFrame)
155
- self.setCenterOnScroll(True)
168
+ self.setFrameStyle(QFrame.Shape.NoFrame)
156
169
 
157
170
  # Custom Shortcuts
158
171
  self.keyContext = QShortcut(self)
159
172
  self.keyContext.setKey("Ctrl+.")
160
- self.keyContext.setContext(Qt.WidgetShortcut)
173
+ self.keyContext.setContext(Qt.ShortcutContext.WidgetShortcut)
161
174
  self.keyContext.activated.connect(self._openContextFromCursor)
162
175
 
163
176
  self.followTag1 = QShortcut(self)
164
- self.followTag1.setKey(Qt.Key_Return | Qt.ControlModifier)
165
- self.followTag1.setContext(Qt.WidgetShortcut)
177
+ self.followTag1.setKey(Qt.Key.Key_Return | Qt.KeyboardModifier.ControlModifier)
178
+ self.followTag1.setContext(Qt.ShortcutContext.WidgetShortcut)
166
179
  self.followTag1.activated.connect(self._processTag)
167
180
 
168
181
  self.followTag2 = QShortcut(self)
169
- self.followTag2.setKey(Qt.Key_Enter | Qt.ControlModifier)
170
- self.followTag2.setContext(Qt.WidgetShortcut)
182
+ self.followTag2.setKey(Qt.Key.Key_Enter | Qt.KeyboardModifier.ControlModifier)
183
+ self.followTag2.setContext(Qt.ShortcutContext.WidgetShortcut)
171
184
  self.followTag2.activated.connect(self._processTag)
172
185
 
173
186
  # Set Up Document Word Counter
@@ -263,14 +276,14 @@ class GuiDocEditor(QPlainTextEdit):
263
276
  def updateSyntaxColours(self) -> None:
264
277
  """Update the syntax highlighting theme."""
265
278
  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))
279
+ mainPalette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
280
+ mainPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
281
+ mainPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
269
282
  self.setPalette(mainPalette)
270
283
 
271
284
  docPalette = self.viewport().palette()
272
- docPalette.setColor(QPalette.Base, QColor(*SHARED.theme.colBack))
273
- docPalette.setColor(QPalette.Text, QColor(*SHARED.theme.colText))
285
+ docPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
286
+ docPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
274
287
  self.viewport().setPalette(docPalette)
275
288
 
276
289
  self.docHeader.matchColours()
@@ -322,24 +335,25 @@ class GuiDocEditor(QPlainTextEdit):
322
335
  options = QTextOption()
323
336
 
324
337
  if CONFIG.doJustify:
325
- options.setAlignment(Qt.AlignJustify)
338
+ options.setAlignment(Qt.AlignmentFlag.AlignJustify)
326
339
  if CONFIG.showTabsNSpaces:
327
- options.setFlags(options.flags() | QTextOption.ShowTabsAndSpaces)
340
+ options.setFlags(options.flags() | QTextOption.Flag.ShowTabsAndSpaces)
328
341
  if CONFIG.showLineEndings:
329
- options.setFlags(options.flags() | QTextOption.ShowLineAndParagraphSeparators)
342
+ options.setFlags(options.flags() | QTextOption.Flag.ShowLineAndParagraphSeparators)
330
343
 
331
344
  self._qDocument.setDefaultTextOption(options)
332
345
 
333
- # Scroll bars
346
+ # Scrolling
347
+ self.setCenterOnScroll(CONFIG.scrollPastEnd)
334
348
  if CONFIG.hideVScroll:
335
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
349
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
336
350
  else:
337
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
351
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
338
352
 
339
353
  if CONFIG.hideHScroll:
340
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
354
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
341
355
  else:
342
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
356
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
343
357
 
344
358
  # Refresh the tab stops
345
359
  self.setTabStopDistance(CONFIG.getTabWidth())
@@ -358,7 +372,7 @@ class GuiDocEditor(QPlainTextEdit):
358
372
 
359
373
  return
360
374
 
361
- def loadText(self, tHandle, tLine=None) -> bool:
375
+ def loadText(self, tHandle: str, tLine=None) -> bool:
362
376
  """Load text from a document into the editor. If we have an I/O
363
377
  error, we must handle this and clear the editor so that we don't
364
378
  risk overwriting the file if it exists. This can for instance
@@ -376,7 +390,7 @@ class GuiDocEditor(QPlainTextEdit):
376
390
  self.clearEditor()
377
391
  return False
378
392
 
379
- qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
393
+ qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
380
394
  self._docHandle = tHandle
381
395
 
382
396
  self._allowAutoReplace(False)
@@ -429,7 +443,7 @@ class GuiDocEditor(QPlainTextEdit):
429
443
  """Replace the text of the current document with the provided
430
444
  text. This also clears undo history.
431
445
  """
432
- qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
446
+ qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
433
447
  self.setPlainText(text)
434
448
  self.updateDocMargins()
435
449
  self.setDocumentChanged(True)
@@ -488,10 +502,7 @@ class GuiDocEditor(QPlainTextEdit):
488
502
  else:
489
503
  self.novelStructureChanged.emit()
490
504
 
491
- # ToDo: This should be a signal
492
505
  if oldHeader != newHeader:
493
- self.mainGui.projView.setTreeItemValues(tHandle)
494
- self.mainGui.itemDetails.updateViewBox(tHandle)
495
506
  self.docFooter.updateInfo()
496
507
 
497
508
  # Update the status bar
@@ -499,6 +510,33 @@ class GuiDocEditor(QPlainTextEdit):
499
510
 
500
511
  return True
501
512
 
513
+ def cursorIsVisible(self) -> bool:
514
+ """Check if the cursor is visible in the editor."""
515
+ return (
516
+ 0 < self.cursorRect().top()
517
+ and self.cursorRect().bottom() < self.viewport().height()
518
+ )
519
+
520
+ def ensureCursorVisibleNoCentre(self) -> None:
521
+ """Ensure cursor is visible, but don't force it to centre."""
522
+ cT = self.cursorRect().top()
523
+ cB = self.cursorRect().bottom()
524
+ vH = self.viewport().height()
525
+ if cT < 0:
526
+ count = 0
527
+ vBar = self.verticalScrollBar()
528
+ while self.cursorRect().top() < 0 and count < 100000:
529
+ vBar.setValue(vBar.value() - 1)
530
+ count += 1
531
+ elif cB > vH:
532
+ count = 0
533
+ vBar = self.verticalScrollBar()
534
+ while self.cursorRect().bottom() > vH and count < 100000:
535
+ vBar.setValue(vBar.value() + 1)
536
+ count += 1
537
+ qApp.processEvents()
538
+ return
539
+
502
540
  def updateDocMargins(self) -> None:
503
541
  """Automatically adjust the margins so the text is centred if
504
542
  we have a text width set or we're in Focus Mode. Otherwise, just
@@ -640,7 +678,7 @@ class GuiDocEditor(QPlainTextEdit):
640
678
  """
641
679
  logger.debug("Running spell checker")
642
680
  start = time()
643
- qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
681
+ qApp.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
644
682
  self._qDocument.syntaxHighlighter.rehighlight()
645
683
  qApp.restoreOverrideCursor()
646
684
  logger.debug("Document highlighted in %.3f ms", 1000*(time() - start))
@@ -690,9 +728,9 @@ class GuiDocEditor(QPlainTextEdit):
690
728
  elif action == nwDocAction.D_QUOTE:
691
729
  self._wrapSelection(self._typDQuoteO, self._typDQuoteC)
692
730
  elif action == nwDocAction.SEL_ALL:
693
- self._makeSelection(QTextCursor.Document)
731
+ self._makeSelection(QTextCursor.SelectionType.Document)
694
732
  elif action == nwDocAction.SEL_PARA:
695
- self._makeSelection(QTextCursor.BlockUnderCursor)
733
+ self._makeSelection(QTextCursor.SelectionType.BlockUnderCursor)
696
734
  elif action == nwDocAction.BLOCK_H1:
697
735
  self._formatBlock(nwDocAction.BLOCK_H1)
698
736
  elif action == nwDocAction.BLOCK_H2:
@@ -796,6 +834,10 @@ class GuiDocEditor(QPlainTextEdit):
796
834
  text = "% Synopsis: "
797
835
  newBlock = True
798
836
  goAfter = True
837
+ elif insert == nwDocInsert.SHORT:
838
+ text = "% Short: "
839
+ newBlock = True
840
+ goAfter = True
799
841
  elif insert == nwDocInsert.NEW_PAGE:
800
842
  text = "[newpage]"
801
843
  newBlock = True
@@ -873,17 +915,17 @@ class GuiDocEditor(QPlainTextEdit):
873
915
  * We also handle automatic scrolling here.
874
916
  """
875
917
  self._lastActive = time()
876
- isReturn = event.key() == Qt.Key_Return
877
- isReturn |= event.key() == Qt.Key_Enter
918
+ isReturn = event.key() == Qt.Key.Key_Return
919
+ isReturn |= event.key() == Qt.Key.Key_Enter
878
920
  if isReturn and self.docSearch.anyFocus():
879
921
  return
880
- elif event == QKeySequence.Redo:
922
+ elif event == QKeySequence.StandardKey.Redo:
881
923
  self.docAction(nwDocAction.REDO)
882
924
  return
883
- elif event == QKeySequence.Undo:
925
+ elif event == QKeySequence.StandardKey.Undo:
884
926
  self.docAction(nwDocAction.UNDO)
885
927
  return
886
- elif event == QKeySequence.SelectAll:
928
+ elif event == QKeySequence.StandardKey.SelectAll:
887
929
  self.docAction(nwDocAction.SEL_ALL)
888
930
  return
889
931
 
@@ -892,7 +934,7 @@ class GuiDocEditor(QPlainTextEdit):
892
934
  super().keyPressEvent(event)
893
935
  nPos = self.cursorRect().topLeft().y()
894
936
  kMod = event.modifiers()
895
- okMod = kMod == Qt.NoModifier or kMod == Qt.ShiftModifier
937
+ okMod = kMod in (Qt.KeyboardModifier.NoModifier, Qt.KeyboardModifier.ShiftModifier)
896
938
  okKey = event.key() not in self.MOVE_KEYS
897
939
  if nPos != cPos and okMod and okKey:
898
940
  mPos = CONFIG.autoScrollPos*0.01 * self.viewport().height()
@@ -923,7 +965,7 @@ class GuiDocEditor(QPlainTextEdit):
923
965
  pressed, check if we're clicking on a tag, and trigger the
924
966
  follow tag function.
925
967
  """
926
- if qApp.keyboardModifiers() == Qt.ControlModifier:
968
+ if qApp.keyboardModifiers() == Qt.KeyboardModifier.ControlModifier:
927
969
  self._processTag(self.cursorForPosition(event.pos()))
928
970
  super().mouseReleaseEvent(event)
929
971
  self.docFooter.updateLineCount()
@@ -996,7 +1038,9 @@ class GuiDocEditor(QPlainTextEdit):
996
1038
  return
997
1039
 
998
1040
  text = block.text()
999
- if text.startswith("@"):
1041
+ if text.startswith("@") and added + removed == 1:
1042
+ # Only run on single keypresses, otherwise it will trigger
1043
+ # at unwanted times when other changes are made to the document
1000
1044
  cursor = self.textCursor()
1001
1045
  bPos = cursor.positionInBlock()
1002
1046
  if bPos > 0:
@@ -1004,8 +1048,10 @@ class GuiDocEditor(QPlainTextEdit):
1004
1048
  point = self.cursorRect().bottomRight()
1005
1049
  self._completer.move(self.viewport().mapToGlobal(point))
1006
1050
  self._completer.setVisible(show)
1051
+ else:
1052
+ self._completer.setVisible(False)
1007
1053
 
1008
- elif self._doReplace and added == 1:
1054
+ if self._doReplace and added == 1:
1009
1055
  self._docAutoReplace(text)
1010
1056
 
1011
1057
  return
@@ -1025,43 +1071,50 @@ class GuiDocEditor(QPlainTextEdit):
1025
1071
 
1026
1072
  @pyqtSlot("QPoint")
1027
1073
  def _openContextMenu(self, pos: QPoint) -> None:
1028
- """Triggered by right click to open the context menu. Also
1029
- triggered by the Ctrl+. shortcut.
1030
- """
1074
+ """Open the editor context menu at a given coordinate."""
1031
1075
  uCursor = self.textCursor()
1032
1076
  pCursor = self.cursorForPosition(pos)
1077
+ pBlock = pCursor.block()
1033
1078
 
1034
1079
  ctxMenu = QMenu(self)
1080
+ ctxMenu.setObjectName("ContextMenu")
1081
+ if pBlock.userState() == GuiDocHighlighter.BLOCK_TITLE:
1082
+ action = ctxMenu.addAction(self.tr("Set as Document Name"))
1083
+ action.triggered.connect(lambda: self._emitRenameItem(pBlock))
1035
1084
 
1036
1085
  # Follow
1037
1086
  status = self._processTag(cursor=pCursor, follow=False)
1038
1087
  if status == nwTrinary.POSITIVE:
1039
- aTag = ctxMenu.addAction(self.tr("Follow Tag"))
1040
- aTag.triggered.connect(lambda: self._processTag(cursor=pCursor, follow=True))
1088
+ action = ctxMenu.addAction(self.tr("Follow Tag"))
1089
+ action.triggered.connect(lambda: self._processTag(cursor=pCursor, follow=True))
1041
1090
  ctxMenu.addSeparator()
1042
1091
  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))
1092
+ action = ctxMenu.addAction(self.tr("Create Note for Tag"))
1093
+ action.triggered.connect(lambda: self._processTag(cursor=pCursor, create=True))
1045
1094
  ctxMenu.addSeparator()
1046
1095
 
1047
1096
  # Cut, Copy and Paste
1048
1097
  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))
1098
+ action = ctxMenu.addAction(self.tr("Cut"))
1099
+ action.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
1100
+ action = ctxMenu.addAction(self.tr("Copy"))
1101
+ action.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
1053
1102
 
1054
- aPaste = ctxMenu.addAction(self.tr("Paste"))
1055
- aPaste.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
1103
+ action = ctxMenu.addAction(self.tr("Paste"))
1104
+ action.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
1056
1105
  ctxMenu.addSeparator()
1057
1106
 
1058
1107
  # 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))
1108
+ action = ctxMenu.addAction(self.tr("Select All"))
1109
+ action.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
1110
+ action = ctxMenu.addAction(self.tr("Select Word"))
1111
+ action.triggered.connect(
1112
+ lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, pos)
1113
+ )
1114
+ action = ctxMenu.addAction(self.tr("Select Paragraph"))
1115
+ action.triggered.connect(lambda: self._makePosSelection(
1116
+ QTextCursor.SelectionType.BlockUnderCursor, pos)
1117
+ )
1065
1118
 
1066
1119
  # Spell Checking
1067
1120
  if SHARED.project.data.spellCheck:
@@ -1071,21 +1124,23 @@ class GuiDocEditor(QPlainTextEdit):
1071
1124
  block = pCursor.block()
1072
1125
  sCursor = self.textCursor()
1073
1126
  sCursor.setPosition(block.position() + cPos)
1074
- sCursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, cLen)
1127
+ sCursor.movePosition(
1128
+ QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor, cLen
1129
+ )
1075
1130
  if suggest:
1076
1131
  ctxMenu.addSeparator()
1077
1132
  ctxMenu.addAction(self.tr("Spelling Suggestion(s)"))
1078
1133
  for option in suggest[:15]:
1079
- aFix = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
1080
- aFix.triggered.connect(
1134
+ action = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
1135
+ action.triggered.connect(
1081
1136
  lambda _, option=option: self._correctWord(sCursor, option)
1082
1137
  )
1083
1138
  else:
1084
1139
  ctxMenu.addAction("%s %s" % (nwUnicode.U_ENDASH, self.tr("No Suggestions")))
1085
1140
 
1086
1141
  ctxMenu.addSeparator()
1087
- aAdd = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
1088
- aAdd.triggered.connect(lambda: self._addWord(word, block))
1142
+ action = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
1143
+ action.triggered.connect(lambda: self._addWord(word, block))
1089
1144
 
1090
1145
  # Execute the context menu
1091
1146
  ctxMenu.exec_(self.viewport().mapToGlobal(pos))
@@ -1276,8 +1331,8 @@ class GuiDocEditor(QPlainTextEdit):
1276
1331
  else:
1277
1332
  resIdx = 0 if doLoop else maxIdx
1278
1333
 
1279
- cursor.setPosition(resS[resIdx], QTextCursor.MoveAnchor)
1280
- cursor.setPosition(resE[resIdx], QTextCursor.KeepAnchor)
1334
+ cursor.setPosition(resS[resIdx], QTextCursor.MoveMode.MoveAnchor)
1335
+ cursor.setPosition(resE[resIdx], QTextCursor.MoveMode.KeepAnchor)
1281
1336
  self.setTextCursor(cursor)
1282
1337
 
1283
1338
  self.docFooter.updateLineCount()
@@ -1303,9 +1358,9 @@ class GuiDocEditor(QPlainTextEdit):
1303
1358
 
1304
1359
  findOpt = QTextDocument.FindFlag(0)
1305
1360
  if self.docSearch.isCaseSense:
1306
- findOpt |= QTextDocument.FindCaseSensitively
1361
+ findOpt |= QTextDocument.FindFlag.FindCaseSensitively
1307
1362
  if self.docSearch.isWholeWord:
1308
- findOpt |= QTextDocument.FindWholeWords
1363
+ findOpt |= QTextDocument.FindFlag.FindWholeWords
1309
1364
 
1310
1365
  searchFor = self.docSearch.getSearchObject()
1311
1366
  cursor.setPosition(0)
@@ -1323,8 +1378,8 @@ class GuiDocEditor(QPlainTextEdit):
1323
1378
  break
1324
1379
 
1325
1380
  if hasSelection:
1326
- cursor.setPosition(origA, QTextCursor.MoveAnchor)
1327
- cursor.setPosition(origB, QTextCursor.KeepAnchor)
1381
+ cursor.setPosition(origA, QTextCursor.MoveMode.MoveAnchor)
1382
+ cursor.setPosition(origB, QTextCursor.MoveMode.KeepAnchor)
1328
1383
  else:
1329
1384
  cursor.setPosition(origA)
1330
1385
 
@@ -1410,22 +1465,33 @@ class GuiDocEditor(QPlainTextEdit):
1410
1465
  If more than one block is selected, the formatting is applied to
1411
1466
  the first block.
1412
1467
  """
1413
- cursor = self._autoSelect()
1414
- if not cursor.hasSelection():
1415
- logger.warning("No selection made, nothing to do")
1416
- return False
1468
+ cursor = self.textCursor()
1469
+ posO = cursor.position()
1470
+ if cursor.hasSelection():
1471
+ select = _SelectAction.KEEP_SELECTION
1472
+ else:
1473
+ cursor = self._autoSelect()
1474
+ if cursor.hasSelection() and posO == cursor.selectionEnd():
1475
+ select = _SelectAction.MOVE_AFTER
1476
+ else:
1477
+ select = _SelectAction.KEEP_POSITION
1417
1478
 
1418
1479
  posS = cursor.selectionStart()
1419
1480
  posE = cursor.selectionEnd()
1481
+ if self._qDocument.characterAt(posO - 1) == fChar:
1482
+ logger.warning("Format repetition, cancelling action")
1483
+ cursor.clearSelection()
1484
+ cursor.setPosition(posO)
1485
+ self.setTextCursor(cursor)
1486
+ return False
1420
1487
 
1421
1488
  blockS = self._qDocument.findBlock(posS)
1422
1489
  blockE = self._qDocument.findBlock(posE)
1423
-
1424
1490
  if blockS != blockE:
1425
1491
  posE = blockS.position() + blockS.length() - 1
1426
1492
  cursor.clearSelection()
1427
- cursor.setPosition(posS, QTextCursor.MoveAnchor)
1428
- cursor.setPosition(posE, QTextCursor.KeepAnchor)
1493
+ cursor.setPosition(posS, QTextCursor.MoveMode.MoveAnchor)
1494
+ cursor.setPosition(posE, QTextCursor.MoveMode.KeepAnchor)
1429
1495
  self.setTextCursor(cursor)
1430
1496
 
1431
1497
  numB = 0
@@ -1443,34 +1509,26 @@ class GuiDocEditor(QPlainTextEdit):
1443
1509
  break
1444
1510
 
1445
1511
  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
1512
+ cursor.clearSelection()
1513
+ cursor.beginEditBlock()
1514
+ cursor.setPosition(posS)
1515
+ for i in range(fLen):
1516
+ cursor.deletePreviousChar()
1517
+ cursor.setPosition(posE)
1518
+ for i in range(fLen):
1519
+ cursor.deletePreviousChar()
1520
+ cursor.endEditBlock()
1521
+ cursor.clearSelection()
1522
+ cursor.setPosition(posO - fLen)
1523
+ self.setTextCursor(cursor)
1457
1524
 
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()
1525
+ else:
1526
+ self._wrapSelection(fChar*fLen, pos=posO, select=select)
1470
1527
 
1471
1528
  return True
1472
1529
 
1473
- def _wrapSelection(self, before: str, after: str | None = None) -> bool:
1530
+ def _wrapSelection(self, before: str, after: str | None = None, pos: int | None = None,
1531
+ select: _SelectAction = _SelectAction.NO_DECISION) -> bool:
1474
1532
  """Wrap the selected text in whatever is in tBefore and tAfter.
1475
1533
  If there is no selection, the autoSelect setting decides the
1476
1534
  action. AutoSelect will select the word under the cursor before
@@ -1479,10 +1537,17 @@ class GuiDocEditor(QPlainTextEdit):
1479
1537
  if after is None:
1480
1538
  after = before
1481
1539
 
1482
- cursor = self._autoSelect()
1483
- if not cursor.hasSelection():
1484
- logger.warning("No selection made, nothing to do")
1485
- return False
1540
+ cursor = self.textCursor()
1541
+ posO = pos if isinstance(pos, int) else cursor.position()
1542
+ if select == _SelectAction.NO_DECISION:
1543
+ if cursor.hasSelection():
1544
+ select = _SelectAction.KEEP_SELECTION
1545
+ else:
1546
+ cursor = self._autoSelect()
1547
+ if cursor.hasSelection() and posO == cursor.selectionEnd():
1548
+ select = _SelectAction.MOVE_AFTER
1549
+ else:
1550
+ select = _SelectAction.KEEP_POSITION
1486
1551
 
1487
1552
  posS = cursor.selectionStart()
1488
1553
  posE = cursor.selectionEnd()
@@ -1500,8 +1565,14 @@ class GuiDocEditor(QPlainTextEdit):
1500
1565
  cursor.insertText(before)
1501
1566
  cursor.endEditBlock()
1502
1567
 
1503
- cursor.setPosition(posE + len(before), QTextCursor.MoveAnchor)
1504
- cursor.setPosition(posS + len(before), QTextCursor.KeepAnchor)
1568
+ if select == _SelectAction.MOVE_AFTER:
1569
+ cursor.setPosition(posE + len(before + after))
1570
+ elif select == _SelectAction.KEEP_SELECTION:
1571
+ cursor.setPosition(posE + len(before), QTextCursor.MoveMode.MoveAnchor)
1572
+ cursor.setPosition(posS + len(before), QTextCursor.MoveMode.KeepAnchor)
1573
+ elif select == _SelectAction.KEEP_POSITION:
1574
+ cursor.setPosition(posO + len(before))
1575
+
1505
1576
  self.setTextCursor(cursor)
1506
1577
 
1507
1578
  return True
@@ -1522,7 +1593,9 @@ class GuiDocEditor(QPlainTextEdit):
1522
1593
  self._allowAutoReplace(False)
1523
1594
  for posC in range(posS, posE+1):
1524
1595
  cursor.setPosition(posC)
1525
- cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2)
1596
+ cursor.movePosition(
1597
+ QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 2
1598
+ )
1526
1599
  selText = cursor.selectedText()
1527
1600
 
1528
1601
  nS = len(selText)
@@ -1542,12 +1615,16 @@ class GuiDocEditor(QPlainTextEdit):
1542
1615
  cursor.setPosition(posC)
1543
1616
  if pC in closeCheck:
1544
1617
  cursor.beginEditBlock()
1545
- cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
1618
+ cursor.movePosition(
1619
+ QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 1
1620
+ )
1546
1621
  cursor.insertText(oQuote)
1547
1622
  cursor.endEditBlock()
1548
1623
  else:
1549
1624
  cursor.beginEditBlock()
1550
- cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
1625
+ cursor.movePosition(
1626
+ QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, 1
1627
+ )
1551
1628
  cursor.insertText(cQuote)
1552
1629
  cursor.endEditBlock()
1553
1630
 
@@ -1667,7 +1744,7 @@ class GuiDocEditor(QPlainTextEdit):
1667
1744
  # Replace the block text
1668
1745
  cursor.beginEditBlock()
1669
1746
  posO = cursor.position()
1670
- cursor.select(QTextCursor.BlockUnderCursor)
1747
+ cursor.select(QTextCursor.SelectionType.BlockUnderCursor)
1671
1748
  posS = cursor.selectionStart()
1672
1749
  cursor.removeSelectedText()
1673
1750
  cursor.setPosition(posS)
@@ -1727,7 +1804,9 @@ class GuiDocEditor(QPlainTextEdit):
1727
1804
  cursor.beginEditBlock()
1728
1805
  cursor.clearSelection()
1729
1806
  cursor.setPosition(rS)
1730
- cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, rE-rS)
1807
+ cursor.movePosition(
1808
+ QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor, rE-rS
1809
+ )
1731
1810
  cursor.insertText(cleanText.rstrip() + "\n")
1732
1811
  cursor.endEditBlock()
1733
1812
 
@@ -1751,13 +1830,13 @@ class GuiDocEditor(QPlainTextEdit):
1751
1830
  block = cursor.block()
1752
1831
  text = block.text()
1753
1832
  if len(text) == 0:
1754
- return nwTrinary.UNKNOWN
1833
+ return nwTrinary.NEUTRAL
1755
1834
 
1756
1835
  if text.startswith("@") and isinstance(self._nwItem, NWItem):
1757
1836
 
1758
1837
  isGood, tBits, tPos = SHARED.project.index.scanThis(text)
1759
1838
  if not isGood:
1760
- return nwTrinary.UNKNOWN
1839
+ return nwTrinary.NEUTRAL
1761
1840
 
1762
1841
  tag = ""
1763
1842
  exist = False
@@ -1774,7 +1853,7 @@ class GuiDocEditor(QPlainTextEdit):
1774
1853
 
1775
1854
  if not tag or tag.startswith("@"):
1776
1855
  # The keyword cannot be looked up, so we ignore that
1777
- return nwTrinary.UNKNOWN
1856
+ return nwTrinary.NEUTRAL
1778
1857
 
1779
1858
  if follow and exist:
1780
1859
  logger.debug("Attempting to follow tag '%s'", tag)
@@ -1794,7 +1873,14 @@ class GuiDocEditor(QPlainTextEdit):
1794
1873
 
1795
1874
  return nwTrinary.POSITIVE if exist else nwTrinary.NEGATIVE
1796
1875
 
1797
- return nwTrinary.UNKNOWN
1876
+ return nwTrinary.NEUTRAL
1877
+
1878
+ def _emitRenameItem(self, block: QTextBlock) -> None:
1879
+ """Emit a signal to request an item be renamed."""
1880
+ if self._docHandle:
1881
+ text = block.text().lstrip("#").lstrip("!").strip()
1882
+ self.requestProjectItemRenamed.emit(self._docHandle, text)
1883
+ return
1798
1884
 
1799
1885
  def _openContextFromCursor(self) -> None:
1800
1886
  """Open the spell check context menu at the cursor."""
@@ -1887,7 +1973,9 @@ class GuiDocEditor(QPlainTextEdit):
1887
1973
  tInsert = tInsert + self._typPadChar
1888
1974
 
1889
1975
  if nDelete > 0:
1890
- cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, nDelete)
1976
+ cursor.movePosition(
1977
+ QTextCursor.MoveOperation.Left, QTextCursor.MoveMode.KeepAnchor, nDelete
1978
+ )
1891
1979
  cursor.insertText(tInsert)
1892
1980
 
1893
1981
  return
@@ -1908,27 +1996,38 @@ class GuiDocEditor(QPlainTextEdit):
1908
1996
 
1909
1997
  def _autoSelect(self) -> QTextCursor:
1910
1998
  """Return a cursor which may or may not have a selection based
1911
- on user settings and document action.
1999
+ on user settings and document action. The selection will be the
2000
+ word closest to the cursor consisting of alphanumerical unicode
2001
+ characters.
1912
2002
  """
1913
2003
  cursor = self.textCursor()
1914
2004
  if CONFIG.autoSelect and not cursor.hasSelection():
1915
- cursor.select(QTextCursor.WordUnderCursor)
1916
- posS = cursor.selectionStart()
1917
- posE = cursor.selectionEnd()
2005
+ cPos = cursor.position()
2006
+ bPos = cursor.block().position()
2007
+ bLen = cursor.block().length()
2008
+
2009
+ # Scan backwards
2010
+ sPos = cPos
2011
+ for i in range(cPos - bPos):
2012
+ sPos = cPos - i - 1
2013
+ if not self._qDocument.characterAt(sPos).isalnum():
2014
+ sPos += 1
2015
+ break
2016
+
2017
+ # Scan forwards
2018
+ ePos = cPos
2019
+ for i in range(bPos + bLen - cPos):
2020
+ ePos = cPos + i
2021
+ if not self._qDocument.characterAt(ePos).isalnum():
2022
+ break
2023
+
2024
+ if ePos - sPos <= 0:
2025
+ # No selection possible
2026
+ return cursor
1918
2027
 
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)
2028
+ cursor.clearSelection()
2029
+ cursor.setPosition(sPos, QTextCursor.MoveMode.MoveAnchor)
2030
+ cursor.setPosition(ePos, QTextCursor.MoveMode.KeepAnchor)
1932
2031
 
1933
2032
  self.setTextCursor(cursor)
1934
2033
 
@@ -1940,18 +2039,18 @@ class GuiDocEditor(QPlainTextEdit):
1940
2039
  cursor.clearSelection()
1941
2040
  cursor.select(mode)
1942
2041
 
1943
- if mode == QTextCursor.WordUnderCursor:
2042
+ if mode == QTextCursor.SelectionType.WordUnderCursor:
1944
2043
  cursor = self._autoSelect()
1945
2044
 
1946
- elif mode == QTextCursor.BlockUnderCursor:
2045
+ elif mode == QTextCursor.SelectionType.BlockUnderCursor:
1947
2046
  # This selection mode also selects the preceding paragraph
1948
2047
  # separator, which we want to avoid.
1949
2048
  posS = cursor.selectionStart()
1950
2049
  posE = cursor.selectionEnd()
1951
2050
  selTxt = cursor.selectedText()
1952
2051
  if selTxt.startswith(nwUnicode.U_PSEP):
1953
- cursor.setPosition(posS+1, QTextCursor.MoveAnchor)
1954
- cursor.setPosition(posE, QTextCursor.KeepAnchor)
2052
+ cursor.setPosition(posS+1, QTextCursor.MoveMode.MoveAnchor)
2053
+ cursor.setPosition(posE, QTextCursor.MoveMode.KeepAnchor)
1955
2054
 
1956
2055
  self.setTextCursor(cursor)
1957
2056
 
@@ -2011,7 +2110,7 @@ class MetaCompleter(QMenu):
2011
2110
  length = len(lookup)
2012
2111
  suffix = ""
2013
2112
  options = list(filter(
2014
- lambda x: lookup in x.lower(), SHARED.project.index.getTags(
2113
+ lambda x: lookup in x.lower(), SHARED.project.index.getClassTags(
2015
2114
  nwKeyWords.KEY_CLASS.get(kw.strip(), nwItemClass.NO_CLASS)
2016
2115
  )
2017
2116
  ))[:15]
@@ -2033,7 +2132,10 @@ class MetaCompleter(QMenu):
2033
2132
  def keyPressEvent(self, event: QKeyEvent) -> None:
2034
2133
  """Capture keypresses and forward most of them to the editor."""
2035
2134
  parent = self.parent()
2036
- if event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_Return, Qt.Key_Enter, Qt.Key_Escape):
2135
+ if event.key() in (
2136
+ Qt.Key.Key_Up, Qt.Key.Key_Down, Qt.Key.Key_Return,
2137
+ Qt.Key.Key_Enter, Qt.Key.Key_Escape
2138
+ ):
2037
2139
  super().keyPressEvent(event)
2038
2140
  elif isinstance(parent, GuiDocEditor):
2039
2141
  parent.keyPressEvent(event)
@@ -2187,9 +2289,9 @@ class GuiDocToolBar(QWidget):
2187
2289
  def updateTheme(self) -> None:
2188
2290
  """Initialise GUI elements that depend on specific settings."""
2189
2291
  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))
2292
+ palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
2293
+ palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
2294
+ palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
2193
2295
  self.setPalette(palette)
2194
2296
 
2195
2297
  tPx = int(0.8*SHARED.theme.fontPixelSize)
@@ -2269,7 +2371,7 @@ class GuiDocEditSearch(QFrame):
2269
2371
 
2270
2372
  self.setContentsMargins(0, 0, 0, 0)
2271
2373
  self.setAutoFillBackground(True)
2272
- self.setFrameStyle(QFrame.StyledPanel | QFrame.Plain)
2374
+ self.setFrameStyle(QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
2273
2375
 
2274
2376
  self.mainBox = QGridLayout(self)
2275
2377
  self.setLayout(self.mainBox)
@@ -2288,7 +2390,7 @@ class GuiDocEditSearch(QFrame):
2288
2390
  self.replaceBox.returnPressed.connect(self._doReplace)
2289
2391
 
2290
2392
  self.searchOpt = QToolBar(self)
2291
- self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
2393
+ self.searchOpt.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2292
2394
  self.searchOpt.setIconSize(QSize(tPx, tPx))
2293
2395
  self.searchOpt.setContentsMargins(0, 0, 0, 0)
2294
2396
 
@@ -2350,7 +2452,7 @@ class GuiDocEditSearch(QFrame):
2350
2452
  bPx = self.searchBox.sizeHint().height()
2351
2453
 
2352
2454
  self.showReplace = QToolButton(self)
2353
- self.showReplace.setArrowType(Qt.RightArrow)
2455
+ self.showReplace.setArrowType(Qt.ArrowType.RightArrow)
2354
2456
  self.showReplace.setCheckable(True)
2355
2457
  self.showReplace.toggled.connect(self._doToggleReplace)
2356
2458
 
@@ -2364,8 +2466,8 @@ class GuiDocEditSearch(QFrame):
2364
2466
  self.replaceButton.setToolTip(self.tr("Find and replace in current document"))
2365
2467
  self.replaceButton.clicked.connect(self._doReplace)
2366
2468
 
2367
- self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignLeft)
2368
- self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 3, Qt.AlignRight)
2469
+ self.mainBox.addWidget(self.searchLabel, 0, 0, 1, 2, Qt.AlignmentFlag.AlignLeft)
2470
+ self.mainBox.addWidget(self.searchOpt, 0, 2, 1, 3, Qt.AlignmentFlag.AlignRight)
2369
2471
  self.mainBox.addWidget(self.showReplace, 1, 0, 1, 1)
2370
2472
  self.mainBox.addWidget(self.searchBox, 1, 1, 1, 2)
2371
2473
  self.mainBox.addWidget(self.searchButton, 1, 3, 1, 1)
@@ -2423,18 +2525,18 @@ class GuiDocEditSearch(QFrame):
2423
2525
  # only added in Qt 5.13. Otherwise, 5.3 and up supports
2424
2526
  # only the QRegExp class.
2425
2527
  if CONFIG.verQtValue >= 0x050d00:
2426
- rxOpt = QRegularExpression.UseUnicodePropertiesOption
2528
+ rxOpt = QRegularExpression.PatternOption.UseUnicodePropertiesOption
2427
2529
  if not self.isCaseSense:
2428
- rxOpt |= QRegularExpression.CaseInsensitiveOption
2530
+ rxOpt |= QRegularExpression.PatternOption.CaseInsensitiveOption
2429
2531
  regEx = QRegularExpression(text, rxOpt)
2430
2532
  self._alertSearchValid(regEx.isValid())
2431
2533
  return regEx
2432
2534
  else: # pragma: no cover
2433
2535
  # >= 50300 to < 51300
2434
2536
  if self.isCaseSense:
2435
- rxOpt = Qt.CaseSensitive
2537
+ rxOpt = Qt.CaseSensitivity.CaseSensitive
2436
2538
  else:
2437
- rxOpt = Qt.CaseInsensitive
2539
+ rxOpt = Qt.CaseSensitivity.CaseInsensitive
2438
2540
  regEx = QRegExp(text, rxOpt)
2439
2541
  self._alertSearchValid(regEx.isValid())
2440
2542
  return regEx
@@ -2570,7 +2672,7 @@ class GuiDocEditSearch(QFrame):
2570
2672
  def _doSearch(self) -> None:
2571
2673
  """Call the search action function for the document editor."""
2572
2674
  modKey = qApp.keyboardModifiers()
2573
- if modKey == Qt.ShiftModifier:
2675
+ if modKey == Qt.KeyboardModifier.ShiftModifier:
2574
2676
  self.docEditor.findNext(goBack=True)
2575
2677
  else:
2576
2678
  self.docEditor.findNext()
@@ -2586,9 +2688,9 @@ class GuiDocEditSearch(QFrame):
2586
2688
  def _doToggleReplace(self, state: bool) -> None:
2587
2689
  """Toggle the show/hide of the replace box."""
2588
2690
  if state:
2589
- self.showReplace.setArrowType(Qt.DownArrow)
2691
+ self.showReplace.setArrowType(Qt.ArrowType.DownArrow)
2590
2692
  else:
2591
- self.showReplace.setArrowType(Qt.RightArrow)
2693
+ self.showReplace.setArrowType(Qt.ArrowType.RightArrow)
2592
2694
  self.replaceBox.setVisible(state)
2593
2695
  self.replaceButton.setVisible(state)
2594
2696
  self.repVisible = state
@@ -2641,7 +2743,7 @@ class GuiDocEditSearch(QFrame):
2641
2743
  isn't valid. Take the colour from the replace box.
2642
2744
  """
2643
2745
  qPalette = self.replaceBox.palette()
2644
- qPalette.setColor(QPalette.Base, self.rxCol[isValid])
2746
+ qPalette.setColor(QPalette.ColorRole.Base, self.rxCol[isValid])
2645
2747
  self.searchBox.setPalette(qPalette)
2646
2748
  return
2647
2749
 
@@ -2682,7 +2784,7 @@ class GuiDocEditHeader(QWidget):
2682
2784
  self.itemTitle.setMargin(0)
2683
2785
  self.itemTitle.setContentsMargins(0, 0, 0, 0)
2684
2786
  self.itemTitle.setAutoFillBackground(True)
2685
- self.itemTitle.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
2787
+ self.itemTitle.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
2686
2788
  self.itemTitle.setFixedHeight(fPx)
2687
2789
 
2688
2790
  lblFont = self.itemTitle.font()
@@ -2694,7 +2796,7 @@ class GuiDocEditHeader(QWidget):
2694
2796
  self.tbButton.setContentsMargins(0, 0, 0, 0)
2695
2797
  self.tbButton.setIconSize(iconSize)
2696
2798
  self.tbButton.setFixedSize(fPx, fPx)
2697
- self.tbButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
2799
+ self.tbButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2698
2800
  self.tbButton.setVisible(False)
2699
2801
  self.tbButton.setToolTip(self.tr("Toggle Tool Bar"))
2700
2802
  self.tbButton.clicked.connect(lambda: self.toggleToolBarRequest.emit())
@@ -2703,7 +2805,7 @@ class GuiDocEditHeader(QWidget):
2703
2805
  self.searchButton.setContentsMargins(0, 0, 0, 0)
2704
2806
  self.searchButton.setIconSize(iconSize)
2705
2807
  self.searchButton.setFixedSize(fPx, fPx)
2706
- self.searchButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
2808
+ self.searchButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2707
2809
  self.searchButton.setVisible(False)
2708
2810
  self.searchButton.setToolTip(self.tr("Search"))
2709
2811
  self.searchButton.clicked.connect(self.docEditor.toggleSearch)
@@ -2712,7 +2814,7 @@ class GuiDocEditHeader(QWidget):
2712
2814
  self.minmaxButton.setContentsMargins(0, 0, 0, 0)
2713
2815
  self.minmaxButton.setIconSize(iconSize)
2714
2816
  self.minmaxButton.setFixedSize(fPx, fPx)
2715
- self.minmaxButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
2817
+ self.minmaxButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2716
2818
  self.minmaxButton.setVisible(False)
2717
2819
  self.minmaxButton.setToolTip(self.tr("Toggle Focus Mode"))
2718
2820
  self.minmaxButton.clicked.connect(lambda: self.docEditor.toggleFocusModeRequest.emit())
@@ -2721,7 +2823,7 @@ class GuiDocEditHeader(QWidget):
2721
2823
  self.closeButton.setContentsMargins(0, 0, 0, 0)
2722
2824
  self.closeButton.setIconSize(iconSize)
2723
2825
  self.closeButton.setFixedSize(fPx, fPx)
2724
- self.closeButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
2826
+ self.closeButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
2725
2827
  self.closeButton.setVisible(False)
2726
2828
  self.closeButton.setToolTip(self.tr("Close"))
2727
2829
  self.closeButton.clicked.connect(self._closeDocument)
@@ -2779,9 +2881,9 @@ class GuiDocEditHeader(QWidget):
2779
2881
  theme rather than the main GUI.
2780
2882
  """
2781
2883
  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))
2884
+ palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
2885
+ palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
2886
+ palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
2785
2887
 
2786
2888
  self.setPalette(palette)
2787
2889
  self.itemTitle.setPalette(palette)
@@ -2857,7 +2959,8 @@ class GuiDocEditHeader(QWidget):
2857
2959
  """Capture a click on the title and ensure that the item is
2858
2960
  selected in the project tree.
2859
2961
  """
2860
- self.mainGui.projView.setSelectedHandle(self._docHandle, doScroll=True)
2962
+ if event.button() == Qt.MouseButton.LeftButton:
2963
+ self.docEditor.requestProjectItemSelected.emit(self._docHandle, True)
2861
2964
  return
2862
2965
 
2863
2966
  # END Class GuiDocEditHeader
@@ -2894,11 +2997,13 @@ class GuiDocEditFooter(QWidget):
2894
2997
  self.setContentsMargins(0, 0, 0, 0)
2895
2998
  self.setAutoFillBackground(True)
2896
2999
 
3000
+ alLeftTop = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
3001
+
2897
3002
  # Status
2898
3003
  self.statusIcon = QLabel("")
2899
3004
  self.statusIcon.setContentsMargins(0, 0, 0, 0)
2900
3005
  self.statusIcon.setFixedHeight(self.sPx)
2901
- self.statusIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3006
+ self.statusIcon.setAlignment(alLeftTop)
2902
3007
 
2903
3008
  self.statusText = QLabel(self.tr("Status"))
2904
3009
  self.statusText.setIndent(0)
@@ -2906,14 +3011,14 @@ class GuiDocEditFooter(QWidget):
2906
3011
  self.statusText.setContentsMargins(0, 0, 0, 0)
2907
3012
  self.statusText.setAutoFillBackground(True)
2908
3013
  self.statusText.setFixedHeight(fPx)
2909
- self.statusText.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3014
+ self.statusText.setAlignment(alLeftTop)
2910
3015
  self.statusText.setFont(lblFont)
2911
3016
 
2912
3017
  # Lines
2913
3018
  self.linesIcon = QLabel("")
2914
3019
  self.linesIcon.setContentsMargins(0, 0, 0, 0)
2915
3020
  self.linesIcon.setFixedHeight(self.sPx)
2916
- self.linesIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3021
+ self.linesIcon.setAlignment(alLeftTop)
2917
3022
 
2918
3023
  self.linesText = QLabel("")
2919
3024
  self.linesText.setIndent(0)
@@ -2921,14 +3026,14 @@ class GuiDocEditFooter(QWidget):
2921
3026
  self.linesText.setContentsMargins(0, 0, 0, 0)
2922
3027
  self.linesText.setAutoFillBackground(True)
2923
3028
  self.linesText.setFixedHeight(fPx)
2924
- self.linesText.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3029
+ self.linesText.setAlignment(alLeftTop)
2925
3030
  self.linesText.setFont(lblFont)
2926
3031
 
2927
3032
  # Words
2928
3033
  self.wordsIcon = QLabel("")
2929
3034
  self.wordsIcon.setContentsMargins(0, 0, 0, 0)
2930
3035
  self.wordsIcon.setFixedHeight(self.sPx)
2931
- self.wordsIcon.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3036
+ self.wordsIcon.setAlignment(alLeftTop)
2932
3037
 
2933
3038
  self.wordsText = QLabel("")
2934
3039
  self.wordsText.setIndent(0)
@@ -2936,7 +3041,7 @@ class GuiDocEditFooter(QWidget):
2936
3041
  self.wordsText.setContentsMargins(0, 0, 0, 0)
2937
3042
  self.wordsText.setAutoFillBackground(True)
2938
3043
  self.wordsText.setFixedHeight(fPx)
2939
- self.wordsText.setAlignment(Qt.AlignLeft | Qt.AlignTop)
3044
+ self.wordsText.setAlignment(alLeftTop)
2940
3045
  self.wordsText.setFont(lblFont)
2941
3046
 
2942
3047
  # Assemble Layout
@@ -2984,9 +3089,9 @@ class GuiDocEditFooter(QWidget):
2984
3089
  theme rather than the main GUI.
2985
3090
  """
2986
3091
  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))
3092
+ palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
3093
+ palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
3094
+ palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
2990
3095
 
2991
3096
  self.setPalette(palette)
2992
3097
  self.statusText.setPalette(palette)