novelWriter 2.3rc1__py3-none-any.whl → 2.4__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 (125) hide show
  1. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/METADATA +5 -6
  2. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/RECORD +119 -109
  3. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/WHEEL +1 -1
  4. novelWriter-2.4.dist-info/entry_points.txt +2 -0
  5. novelwriter/__init__.py +17 -10
  6. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  7. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  8. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  9. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  10. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  11. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  12. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  13. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  14. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  15. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  16. novelwriter/assets/i18n/project_nl_NL.json +11 -0
  17. novelwriter/assets/i18n/project_pt_BR.json +11 -0
  18. novelwriter/assets/icons/none.svg +4 -0
  19. novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
  20. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
  21. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
  22. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
  23. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
  24. novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
  25. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +4 -0
  26. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +4 -0
  27. novelwriter/assets/icons/typicons_light/icons.conf +4 -0
  28. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
  29. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
  30. novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
  31. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
  32. novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
  33. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +4 -0
  34. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +4 -0
  35. novelwriter/assets/manual.pdf +0 -0
  36. novelwriter/assets/sample.zip +0 -0
  37. novelwriter/assets/syntax/cyberpunk_night.conf +26 -0
  38. novelwriter/assets/syntax/default_dark.conf +1 -0
  39. novelwriter/assets/syntax/default_light.conf +1 -0
  40. novelwriter/assets/syntax/grey_dark.conf +1 -0
  41. novelwriter/assets/syntax/grey_light.conf +1 -0
  42. novelwriter/assets/syntax/light_owl.conf +1 -0
  43. novelwriter/assets/syntax/night_owl.conf +1 -0
  44. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  45. novelwriter/assets/syntax/solarized_light.conf +1 -0
  46. novelwriter/assets/syntax/tango.conf +23 -0
  47. novelwriter/assets/syntax/tomorrow.conf +1 -0
  48. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  49. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  50. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  51. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  52. novelwriter/assets/text/credits_en.htm +25 -23
  53. novelwriter/assets/themes/cyberpunk_night.conf +29 -0
  54. novelwriter/common.py +12 -4
  55. novelwriter/config.py +47 -16
  56. novelwriter/constants.py +5 -6
  57. novelwriter/core/buildsettings.py +64 -44
  58. novelwriter/core/coretools.py +97 -13
  59. novelwriter/core/docbuild.py +74 -7
  60. novelwriter/core/document.py +24 -3
  61. novelwriter/core/index.py +31 -112
  62. novelwriter/core/project.py +11 -15
  63. novelwriter/core/projectxml.py +3 -2
  64. novelwriter/core/sessions.py +2 -2
  65. novelwriter/core/spellcheck.py +3 -3
  66. novelwriter/core/status.py +6 -5
  67. novelwriter/core/storage.py +16 -6
  68. novelwriter/core/tohtml.py +22 -25
  69. novelwriter/core/tokenizer.py +417 -237
  70. novelwriter/core/tomd.py +17 -8
  71. novelwriter/core/toodt.py +386 -351
  72. novelwriter/core/tree.py +8 -8
  73. novelwriter/dialogs/about.py +10 -12
  74. novelwriter/dialogs/docmerge.py +17 -14
  75. novelwriter/dialogs/docsplit.py +20 -19
  76. novelwriter/dialogs/editlabel.py +5 -4
  77. novelwriter/dialogs/preferences.py +32 -40
  78. novelwriter/dialogs/projectsettings.py +31 -28
  79. novelwriter/dialogs/quotes.py +10 -9
  80. novelwriter/dialogs/wordlist.py +18 -15
  81. novelwriter/enum.py +17 -14
  82. novelwriter/error.py +14 -12
  83. novelwriter/extensions/circularprogress.py +12 -8
  84. novelwriter/extensions/configlayout.py +23 -3
  85. novelwriter/extensions/modified.py +51 -2
  86. novelwriter/extensions/pagedsidebar.py +16 -14
  87. novelwriter/extensions/simpleprogress.py +3 -1
  88. novelwriter/extensions/statusled.py +3 -1
  89. novelwriter/extensions/switch.py +10 -9
  90. novelwriter/extensions/switchbox.py +14 -13
  91. novelwriter/extensions/versioninfo.py +1 -1
  92. novelwriter/gui/doceditor.py +433 -496
  93. novelwriter/gui/dochighlight.py +54 -33
  94. novelwriter/gui/docviewer.py +162 -175
  95. novelwriter/gui/docviewerpanel.py +20 -37
  96. novelwriter/gui/editordocument.py +15 -4
  97. novelwriter/gui/itemdetails.py +51 -54
  98. novelwriter/gui/mainmenu.py +37 -17
  99. novelwriter/gui/noveltree.py +31 -37
  100. novelwriter/gui/outline.py +120 -98
  101. novelwriter/gui/projtree.py +114 -112
  102. novelwriter/gui/search.py +362 -0
  103. novelwriter/gui/sidebar.py +36 -45
  104. novelwriter/gui/statusbar.py +14 -14
  105. novelwriter/gui/theme.py +116 -34
  106. novelwriter/guimain.py +216 -207
  107. novelwriter/shared.py +31 -6
  108. novelwriter/text/counting.py +138 -0
  109. novelwriter/tools/dictionaries.py +15 -14
  110. novelwriter/tools/lipsum.py +20 -17
  111. novelwriter/tools/manusbuild.py +43 -35
  112. novelwriter/tools/manuscript.py +381 -104
  113. novelwriter/tools/manussettings.py +263 -125
  114. novelwriter/tools/noveldetails.py +21 -19
  115. novelwriter/tools/welcome.py +59 -57
  116. novelwriter/tools/writingstats.py +61 -55
  117. novelwriter/types.py +90 -0
  118. novelWriter-2.3rc1.dist-info/entry_points.txt +0 -5
  119. novelwriter/core/__init__.py +0 -3
  120. novelwriter/dialogs/__init__.py +0 -3
  121. novelwriter/extensions/__init__.py +0 -3
  122. novelwriter/gui/__init__.py +0 -3
  123. novelwriter/tools/__init__.py +0 -3
  124. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/LICENSE.md +0 -0
  125. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/top_level.txt +0 -0
