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
@@ -27,17 +27,20 @@ import logging
27
27
 
28
28
  from enum import Enum
29
29
 
30
- from PyQt5.QtCore import QModelIndex, QSize, Qt, pyqtSignal, pyqtSlot
30
+ from PyQt5.QtCore import QModelIndex, Qt, pyqtSignal, pyqtSlot
31
31
  from PyQt5.QtWidgets import (
32
32
  QAbstractItemView, QFrame, QHeaderView, QMenu, QTabWidget, QToolButton,
33
33
  QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
34
34
  )
35
35
 
36
36
  from novelwriter import CONFIG, SHARED
37
- from novelwriter.enum import nwDocMode, nwItemClass
38
37
  from novelwriter.common import checkInt
39
38
  from novelwriter.constants import nwHeaders, nwLabels, nwLists, trConst
40
39
  from novelwriter.core.index import IndexHeading, IndexItem
40
+ from novelwriter.enum import nwDocMode, nwItemClass
41
+ from novelwriter.extensions.modified import NIconToolButton
42
+ from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON
43
+ from novelwriter.types import QtDecoration, QtUserRole
41
44
 
42
45
  logger = logging.getLogger(__name__)
43
46
 
@@ -54,7 +57,7 @@ class GuiDocViewerPanel(QWidget):
54
57
 
55
58
  self._lastHandle = None
56
59
 
57
- iPx = int(1.0*SHARED.theme.baseIconSize)
60
+ iSz = SHARED.theme.baseIconSize
58
61
 
59
62
  self.tabBackRefs = _ViewPanelBackRefs(self)
60
63
 
@@ -64,8 +67,7 @@ class GuiDocViewerPanel(QWidget):
64
67
  self.aInactive.setCheckable(True)
65
68
  self.aInactive.toggled.connect(self._toggleHideInactive)
66
69
 
67
- self.optsButton = QToolButton(self)
68
- self.optsButton.setIconSize(QSize(iPx, iPx))
70
+ self.optsButton = NIconToolButton(self, iSz)
69
71
  self.optsButton.setMenu(self.optsMenu)
70
72
  self.optsButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
71
73
 
@@ -99,35 +101,14 @@ class GuiDocViewerPanel(QWidget):
99
101
 
100
102
  def updateTheme(self, updateTabs: bool = True) -> None:
101
103
  """Update theme elements."""
102
- qPalette = self.palette()
103
- mPx = CONFIG.pxInt(2)
104
- vPx = CONFIG.pxInt(4)
105
- hPx = CONFIG.pxInt(8)
106
- hCol = qPalette.highlight().color()
107
- fCol = qPalette.text().color()
108
-
109
- buttonStyle = (
110
- "QToolButton {{padding: {0}px; margin: 0 0 {1}px 0; border: none; "
111
- "background: transparent;}} "
112
- "QToolButton:hover {{border: none; background: rgba({2}, {3}, {4}, 0.2);}} "
113
- "QToolButton::menu-indicator {{image: none;}} "
114
- ).format(mPx, mPx, fCol.red(), fCol.green(), fCol.blue())
115
- self.optsButton.setIcon(SHARED.theme.getIcon("menu"))
116
- self.optsButton.setStyleSheet(buttonStyle)
117
-
118
- styleSheet = (
119
- "QTabWidget::pane {{border: 0;}} "
120
- "QTabWidget QTabBar::tab {{border: 0; padding: {0}px {1}px;}} "
121
- "QTabWidget QTabBar::tab:selected {{color: rgb({2}, {3}, {4});}} "
122
- ).format(vPx, hPx, hCol.red(), hCol.green(), hCol.blue())
123
- self.mainTabs.setStyleSheet(styleSheet)
104
+ self.optsButton.setThemeIcon("menu")
105
+ self.optsButton.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON))
106
+ self.mainTabs.setStyleSheet(SHARED.theme.getStyleSheet(STYLES_FLAT_TABS))
124
107
  self.updateHandle(self._lastHandle)
125
-
126
108
  if updateTabs:
127
109
  self.tabBackRefs.updateTheme()
128
110
  for tab in self.kwTabs.values():
129
111
  tab.updateTheme()
130
-
131
112
  return
132
113
 
133
114
  def openProjectTasks(self) -> None:
@@ -252,7 +233,7 @@ class _ViewPanelBackRefs(QTreeWidget):
252
233
  C_VIEW = 2
253
234
  C_TITLE = 3
254
235
 
255
- D_HANDLE = Qt.ItemDataRole.UserRole
236
+ D_HANDLE = QtUserRole
256
237
 
257
238
  def __init__(self, parent: GuiDocViewerPanel) -> None:
258
239
  super().__init__(parent=parent)
@@ -260,13 +241,14 @@ class _ViewPanelBackRefs(QTreeWidget):
260
241
  self._parent = parent
261
242
  self._treeMap: dict[str, QTreeWidgetItem] = {}
262
243
 
263
- iPx = SHARED.theme.baseIconSize
244
+ iPx = SHARED.theme.baseIconHeight
245
+ iSz = SHARED.theme.baseIconSize
264
246
  cMg = CONFIG.pxInt(6)
265
247
 
266
248
  self.setHeaderLabels([self.tr("Document"), "", "", self.tr("First Heading")])
267
249
  self.setIndentation(0)
268
250
  self.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
269
- self.setIconSize(QSize(iPx, iPx))
251
+ self.setIconSize(iSz)
270
252
  self.setFrameStyle(QFrame.Shape.NoFrame)
271
253
 
272
254
  # Set Header Sizes
@@ -368,7 +350,7 @@ class _ViewPanelBackRefs(QTreeWidget):
368
350
  trItem.setToolTip(self.C_DOC, nwItem.itemName)
369
351
  trItem.setIcon(self.C_EDIT, self._editIcon)
370
352
  trItem.setIcon(self.C_VIEW, self._viewIcon)
371
- trItem.setData(self.C_TITLE, Qt.ItemDataRole.DecorationRole, hDec)
353
+ trItem.setData(self.C_TITLE, QtDecoration, hDec)
372
354
  trItem.setText(self.C_TITLE, hItem.title)
373
355
  trItem.setToolTip(self.C_TITLE, hItem.title)
374
356
  trItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
@@ -393,7 +375,7 @@ class _ViewPanelKeyWords(QTreeWidget):
393
375
  C_TITLE = 5
394
376
  C_SHORT = 6
395
377
 
396
- D_TAG = Qt.ItemDataRole.UserRole
378
+ D_TAG = QtUserRole
397
379
 
398
380
  def __init__(self, parent: GuiDocViewerPanel, itemClass: nwItemClass) -> None:
399
381
  super().__init__(parent=parent)
@@ -402,7 +384,8 @@ class _ViewPanelKeyWords(QTreeWidget):
402
384
  self._class = itemClass
403
385
  self._treeMap: dict[str, QTreeWidgetItem] = {}
404
386
 
405
- iPx = SHARED.theme.baseIconSize
387
+ iPx = SHARED.theme.baseIconHeight
388
+ iSz = SHARED.theme.baseIconSize
406
389
  cMg = CONFIG.pxInt(6)
407
390
 
408
391
  self.setHeaderLabels([
@@ -410,7 +393,7 @@ class _ViewPanelKeyWords(QTreeWidget):
410
393
  self.tr("Heading"), self.tr("Short Description")
411
394
  ])
412
395
  self.setIndentation(0)
413
- self.setIconSize(QSize(iPx, iPx))
396
+ self.setIconSize(iSz)
414
397
  self.setFrameStyle(QFrame.Shape.NoFrame)
415
398
  self.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
416
399
  self.setExpandsOnDoubleClick(False)
@@ -486,7 +469,7 @@ class _ViewPanelKeyWords(QTreeWidget):
486
469
  trItem.setIcon(self.C_DOC, docIcon)
487
470
  trItem.setText(self.C_DOC, nwItem.itemName)
488
471
  trItem.setToolTip(self.C_DOC, nwItem.itemName)
489
- trItem.setData(self.C_TITLE, Qt.ItemDataRole.DecorationRole, hDec)
472
+ trItem.setData(self.C_TITLE, QtDecoration, hDec)
490
473
  trItem.setText(self.C_TITLE, hItem.title)