novelwriter/core/tree.py CHANGED
@@ -28,7 +28,7 @@ import logging
28
28
 
29
29
  from typing import TYPE_CHECKING, Literal, overload
30
30
  from pathlib import Path
31
- from collections.abc import Iterator
31
+ from collections.abc import Iterable, Iterator
32
32
 
33
33
  from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
34
34
  from novelwriter.error import logException
@@ -351,16 +351,16 @@ class NWTree:
351
351
  return False
352
352
  return tItem.itemType == itemType
353
353
 
354
- def getItemPath(self, tHandle: str) -> list[str]:
354
+ def getItemPath(self, tHandle: str, asName: bool = False) -> list[str]:
355
355
  """Iterate upwards in the tree until we find the item with
356
- parent None, the root item, and return the list of handles.
357
- We do this with a for loop with a maximum depth to make
358
- infinite loops impossible.
356
+ parent None, the root item, and return the list of handles, or
357
+ alternatively item names. We do this with a for loop with a
358
+ maximum depth to make infinite loops impossible.
359
359
  """
360
360
  tTree = []
361
361
  tItem = self.__getitem__(tHandle)
362
362
  if tItem is not None:
363
- tTree.append(tHandle)
363
+ tTree.append(tItem.itemName if asName else tHandle)
364
364
  for _ in range(MAX_DEPTH):
365
365
  if tItem.itemParent is None:
366
366
  return tTree
@@ -370,7 +370,7 @@ class NWTree:
370
370
  if tItem is None:
371
371
  return tTree
372
372
  else:
373
- tTree.append(tHandle)
373
+ tTree.append(tItem.itemName if asName else tHandle)
374
374
  else:
375
375
  raise RecursionError("Critical internal error")
376
376
 
@@ -387,7 +387,7 @@ class NWTree:
387
387
  rootClasses.add(nwItem.itemClass)
388
388
  return rootClasses
389
389
 
390
- def iterRoots(self, itemClass: nwItemClass | None) -> Iterator[tuple[str, NWItem]]:
390
+ def iterRoots(self, itemClass: nwItemClass | None) -> Iterable[tuple[str, NWItem]]:
391
391
  """Iterate over all root items of a given class in order."""
392
392
  for tHandle in self._order:
393
393
  nwItem = self.__getitem__(tHandle)
@@ -26,16 +26,16 @@ from __future__ import annotations
26
26
  import logging
27
27
 
28
28
  from PyQt5.QtGui import QCloseEvent, QColor
29
- from PyQt5.QtCore import Qt
30
29
  from PyQt5.QtWidgets import (
31
30
  QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QTextBrowser, QVBoxLayout,
32
31
  QWidget
33
32
  )
34
33
 
35
34
  from novelwriter import CONFIG, SHARED
36
- from novelwriter.common import readTextFile
35
+ from novelwriter.common import cssCol, readTextFile
37
36
  from novelwriter.extensions.configlayout import NColourLabel
38
37
  from novelwriter.extensions.versioninfo import VersionInfoWidget
38
+ from novelwriter.types import QtAlignRightTop, QtDialogClose
39
39
 
40
40
  logger = logging.getLogger(__name__)