491
474
  trItem.setToolTip(self.C_TITLE, hItem.title)
492
475
  trItem.setText(self.C_SHORT, hItem.synopsis)
@@ -25,13 +25,14 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
+ from collections.abc import Iterable
28
29
  from time import time
29
30
 
30
- from PyQt5.QtGui import QTextCursor, QTextDocument
31
+ from PyQt5.QtGui import QTextBlock, QTextCursor, QTextDocument
31
32
  from PyQt5.QtCore import QObject, pyqtSlot
32
- from PyQt5.QtWidgets import QPlainTextDocumentLayout, qApp
33
- from novelwriter import SHARED
33
+ from PyQt5.QtWidgets import QApplication, QPlainTextDocumentLayout
34
34
 
35
+ from novelwriter import SHARED
35
36
  from novelwriter.gui.dochighlight import GuiDocHighlighter, TextBlockData
36
37
 
37
38
  logger = logging.getLogger(__name__)
@@ -85,7 +86,7 @@ class GuiTextDocument(QTextDocument):
85
86
  self.setUndoRedoEnabled(True)
86
87
  self.blockSignals(False)
87
88
  self._syntax.rehighlight()
88
- qApp.processEvents()
89
+ QApplication.processEvents()
89
90
 
90
91
  tEnd = time()
91
92
 
@@ -113,6 +114,16 @@ class GuiTextDocument(QTextDocument):
113
114
  return word, cPos, cLen, SHARED.spelling.suggestWords(word)
114
115
  return "", -1, -1, []
115
116
 
117
+ def iterBlockByType(self, cType: int, maxCount: int = 1000) -> Iterable[QTextBlock]:
118
+ """Iterate over all text blocks of a given type."""
119
+ count = 0
120
+ for i in range(self.blockCount()):
121
+ block = self.findBlockByNumber(i)
122
+ if count < maxCount and block.isValid() and block.userState() & cType > 0:
123
+ count += 1
124
+ yield block
125
+ return None
126
+
116
127
  ##
117
128
  # Public Slots
118
129
  ##
@@ -25,30 +25,27 @@ from __future__ import annotations
25
25
 
26
26
  import logging
27
27
 
28
- from typing import TYPE_CHECKING
29
-
30
- from PyQt5.QtGui import QFont
31
- from PyQt5.QtCore import Qt, pyqtSlot
28
+ from PyQt5.QtCore import pyqtSlot
32
29
  from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
33
30
 
34
31
  from novelwriter import CONFIG, SHARED
35
32
  from novelwriter.constants import trConst, nwLabels
36
-
37
- if TYPE_CHECKING: # pragma: no cover
38
- from novelwriter.guimain import GuiMain
33
+ from novelwriter.types import (
34
+ QtAlignLeft, QtAlignLeftBase, QtAlignRight, QtAlignRightBase, QtAlignRightMiddle
35
+ )
39
36
 
40
37
  logger = logging.getLogger(__name__)
41
38
 
42
39
 
43
40
  class GuiItemDetails(QWidget):
44
41
 
45
- def __init__(self, mainGui: GuiMain) -> None:
46
- super().__init__(parent=mainGui)
42
+ def __init__(self, parent: QWidget) -> None:
43
+ super().__init__(parent=parent)
47
44
 
48
45
  logger.debug("Create: GuiItemDetails")
49
46
 
50
47
  # Internal Variables
51
- self._itemHandle = None
48
+ self._handle = None
52
49
 
53
50
  # Sizes
54
51
  hSp = CONFIG.pxInt(6)
@@ -56,89 +53,89 @@ class GuiItemDetails(QWidget):
56
53
  mPx = CONFIG.pxInt(6)
57
54
  fPt = SHARED.theme.fontPointSize
58
55
 
59
- fntLabel = QFont()
56
+ fntLabel = self.font()
60
57
  fntLabel.setBold(True)
61
58
  fntLabel.setPointSizeF(0.9*fPt)
62
59
 
63
- fntValue = QFont()
60
+ fntValue = self.font()
64
61
  fntValue.setPointSizeF(0.9*fPt)
65
62
 
66
63
  # Label
67
- self.labelName = QLabel(self.tr("Label"))
64
+ self.labelName = QLabel(self.tr("Label"), self)
68
65
  self.labelName.setFont(fntLabel)
69
- self.labelName.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
66
+ self.labelName.setAlignment(QtAlignLeftBase)
70
67
 
71
- self.labelIcon = QLabel("")
72
- self.labelIcon.setAlignment(Qt.AlignRight | Qt.AlignBaseline)
68
+ self.labelIcon = QLabel("", self)
69
+ self.labelIcon.setAlignment(QtAlignRightBase)
73
70
 
74
- self.labelData = QLabel("")
71
+ self.labelData = QLabel("", self)
75
72
  self.labelData.setFont(fntValue)
76
- self.labelData.setAlignment(Qt.AlignLeft | Qt.AlignBaseline)
73
+ self.labelData.setAlignment(QtAlignLeftBase)
77
74
  self.labelData.setWordWrap(True)
78
75
 
79
76
  # Status
80
- self.statusName = QLabel(self.tr("Status"))
77
+ self.statusName = QLabel(self.tr("Status"), self)
81
78
  self.statusName.setFont(fntLabel)
82
- self.statusName.setAlignment(Qt.AlignLeft)
79
+ self.statusName.setAlignment(QtAlignLeft)
83
80
 
84
- self.statusIcon = QLabel("")
85
- self.statusIcon.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
81
+ self.statusIcon = QLabel("", self)
82
+ self.statusIcon.setAlignment(QtAlignRightMiddle)
86
83
 
87
- self.statusData = QLabel("")
84
+ self.statusData = QLabel("", self)
88
85
  self.statusData.setFont(fntValue)
89
- self.statusData.setAlignment(Qt.AlignLeft)
86
+ self.statusData.setAlignment(QtAlignLeft)
90
87
 
91
88
  # Class
92
- self.className = QLabel(self.tr("Class"))
89
+ self.className = QLabel(self.tr("Class"), self)
93
90
  self.className.setFont(fntLabel)
94
- self.className.setAlignment(Qt.AlignLeft)
91
+ self.className.setAlignment(QtAlignLeft)
95
92
 
96
- self.classIcon = QLabel("")
97
- self.classIcon.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
93
+ self.classIcon = QLabel("", self)
94
+ self.classIcon.setAlignment(QtAlignRightMiddle)
98
95
 
99
- self.classData = QLabel("")
96
+ self.classData = QLabel("", self)
100
97
  self.classData.setFont(fntValue)
101
- self.classData.setAlignment(Qt.AlignLeft)
98
+ self.classData.setAlignment(QtAlignLeft)
102
99
 
103
100
  # Layout
104
- self.usageName = QLabel(self.tr("Usage"))
101
+ self.usageName = QLabel(self.tr("Usage"), self)
105
102
  self.usageName.setFont(fntLabel)
106
- self.usageName.setAlignment(Qt.AlignLeft)
103
+ self.usageName.setAlignment(QtAlignLeft)
107
104
 
108
- self.usageIcon = QLabel("")
109
- self.usageIcon.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
105
+ self.usageIcon = QLabel("", self)
106
+ self.usageIcon.setAlignment(QtAlignRightMiddle)
110
107
 
111
- self.usageData = QLabel("")
108
+ self.usageData = QLabel("", self)
112
109
  self.usageData.setFont(fntValue)
113
- self.usageData.setAlignment(Qt.AlignLeft)
110
+ self.usageData.setAlignment(QtAlignLeft)
114
111
  self.usageData.setWordWrap(True)
115
112
 
116
113
  # Character Count
117
- self.cCountName = QLabel(" "+self.tr("Characters"))
114
+ self.cCountName = QLabel(" "+self.tr("Characters"), self)
118
115
  self.cCountName.setFont(fntLabel)
119
- self.cCountName.setAlignment(Qt.AlignRight)
116
+ self.cCountName.setAlignment(QtAlignRight)
120
117
 
121
- self.cCountData = QLabel("")
118
+ self.cCountData = QLabel("", self)
122
119
  self.cCountData.setFont(fntValue)