41
41
 
@@ -68,9 +68,9 @@ class GuiAbout(QDialog):
68
68
 
69
69
  self.nwInfo = VersionInfoWidget(self)
70
70
 
71
- self.nwLicence = QLabel(self.tr("This application is licenced under {0}".format(
71
+ self.nwLicence = QLabel(self.tr("This application is licenced under {0}").format(
72
72
  "<a href='https://www.gnu.org/licenses/gpl-3.0.html'>GPL v3.0</a>"
73
- )))
73
+ ), self)
74
74
  self.nwLicence.setOpenExternalLinks(True)
75
75
 
76
76
  # Credits
@@ -84,7 +84,7 @@ class GuiAbout(QDialog):
84
84
  self.txtCredits.setViewportMargins(0, hA, hA, 0)
85
85
 
86
86
  # Buttons
87
- self.btnBox = QDialogButtonBox(QDialogButtonBox.Close, self)
87
+ self.btnBox = QDialogButtonBox(QtDialogClose, self)
88
88
  self.btnBox.rejected.connect(self.close)
89
89
 
90
90
  # Assemble
@@ -100,10 +100,8 @@ class GuiAbout(QDialog):
100
100
  self.innerBox.addSpacing(hB)
101
101
  self.innerBox.addWidget(self.btnBox)
102
102
 
103
- topRight = Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignRight
104
-
105
103
  self.outerBox = QHBoxLayout()
106
- self.outerBox.addWidget(self.nwLogo, 0, topRight)
104
+ self.outerBox.addWidget(self.nwLogo, 0, QtAlignRightTop)
107
105
  self.outerBox.addLayout(self.innerBox, 1)
108
106
 
109
107
  self.setLayout(self.outerBox)
@@ -149,10 +147,10 @@ class GuiAbout(QDialog):
149
147
 
150
148
  def _setStyleSheet(self) -> None:
151
149
  """Set stylesheet for all browser tabs."""
152
- baseCol = self.palette().window().color()
153
- self.txtCredits.setStyleSheet((
154
- "QTextBrowser {{border: none; background: rgb({r},{g},{b});}} "
155
- ).format(r=baseCol.red(), g=baseCol.green(), b=baseCol.blue()))
150
+ baseCol = cssCol(self.palette().window().color())
151
+ self.txtCredits.setStyleSheet(
152
+ f"QTextBrowser {{border: none; background: {baseCol};}} "
153
+ )
156
154
  return
157
155
 
158
156
  # END Class GuiAbout
@@ -26,8 +26,8 @@ from __future__ import annotations
26
26
 
27
27
  import logging
28
28
 
29
+ from PyQt5.QtCore import Qt, pyqtSlot
29
30
  from PyQt5.QtGui import QCloseEvent