123
- self.cCountData.setAlignment(Qt.AlignRight)
120
+ self.cCountData.setAlignment(QtAlignRight)
124
121
 
125
122
  # Word Count
126
- self.wCountName = QLabel(" "+self.tr("Words"))
123
+ self.wCountName = QLabel(" "+self.tr("Words"), self)
127
124
  self.wCountName.setFont(fntLabel)
128
- self.wCountName.setAlignment(Qt.AlignRight)
125
+ self.wCountName.setAlignment(QtAlignRight)
129
126
 
130
- self.wCountData = QLabel("")
127
+ self.wCountData = QLabel("", self)
131
128
  self.wCountData.setFont(fntValue)
132
- self.wCountData.setAlignment(Qt.AlignRight)
129
+ self.wCountData.setAlignment(QtAlignRight)
133
130
 
134
131
  # Paragraph Count
135
- self.pCountName = QLabel(" "+self.tr("Paragraphs"))
132
+ self.pCountName = QLabel(" "+self.tr("Paragraphs"), self)
136
133
  self.pCountName.setFont(fntLabel)
137
- self.pCountName.setAlignment(Qt.AlignRight)
134
+ self.pCountName.setAlignment(QtAlignRight)
138
135
 
139
- self.pCountData = QLabel("")
136
+ self.pCountData = QLabel("", self)
140
137
  self.pCountData.setFont(fntValue)
141
- self.pCountData.setAlignment(Qt.AlignRight)
138
+ self.pCountData.setAlignment(QtAlignRight)
142
139
 
143
140
  # Assemble
144
141
  self.mainBox = QGridLayout(self)
@@ -194,7 +191,7 @@ class GuiItemDetails(QWidget):
194
191
 
195
192
  def clearDetails(self) -> None:
196
193
  """Clear all the data values."""
197
- self._itemHandle = None
194
+ self._handle = None
198
195
  self.labelIcon.clear()
199
196
  self.labelData.clear()
200
197
  self.statusIcon.clear()
@@ -210,11 +207,11 @@ class GuiItemDetails(QWidget):
210
207
 
211
208
  def refreshDetails(self) -> None:
212
209
  """Reload the content of the details panel."""
213
- self.updateViewBox(self._itemHandle)
210
+ self.updateViewBox(self._handle)
214
211
 
215
212
  def updateTheme(self) -> None:
216
213
  """Update theme elements."""
217
- self.updateViewBox(self._itemHandle)
214
+ self.updateViewBox(self._handle)
218
215
  return
219
216
 
220
217
  ##
@@ -233,8 +230,8 @@ class GuiItemDetails(QWidget):
233
230
  self.clearDetails()
234
231
  return
235
232
 
236
- self._itemHandle = tHandle
237
- iPx = int(round(0.8*SHARED.theme.baseIconSize))
233
+ self._handle = tHandle
234
+ iPx = int(round(0.9*SHARED.theme.baseIconHeight))
238
235
 
239
236
  # Label
240
237
  # =====
@@ -295,7 +292,7 @@ class GuiItemDetails(QWidget):
295
292
  """Update the counts if the handle is the same as the one we're
296
293
  already showing. Otherwise, do nothing.
297
294
  """
298
- if tHandle == self._itemHandle:
295
+ if tHandle == self._handle:
299
296
  self.cCountData.setText(f"{cC:n}")
300
297
  self.wCountData.setText(f"{wC:n}")
301
298
  self.pCountData.setText(f"{pC:n}")
@@ -33,9 +33,9 @@ from PyQt5.QtCore import QUrl, pyqtSignal, pyqtSlot
33
33
  from PyQt5.QtWidgets import QMenuBar, QAction
34
34
 
35
35
  from novelwriter import CONFIG, SHARED
36
- from novelwriter.enum import nwDocAction, nwDocInsert, nwWidget
37
36
  from novelwriter.common import openExternalPath
38
37
  from novelwriter.constants import nwConst, trConst, nwKeyWords, nwLabels, nwUnicode
38
+ from novelwriter.enum import nwDocAction, nwDocInsert, nwView, nwWidget
39
39
  from novelwriter.extensions.eventfilters import StatusTipFilter
40
40
 
41
41
  if TYPE_CHECKING: # pragma: no cover
@@ -55,6 +55,7 @@ class GuiMainMenu(QMenuBar):
55
55
  requestDocInsertText = pyqtSignal(str)
56
56
  requestDocKeyWordInsert = pyqtSignal(str)
57
57
  requestFocusChange = pyqtSignal(nwWidget)
58
+ requestViewChange = pyqtSignal(nwView)
58
59
 
59
60
  def __init__(self, mainGui: GuiMain) -> None:
60
61
  super().__init__(parent=mainGui)
@@ -196,12 +197,12 @@ class GuiMainMenu(QMenuBar):
196
197
  # Document > Open
197
198
  self.aOpenDoc = self.docuMenu.addAction(self.tr("Open Document"))
198
199
  self.aOpenDoc.setShortcut("Ctrl+O")
199
- self.aOpenDoc.triggered.connect(lambda: self.mainGui.openSelectedItem())
200
+ self.aOpenDoc.triggered.connect(self.mainGui.openSelectedItem)
200
201
 
201
202
  # Document > Save
202
203
  self.aSaveDoc = self.docuMenu.addAction(self.tr("Save Document"))
203
204
  self.aSaveDoc.setShortcut("Ctrl+S")
204
- self.aSaveDoc.triggered.connect(lambda: self.mainGui.saveDocument())
205
+ self.aSaveDoc.triggered.connect(self.mainGui.saveDocument)
205
206
 
206
207
  # Document > Close
207
208
  self.aCloseDoc = self.docuMenu.addAction(self.tr("Close Document"))
@@ -219,7 +220,7 @@ class GuiMainMenu(QMenuBar):
219
220
  # Document > Close Preview
220
221
  self.aCloseView = self.docuMenu.addAction(self.tr("Close Document View"))
221
222
  self.aCloseView.setShortcut("Ctrl+Shift+R")
222
- self.aCloseView.triggered.connect(lambda: self.mainGui.closeDocViewer())
223
+ self.aCloseView.triggered.connect(self.mainGui.closeDocViewer)
223
224
 
224
225
  # Document > Separator
225
226
  self.docuMenu.addSeparator()
@@ -230,7 +231,6 @@ class GuiMainMenu(QMenuBar):
230
231
 
231
232
  # Document > Import From File
232
233
  self.aImportFile = self.docuMenu.addAction(self.tr("Import Text from File"))
233
- self.aImportFile.setShortcut("Ctrl+Shift+I")
234
234
  self.aImportFile.triggered.connect(lambda: self.mainGui.importDocument())
235
235
 
236
236
  return
@@ -672,6 +672,12 @@ class GuiMainMenu(QMenuBar):
672
672
  lambda: self.requestDocAction.emit(nwDocAction.SC_ULINE)
673
673
  )
674
674
 
675
+ # Shortcode Mark
676
+ self.aScMark = self.mShortcodes.addAction(self.tr("Highlight"))
677
+ self.aScMark.triggered.connect(
678
+ lambda: self.requestDocAction.emit(nwDocAction.SC_MARK)
679
+ )
680
+
675
681
  # Shortcode Superscript
676
682
  self.aScSuper = self.mShortcodes.addAction(self.tr("Superscript"))
677
683
  self.aScSuper.triggered.connect(
@@ -687,29 +693,29 @@ class GuiMainMenu(QMenuBar):
687
693
  # Format > Separator
688
694
  self.fmtMenu.addSeparator()
689
695
 
690
- # Format > Header 1 (Partition)
691
- self.aFmtHead1 = self.fmtMenu.addAction(self.tr("Header 1 (Partition)"))
696
+ # Format > Heading 1 (Partition)
697
+ self.aFmtHead1 = self.fmtMenu.addAction(self.tr("Heading 1 (Partition)"))
692
698
  self.aFmtHead1.setShortcut("Ctrl+1")
693
699
  self.aFmtHead1.triggered.connect(
694
700
  lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H1)
695
701
  )
696
702
 