30
- from PyQt5.QtCore import Qt, QSize, pyqtSlot
31
31
  from PyQt5.QtWidgets import (
32
32
  QAbstractItemView, QDialog, QDialogButtonBox, QGridLayout, QLabel,
33
33
  QListWidget, QListWidgetItem, QVBoxLayout, QWidget
@@ -36,13 +36,14 @@ from PyQt5.QtWidgets import (
36
36
  from novelwriter import CONFIG, SHARED
37
37
  from novelwriter.extensions.switch import NSwitch
38
38
  from novelwriter.extensions.configlayout import NColourLabel
39
+ from novelwriter.types import QtDialogCancel, QtDialogOk, QtDialogReset, QtUserRole
39
40
 
40
41
  logger = logging.getLogger(__name__)
41
42
 
42
43
 
43
44
  class GuiDocMerge(QDialog):
44
45
 
45
- D_HANDLE = Qt.ItemDataRole.UserRole
46
+ D_HANDLE = QtUserRole
46
47
 
47
48
  def __init__(self, parent: QWidget, sHandle: str, itemList: list[str]) -> None:
48
49
  super().__init__(parent=parent)
@@ -53,27 +54,29 @@ class GuiDocMerge(QDialog):
53
54
 
54
55
  self._data = {}
55
56
 
56
- self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
57
+ self.headLabel = QLabel(self.tr("Documents to Merge"), self)
58
+ self.headLabel.setFont(SHARED.theme.guiFontB)
57
59
  self.helpLabel = NColourLabel(
58
60
  self.tr("Drag and drop items to change the order, or uncheck to exclude."),
59
61
  SHARED.theme.helpText, parent=self, wrap=True
60
62
  )
61
63
 
62
- iPx = SHARED.theme.baseIconSize
64
+ iPx = SHARED.theme.baseIconHeight
65
+ iSz = SHARED.theme.baseIconSize
63
66
  hSp = CONFIG.pxInt(12)
64
67
  vSp = CONFIG.pxInt(8)
65
68
  bSp = CONFIG.pxInt(12)
66
69
 
67
- self.listBox = QListWidget()
68
- self.listBox.setIconSize(QSize(iPx, iPx))
70
+ self.listBox = QListWidget(self)
71
+ self.listBox.setIconSize(iSz)
69
72
  self.listBox.setMinimumWidth(CONFIG.pxInt(400))
70
73
  self.listBox.setMinimumHeight(CONFIG.pxInt(180))
71
- self.listBox.setSelectionBehavior(QAbstractItemView.SelectRows)
72
- self.listBox.setSelectionMode(QAbstractItemView.SingleSelection)
73
- self.listBox.setDragDropMode(QAbstractItemView.InternalMove)
74
+ self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
75
+ self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
76
+ self.listBox.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
74
77
 
75
78
  # Merge Options
76
- self.trashLabel = QLabel(self.tr("Move merged items to Trash"))
79
+ self.trashLabel = QLabel(self.tr("Move merged items to Trash"), self)
77
80
  self.trashSwitch = NSwitch(self, height=iPx)
78
81
 
79
82
  self.optBox = QGridLayout()
@@ -83,11 +86,11 @@ class GuiDocMerge(QDialog):
83
86
  self.optBox.setColumnStretch(2, 1)
84
87
 
85
88
  # Buttons
86
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
89
+ self.buttonBox = QDialogButtonBox(QtDialogOk | QtDialogCancel, self)
87
90
  self.buttonBox.accepted.connect(self.accept)
88
91
  self.buttonBox.rejected.connect(self.reject)
89
92
 
90
- self.resetButton = self.buttonBox.addButton(QDialogButtonBox.Reset)
93
+ self.resetButton = self.buttonBox.addButton(QtDialogReset)
91
94
  self.resetButton.clicked.connect(self._resetList)
92
95
 
93
96
  # Assemble
@@ -119,7 +122,7 @@ class GuiDocMerge(QDialog):
119
122
  finalItems = []
120
123
  for i in range(self.listBox.count()):
121
124
  item = self.listBox.item(i)
122
- if item is not None and item.checkState() == Qt.Checked:
125
+ if item is not None and item.checkState() == Qt.CheckState.Checked:
123
126
  finalItems.append(item.data(self.D_HANDLE))
124
127
 
125
128
  self._data["moveToTrash"] = self.trashSwitch.isChecked()
@@ -174,7 +177,7 @@ class GuiDocMerge(QDialog):
174
177
  newItem.setIcon(itemIcon)
175
178
  newItem.setText(nwItem.itemName)
176
179
  newItem.setData(self.D_HANDLE, tHandle)
177
- newItem.setCheckState(Qt.Checked)
180
+ newItem.setCheckState(Qt.CheckState.Checked)
178
181
 
179
182
  self.listBox.addItem(newItem)
180
183
 
@@ -26,8 +26,8 @@ from __future__ import annotations
26
26
 
27
27
  import logging
28
28
 
29
+ from PyQt5.QtCore import pyqtSlot
29
30
  from PyQt5.QtGui import QCloseEvent
30
- from PyQt5.QtCore import Qt, pyqtSlot
31
31
  from PyQt5.QtWidgets import (
32
32
  QAbstractItemView, QComboBox, QDialog, QDialogButtonBox, QGridLayout,
33
33
  QLabel, QListWidget, QListWidgetItem, QVBoxLayout, QWidget
@@ -36,15 +36,16 @@ from PyQt5.QtWidgets import (
36
36
  from novelwriter import CONFIG, SHARED
37
37
  from novelwriter.extensions.switch import NSwitch
38
38
  from novelwriter.extensions.configlayout import NColourLabel
39
+ from novelwriter.types import QtDialogCancel, QtDialogOk, QtUserRole
39
40
 
40
41
  logger = logging.getLogger(__name__)
41
42
 
42
43
 
43
44
  class GuiDocSplit(QDialog):
44
45
 
45
- LINE_ROLE = Qt.ItemDataRole.UserRole
46
- LEVEL_ROLE = Qt.ItemDataRole.UserRole + 1
47
- LABEL_ROLE = Qt.ItemDataRole.UserRole + 2
46
+ LINE_ROLE = QtUserRole
47
+ LEVEL_ROLE = QtUserRole + 1
48
+ LABEL_ROLE = QtUserRole + 2
48
49
 
49
50
  def __init__(self, parent: QWidget, sHandle: str) -> None:
50
51
  super().__init__(parent=parent)
@@ -57,14 +58,15 @@ class GuiDocSplit(QDialog):
57
58
 
58
59
  self.setWindowTitle(self.tr("Split Document"))
59
60
 
60
- self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
61
+ self.headLabel = QLabel(self.tr("Document Headings"), self)
62
+ self.headLabel.setFont(SHARED.theme.guiFontB)
61
63
  self.helpLabel = NColourLabel(
62
64
  self.tr("Select the maximum level to split into files."),
63
65
  SHARED.theme.helpText, parent=self, wrap=True
64
66
  )
65
67
 
66
68
  # Values
67
- iPx = SHARED.theme.baseIconSize
69
+ iPx = SHARED.theme.baseIconHeight
68
70
  hSp = CONFIG.pxInt(12)
69
71
  vSp = CONFIG.pxInt(8)
70
72
  bSp = CONFIG.pxInt(12)
@@ -74,32 +76,32 @@ class GuiDocSplit(QDialog):
74
76
  intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True)
75
77
  docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True)
76
78
 
77
- # Header Selection
78
- self.listBox = QListWidget()
79
- self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
79
+ # Heading Selection
80
+ self.listBox = QListWidget(self)
81
+ self.listBox.setDragDropMode(QAbstractItemView.DragDropMode.NoDragDrop)
80
82
  self.listBox.setMinimumWidth(CONFIG.pxInt(400))
81
83
  self.listBox.setMinimumHeight(CONFIG.pxInt(180))
82
84
 
83
85
  self.splitLevel = QComboBox(self)
84
- self.splitLevel.addItem(self.tr("Split on Header Level 1 (Title)"), 1)
85
- self.splitLevel.addItem(self.tr("Split up to Header Level 2 (Chapter)"), 2)
86
- self.splitLevel.addItem(self.tr("Split up to Header Level 3 (Scene)"), 3)
87
- self.splitLevel.addItem(self.tr("Split up to Header Level 4 (Section)"), 4)
86
+ self.splitLevel.addItem(self.tr("Split on Heading Level 1 (Partition)"), 1)
87
+ self.splitLevel.addItem(self.tr("Split up to Heading Level 2 (Chapter)"), 2)
88
+ self.splitLevel.addItem(self.tr("Split up to Heading Level 3 (Scene)"), 3)
89
+ self.splitLevel.addItem(self.tr("Split up to Heading Level 4 (Section)"), 4)
88
90
  spIndex = self.splitLevel.findData(spLevel)
89
91
  if spIndex != -1:
90
92
  self.splitLevel.setCurrentIndex(spIndex)
91
93
  self.splitLevel.currentIndexChanged.connect(self._reloadList)
92
94
 
93
95
  # Split Options
94
- self.folderLabel = QLabel(self.tr("Split into a new folder"))
96
+ self.folderLabel = QLabel(self.tr("Split into a new folder"), self)
95
97
  self.folderSwitch = NSwitch(self, height=iPx)
96
98
  self.folderSwitch.setChecked(intoFolder)
97
99
 
98
- self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"))
100
+ self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"), self)
99
101
  self.hierarchySwitch = NSwitch(self, height=iPx)
100
102
  self.hierarchySwitch.setChecked(docHierarchy)
101
103
 
102
- self.trashLabel = QLabel(self.tr("Move split document to Trash"))
104
+ self.trashLabel = QLabel(self.tr("Move split document to Trash"), self)
103
105
  self.trashSwitch = NSwitch(self, height=iPx)
104
106
 
105
107
  self.optBox = QGridLayout()
@@ -114,7 +116,7 @@ class GuiDocSplit(QDialog):
114
116
  self.optBox.setColumnStretch(3, 1)
115
117
 
116
118
  # Buttons
117
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
119
+ self.buttonBox = QDialogButtonBox(QtDialogOk | QtDialogCancel, self)
118
120
  self.buttonBox.accepted.connect(self.accept)
119
121
  self.buttonBox.rejected.connect(self.reject)
120
122
 
@@ -214,8 +216,7 @@ class GuiDocSplit(QDialog):
214
216
 
215
217
  spLevel = self.splitLevel.currentData()
216
218
  if not self._text:
217
- inDoc = SHARED.project.storage.getDocument(sHandle)
218
- self._text = (inDoc.readDocument() or "").splitlines()
219
+ self._text = SHARED.project.storage.getDocumentText(sHandle).splitlines()
219
220
 
220
221
  for lineNo, aLine in enumerate(self._text):
221
222
 
@@ -31,6 +31,7 @@ from PyQt5.QtWidgets import (
31
31
  )
32
32
 
33
33
  from novelwriter import CONFIG
34
+ from novelwriter.types import QtDialogCancel, QtDialogOk
34
35
 
35
36
  logger = logging.getLogger(__name__)
36
37
 
@@ -55,13 +56,13 @@ class GuiEditLabel(QDialog):
55
56
  self.labelValue.selectAll()
56
57
 
57
58
  # Buttons
58
- self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
59
+ self.buttonBox = QDialogButtonBox(QtDialogOk | QtDialogCancel, self)
59
60
  self.buttonBox.accepted.connect(self.accept)
60
61
  self.buttonBox.rejected.connect(self.reject)
61
62
 
62
63
  # Assemble
63
64
  self.innerBox = QHBoxLayout()
64
- self.innerBox.addWidget(QLabel(self.tr("Label")), 0)
65
+ self.innerBox.addWidget(QLabel(self.tr("Label"), self), 0)
65
66
  self.innerBox.addWidget(self.labelValue, 1)
66
67
  self.innerBox.setSpacing(mSp)
67
68
 
@@ -88,9 +89,9 @@ class GuiEditLabel(QDialog):
88
89
  def getLabel(cls, parent: QWidget, text: str) -> tuple[str, bool]:
89
90
  """Pop the dialog and return the result."""
90
91
  cls = GuiEditLabel(parent, text=text)
91
- cls.exec_()
92
+ cls.exec()
92
93
  label = cls.itemLabel
93
- accepted = cls.result() == QDialog.Accepted
94
+ accepted = cls.result() == QDialog.DialogCode.Accepted
94
95
  cls.deleteLater()
95
96
  return label, accepted
96
97
 
@@ -26,21 +26,25 @@ from __future__ import annotations
26
26
 
27
27
  import logging
28
28
 
29
- from PyQt5.QtGui import QCloseEvent, QFont, QKeyEvent, QKeySequence
30
29
  from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
30
+ from PyQt5.QtGui import QCloseEvent, QFont, QKeyEvent, QKeySequence
31
31
  from PyQt5.QtWidgets import (
32
- QAbstractButton, QCompleter, QDialog, QDialogButtonBox, QFileDialog,
33
- QFontDialog, QHBoxLayout, QLineEdit, QPushButton, QToolButton, QVBoxLayout,
34
- QWidget, qApp
32
+ QAbstractButton, QApplication, QCompleter, QDialog, QDialogButtonBox,
33
+ QFileDialog, QFontDialog, QHBoxLayout, QLineEdit, QPushButton, QVBoxLayout,
34
+ QWidget
35
35
  )
36
36
 
37
37
  from novelwriter import CONFIG, SHARED
38
38
  from novelwriter.constants import nwConst, nwUnicode
39
39
  from novelwriter.dialogs.quotes import GuiQuoteSelect
40
- from novelwriter.extensions.switch import NSwitch
41
- from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NSpinBox
42
40
  from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
41
+ from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NIconToolButton, NSpinBox
43
42
  from novelwriter.extensions.pagedsidebar import NPagedSideBar
43
+ from novelwriter.extensions.switch import NSwitch
44
+ from novelwriter.types import (
45
+ QtAlignCenter, QtDialogApply, QtDialogClose, QtDialogSave, QtRoleAccept,
46
+ QtRoleApply, QtRoleReject
47
+ )
44
48
 
45
49
  logger = logging.getLogger(__name__)
46
50
 
@@ -83,11 +87,7 @@ class GuiPreferences(QDialog):
83
87
  self.mainForm.setHelpTextStyle(SHARED.theme.helpText)
84
88
 
85
89
  # Buttons
86
- self.buttonBox = QDialogButtonBox(
87
- QDialogButtonBox.StandardButton.Apply
88
- | QDialogButtonBox.StandardButton.Save
89
- | QDialogButtonBox.StandardButton.Close
90
- )
90
+ self.buttonBox = QDialogButtonBox(QtDialogApply | QtDialogSave | QtDialogClose, self)
91
91
  self.buttonBox.clicked.connect(self._dialogButtonClicked)
92
92
 
93
93
  # Assemble
@@ -132,8 +132,8 @@ class GuiPreferences(QDialog):
132
132
  def buildForm(self) -> None:
133
133
  """Build the settings form."""
134
134
  section = 0
135
+ iSz = SHARED.theme.baseIconSize
135
136
  minWidth = CONFIG.pxInt(200)
136
- mIcon = SHARED.theme.getIcon("more")
137
137
 
138
138
  # Label
139
139
  self.sidebar.addLabel(self.tr("General"))
@@ -151,8 +151,7 @@ class GuiPreferences(QDialog):
151
151
  self.guiLocale.setMinimumWidth(minWidth)
152
152
  for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW):
153
153
  self.guiLocale.addItem(name, lang)
154
- if (idx := self.guiLocale.findData(CONFIG.guiLocale)) != -1:
155
- self.guiLocale.setCurrentIndex(idx)
154
+ self.guiLocale.setCurrentData(CONFIG.guiLocale, "en_GB")
156
155
 
157
156
  self.mainForm.addRow(
158
157
  self.tr("Display language"), self.guiLocale,
@@ -164,8 +163,7 @@ class GuiPreferences(QDialog):
164
163
  self.guiTheme.setMinimumWidth(minWidth)
165
164
  for theme, name in SHARED.theme.listThemes():
166
165
  self.guiTheme.addItem(name, theme)
167
- if (idx := self.guiTheme.findData(CONFIG.guiTheme)) != -1:
168
- self.guiTheme.setCurrentIndex(idx)
166
+ self.guiTheme.setCurrentData(CONFIG.guiTheme, "default")
169
167
 
170
168
  self.mainForm.addRow(
171
169
  self.tr("Colour theme"), self.guiTheme,
@@ -177,8 +175,7 @@ class GuiPreferences(QDialog):
177
175
  self.guiFont.setReadOnly(True)
178
176
  self.guiFont.setMinimumWidth(CONFIG.pxInt(162))
179
177
  self.guiFont.setText(CONFIG.guiFont)
180
- self.guiFontButton = QToolButton(self)
181
- self.guiFontButton.setIcon(mIcon)
178
+ self.guiFontButton = NIconToolButton(self, iSz, "more")
182
179
  self.guiFontButton.clicked.connect(self._selectGuiFont)
183
180
  self.mainForm.addRow(
184
181
  self.tr("Application font family"), self.guiFont,
@@ -226,8 +223,7 @@ class GuiPreferences(QDialog):
226
223
  self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200))
227
224
  for syntax, name in SHARED.theme.listSyntax():
228
225
  self.guiSyntax.addItem(name, syntax)
229
- if (idx := self.guiSyntax.findData(CONFIG.guiSyntax)) != -1:
230
- self.guiSyntax.setCurrentIndex(idx)
226
+ self.guiSyntax.setCurrentData(CONFIG.guiSyntax, "default_light")
231
227
 
232
228
  self.mainForm.addRow(
233
229
  self.tr("Document colour theme"), self.guiSyntax,
@@ -239,8 +235,7 @@ class GuiPreferences(QDialog):
239
235
  self.textFont.setReadOnly(True)
240
236
  self.textFont.setMinimumWidth(CONFIG.pxInt(162))
241
237
  self.textFont.setText(CONFIG.textFont)
242
- self.textFontButton = QToolButton(self)
243
- self.textFontButton.setIcon(mIcon)
238
+ self.textFontButton = NIconToolButton(self, iSz, "more")
244
239
  self.textFontButton.clicked.connect(self._selectTextFont)
245
240
  self.mainForm.addRow(
246
241
  self.tr("Document font family"), self.textFont,
@@ -323,6 +318,7 @@ class GuiPreferences(QDialog):
323
318
  # Backup Path
324
319
  self.backupPath = CONFIG.backupPath()
325
320
  self.backupGetPath = QPushButton(SHARED.theme.getIcon("browse"), self.tr("Browse"), self)
321
+ self.backupGetPath.setIconSize(iSz)
326
322
  self.backupGetPath.clicked.connect(self._backupFolder)
327
323
  self.mainForm.addRow(
328
324
  self.tr("Backup storage location"), self.backupGetPath,
@@ -678,10 +674,9 @@ class GuiPreferences(QDialog):
678
674
  self.quoteSym["SO"].setMaxLength(1)
679
675
  self.quoteSym["SO"].setReadOnly(True)
680
676
  self.quoteSym["SO"].setFixedWidth(qWidth)
681
- self.quoteSym["SO"].setAlignment(Qt.AlignCenter)
677
+ self.quoteSym["SO"].setAlignment(QtAlignCenter)
682
678
  self.quoteSym["SO"].setText(CONFIG.fmtSQuoteOpen)
683
- self.btnSingleStyleO = QToolButton(self)
684
- self.btnSingleStyleO.setIcon(mIcon)
679
+ self.btnSingleStyleO = NIconToolButton(self, iSz, "more")
685
680
  self.btnSingleStyleO.clicked.connect(lambda: self._getQuote("SO"))
686
681
  self.mainForm.addRow(
687
682
  self.tr("Single quote open style"), self.quoteSym["SO"],
@@ -693,10 +688,9 @@ class GuiPreferences(QDialog):
693
688
  self.quoteSym["SC"].setMaxLength(1)
694
689
  self.quoteSym["SC"].setReadOnly(True)
695
690
  self.quoteSym["SC"].setFixedWidth(qWidth)
696
- self.quoteSym["SC"].setAlignment(Qt.AlignCenter)
691
+ self.quoteSym["SC"].setAlignment(QtAlignCenter)
697
692
  self.quoteSym["SC"].setText(CONFIG.fmtSQuoteClose)
698
- self.btnSingleStyleC = QToolButton(self)
699
- self.btnSingleStyleC.setIcon(mIcon)
693
+ self.btnSingleStyleC = NIconToolButton(self, iSz, "more")
700
694
  self.btnSingleStyleC.clicked.connect(lambda: self._getQuote("SC"))
701
695
  self.mainForm.addRow(
702
696
  self.tr("Single quote close style"), self.quoteSym["SC"],
@@ -709,10 +703,9 @@ class GuiPreferences(QDialog):
709
703
  self.quoteSym["DO"].setMaxLength(1)
710
704
  self.quoteSym["DO"].setReadOnly(True)
711
705
  self.quoteSym["DO"].setFixedWidth(qWidth)
712
- self.quoteSym["DO"].setAlignment(Qt.AlignCenter)
706
+ self.quoteSym["DO"].setAlignment(QtAlignCenter)
713
707
  self.quoteSym["DO"].setText(CONFIG.fmtDQuoteOpen)
714
- self.btnDoubleStyleO = QToolButton(self)
715
- self.btnDoubleStyleO.setIcon(mIcon)
708
+ self.btnDoubleStyleO = NIconToolButton(self, iSz, "more")
716
709
  self.btnDoubleStyleO.clicked.connect(lambda: self._getQuote("DO"))
717
710
  self.mainForm.addRow(
718
711
  self.tr("Double quote open style"), self.quoteSym["DO"],
@@ -724,10 +717,9 @@ class GuiPreferences(QDialog):
724
717
  self.quoteSym["DC"].setMaxLength(1)
725
718
  self.quoteSym["DC"].setReadOnly(True)
726
719
  self.quoteSym["DC"].setFixedWidth(qWidth)
727
- self.quoteSym["DC"].setAlignment(Qt.AlignCenter)
720
+ self.quoteSym["DC"].setAlignment(QtAlignCenter)
728
721
  self.quoteSym["DC"].setText(CONFIG.fmtDQuoteClose)
729
- self.btnDoubleStyleC = QToolButton(self)
730
- self.btnDoubleStyleC.setIcon(mIcon)
722
+ self.btnDoubleStyleC = NIconToolButton(self, iSz, "more")
731
723
  self.btnDoubleStyleC.clicked.connect(lambda: self._getQuote("DC"))
732
724
  self.mainForm.addRow(
733
725
  self.tr("Double quote close style"), self.quoteSym["DC"],
@@ -749,7 +741,7 @@ class GuiPreferences(QDialog):
749
741
  logger.debug("Close: GuiPreferences")
750
742
  self._saveWindowSize()
751
743
  event.accept()
752
- qApp.processEvents()
744
+ QApplication.processEvents()
753
745
  self.done(nwConst.DLG_FINISHED)
754
746
  self.deleteLater()
755
747
  return
@@ -769,12 +761,12 @@ class GuiPreferences(QDialog):
769
761
  def _dialogButtonClicked(self, button: QAbstractButton) -> None:
770
762
  """Handle button clicks from the dialog button box."""
771
763
  role = self.buttonBox.buttonRole(button)
772
- if role == QDialogButtonBox.ButtonRole.ApplyRole:
764
+ if role == QtRoleApply:
773
765
  self._saveValues()
774
- elif role == QDialogButtonBox.ButtonRole.AcceptRole:
766
+ elif role == QtRoleAccept:
775
767
  self._saveValues()
776
768
  self.close()
777
- elif role == QDialogButtonBox.ButtonRole.RejectRole:
769
+ elif role == QtRoleReject:
778
770
  self.close()
779
771
  return
780
772
 
@@ -803,7 +795,7 @@ class GuiPreferences(QDialog):
803
795
  return
804
796
 
805
797
  @pyqtSlot()
806
- def _selectTextFont(self):
798
+ def _selectTextFont(self) -> None:
807
799
  """Open the QFontDialog and set a font for the font style."""
808
800
  current = QFont()
809
801
  current.setFamily(CONFIG.textFont)
@@ -819,7 +811,7 @@ class GuiPreferences(QDialog):
819
811
  """Open a dialog to select the backup folder."""
820
812
  if path := QFileDialog.getExistingDirectory(
821
813
  self, self.tr("Backup Directory"), str(self.backupPath) or "",
822
- options=QFileDialog.ShowDirsOnly
814
+ options=QFileDialog.Option.ShowDirsOnly
823
815
  ):
824
816
  self.backupPath = path
825
817
  self.mainForm.setHelpText("backupPath", self.tr("Path: {0}").format(path))