697
- # Format > Header 2 (Chapter)
698
- self.aFmtHead2 = self.fmtMenu.addAction(self.tr("Header 2 (Chapter)"))
703
+ # Format > Heading 2 (Chapter)
704
+ self.aFmtHead2 = self.fmtMenu.addAction(self.tr("Heading 2 (Chapter)"))
699
705
  self.aFmtHead2.setShortcut("Ctrl+2")
700
706
  self.aFmtHead2.triggered.connect(
701
707
  lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H2)
702
708
  )
703
709
 
704
- # Format > Header 3 (Scene)
705
- self.aFmtHead3 = self.fmtMenu.addAction(self.tr("Header 3 (Scene)"))
710
+ # Format > Heading 3 (Scene)
711
+ self.aFmtHead3 = self.fmtMenu.addAction(self.tr("Heading 3 (Scene)"))
706
712
  self.aFmtHead3.setShortcut("Ctrl+3")
707
713
  self.aFmtHead3.triggered.connect(
708
714
  lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H3)
709
715
  )
710
716
 
711
- # Format > Header 4 (Section)
712
- self.aFmtHead4 = self.fmtMenu.addAction(self.tr("Header 4 (Section)"))
717
+ # Format > Heading 4 (Section)
718
+ self.aFmtHead4 = self.fmtMenu.addAction(self.tr("Heading 4 (Section)"))
713
719
  self.aFmtHead4.setShortcut("Ctrl+4")
714
720
  self.aFmtHead4.triggered.connect(
715
721
  lambda: self.requestDocAction.emit(nwDocAction.BLOCK_H4)
@@ -730,6 +736,12 @@ class GuiMainMenu(QMenuBar):
730
736
  lambda: self.requestDocAction.emit(nwDocAction.BLOCK_UNN)
731
737
  )
732
738
 
739
+ # Format > Alternative Scene
740
+ self.aFmtHardSc = self.fmtMenu.addAction(self.tr("Alternative Scene"))
741
+ self.aFmtHardSc.triggered.connect(
742
+ lambda: self.requestDocAction.emit(nwDocAction.BLOCK_HSC)
743
+ )
744
+
733
745
  # Format > Separator
734
746
  self.fmtMenu.addSeparator()
735
747
 
@@ -798,14 +810,14 @@ class GuiMainMenu(QMenuBar):
798
810
  # Format > Separator
799
811
  self.fmtMenu.addSeparator()
800
812
 
801
- # Format > Replace Single Quotes
802
- self.aFmtReplSng = self.fmtMenu.addAction(self.tr("Convert Single Quotes"))
813
+ # Format > Replace Straight Single Quotes
814
+ self.aFmtReplSng = self.fmtMenu.addAction(self.tr("Replace Straight Single Quotes"))
803
815
  self.aFmtReplSng.triggered.connect(
804
816
  lambda: self.requestDocAction.emit(nwDocAction.REPL_SNG)
805
817
  )
806
818
 
807
- # Format > Replace Double Quotes
808
- self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("Convert Double Quotes"))
819
+ # Format > Replace Straight Double Quotes
820
+ self.aFmtReplDbl = self.fmtMenu.addAction(self.tr("Replace Straight Double Quotes"))
809
821
  self.aFmtReplDbl.triggered.connect(
810
822
  lambda: self.requestDocAction.emit(nwDocAction.REPL_DBL)
811
823
  )
@@ -850,6 +862,14 @@ class GuiMainMenu(QMenuBar):
850
862
  self.aReplaceNext.setShortcut("Ctrl+Shift+1")
851
863
  self.aReplaceNext.triggered.connect(lambda: self.mainGui.docEditor.replaceNext())
852
864
 
865
+ # Search > Separator
866
+ self.srcMenu.addSeparator()
867
+
868
+ # Search > Find in Project
869
+ self.aFindProj = self.srcMenu.addAction(self.tr("Find in Project"))
870
+ self.aFindProj.setShortcut("Ctrl+Shift+F")
871
+ self.aFindProj.triggered.connect(lambda: self.requestViewChange.emit(nwView.SEARCH))
872
+
853
873
  return
854
874
 
855
875
  def _buildToolsMenu(self) -> None: