novelWriter 2.2rc1__py3-none-any.whl → 2.3__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 (162) hide show
  1. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/METADATA +1 -1
  2. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/RECORD +149 -132
  3. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/WHEEL +1 -1
  4. novelWriter-2.3.dist-info/entry_points.txt +2 -0
  5. novelwriter/__init__.py +11 -6
  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_zh_CN.qm +0 -0
  15. novelwriter/assets/i18n/project_de_DE.json +1 -0
  16. novelwriter/assets/i18n/project_en_US.json +1 -0
  17. novelwriter/assets/i18n/project_es_419.json +11 -0
  18. novelwriter/assets/i18n/project_fr_FR.json +11 -0
  19. novelwriter/assets/i18n/project_it_IT.json +11 -0
  20. novelwriter/assets/i18n/project_ja_JP.json +2 -1
  21. novelwriter/assets/i18n/project_nb_NO.json +1 -0
  22. novelwriter/assets/i18n/project_nl_NL.json +11 -0
  23. novelwriter/assets/i18n/project_pt_BR.json +11 -0
  24. novelwriter/assets/i18n/project_zh_CN.json +11 -0
  25. novelwriter/assets/icons/typicons_dark/icons.conf +11 -2
  26. novelwriter/assets/icons/typicons_dark/mixed_document-new.svg +6 -0
  27. novelwriter/assets/icons/typicons_dark/mixed_import.svg +5 -0
  28. novelwriter/assets/icons/typicons_dark/nw_tb-bold-md.svg +4 -0
  29. novelwriter/assets/icons/typicons_dark/nw_tb-bold.svg +3 -1
  30. novelwriter/assets/icons/typicons_dark/nw_tb-italic-md.svg +4 -0
  31. novelwriter/assets/icons/typicons_dark/nw_tb-italic.svg +3 -1
  32. novelwriter/assets/icons/typicons_dark/nw_tb-strike-md.svg +4 -0
  33. novelwriter/assets/icons/typicons_dark/nw_tb-strike.svg +3 -1
  34. novelwriter/assets/icons/typicons_dark/nw_tb-subscript.svg +4 -2
  35. novelwriter/assets/icons/typicons_dark/nw_tb-superscript.svg +4 -2
  36. novelwriter/assets/icons/typicons_dark/nw_tb-underline.svg +4 -2
  37. novelwriter/assets/icons/typicons_dark/typ_document-add.svg +4 -0
  38. novelwriter/assets/icons/typicons_dark/typ_document.svg +4 -0
  39. novelwriter/assets/icons/typicons_dark/typ_th-dot-more.svg +4 -0
  40. novelwriter/assets/icons/typicons_dark/typ_th-list.svg +9 -0
  41. novelwriter/assets/icons/typicons_light/icons.conf +11 -2
  42. novelwriter/assets/icons/typicons_light/mixed_document-new.svg +6 -0
  43. novelwriter/assets/icons/typicons_light/mixed_import.svg +5 -0
  44. novelwriter/assets/icons/typicons_light/nw_tb-bold-md.svg +4 -0
  45. novelwriter/assets/icons/typicons_light/nw_tb-bold.svg +3 -1
  46. novelwriter/assets/icons/typicons_light/nw_tb-italic-md.svg +4 -0
  47. novelwriter/assets/icons/typicons_light/nw_tb-italic.svg +3 -1
  48. novelwriter/assets/icons/typicons_light/nw_tb-strike-md.svg +4 -0
  49. novelwriter/assets/icons/typicons_light/nw_tb-strike.svg +3 -1
  50. novelwriter/assets/icons/typicons_light/nw_tb-subscript.svg +4 -2
  51. novelwriter/assets/icons/typicons_light/nw_tb-superscript.svg +4 -2
  52. novelwriter/assets/icons/typicons_light/nw_tb-underline.svg +4 -2
  53. novelwriter/assets/icons/typicons_light/typ_document-add.svg +4 -0
  54. novelwriter/assets/icons/typicons_light/typ_document.svg +4 -0
  55. novelwriter/assets/icons/typicons_light/typ_th-dot-more.svg +4 -0
  56. novelwriter/assets/icons/typicons_light/typ_th-list.svg +9 -0
  57. novelwriter/assets/images/novelwriter-text-dark.svg +4 -0
  58. novelwriter/assets/images/novelwriter-text-light.svg +4 -0
  59. novelwriter/assets/images/welcome-dark.jpg +0 -0
  60. novelwriter/assets/images/welcome-light.jpg +0 -0
  61. novelwriter/assets/manual.pdf +0 -0
  62. novelwriter/assets/sample.zip +0 -0
  63. novelwriter/assets/syntax/cyberpunk_night.conf +26 -0
  64. novelwriter/assets/syntax/default_dark.conf +1 -0
  65. novelwriter/assets/syntax/default_light.conf +1 -0
  66. novelwriter/assets/syntax/grey_dark.conf +1 -0
  67. novelwriter/assets/syntax/grey_light.conf +1 -0
  68. novelwriter/assets/syntax/light_owl.conf +1 -0
  69. novelwriter/assets/syntax/night_owl.conf +1 -0
  70. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  71. novelwriter/assets/syntax/solarized_light.conf +1 -0
  72. novelwriter/assets/syntax/tango.conf +23 -0
  73. novelwriter/assets/syntax/tomorrow.conf +1 -0
  74. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  75. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  76. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  77. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  78. novelwriter/assets/text/credits_en.htm +4 -2
  79. novelwriter/assets/themes/cyberpunk_night.conf +29 -0
  80. novelwriter/assets/themes/default_dark.conf +2 -2
  81. novelwriter/assets/themes/default_light.conf +2 -2
  82. novelwriter/common.py +64 -66
  83. novelwriter/config.py +39 -44
  84. novelwriter/constants.py +39 -17
  85. novelwriter/core/buildsettings.py +8 -8
  86. novelwriter/core/coretools.py +198 -157
  87. novelwriter/core/docbuild.py +7 -4
  88. novelwriter/core/document.py +7 -7
  89. novelwriter/core/index.py +90 -57
  90. novelwriter/core/item.py +23 -5
  91. novelwriter/core/options.py +11 -10
  92. novelwriter/core/project.py +73 -47
  93. novelwriter/core/projectdata.py +3 -16
  94. novelwriter/core/projectxml.py +14 -42
  95. novelwriter/core/sessions.py +4 -3
  96. novelwriter/core/spellcheck.py +6 -4
  97. novelwriter/core/status.py +5 -4
  98. novelwriter/core/storage.py +183 -141
  99. novelwriter/core/tohtml.py +6 -4
  100. novelwriter/core/tokenizer.py +110 -83
  101. novelwriter/core/tomd.py +2 -2
  102. novelwriter/core/toodt.py +41 -31
  103. novelwriter/core/tree.py +5 -4
  104. novelwriter/dialogs/about.py +88 -179
  105. novelwriter/dialogs/docmerge.py +30 -20
  106. novelwriter/dialogs/docsplit.py +33 -22
  107. novelwriter/dialogs/editlabel.py +20 -8
  108. novelwriter/dialogs/preferences.py +562 -725
  109. novelwriter/dialogs/{projsettings.py → projectsettings.py} +301 -270
  110. novelwriter/dialogs/quotes.py +47 -36
  111. novelwriter/dialogs/wordlist.py +128 -59
  112. novelwriter/enum.py +25 -22
  113. novelwriter/error.py +2 -2
  114. novelwriter/extensions/circularprogress.py +12 -12
  115. novelwriter/extensions/configlayout.py +185 -146
  116. novelwriter/extensions/{wheeleventfilter.py → eventfilters.py} +15 -5
  117. novelwriter/extensions/modified.py +81 -0
  118. novelwriter/extensions/novelselector.py +27 -13
  119. novelwriter/extensions/pagedsidebar.py +15 -20
  120. novelwriter/extensions/simpleprogress.py +8 -9
  121. novelwriter/extensions/statusled.py +9 -9
  122. novelwriter/extensions/switch.py +32 -64
  123. novelwriter/extensions/switchbox.py +2 -7
  124. novelwriter/extensions/versioninfo.py +153 -0
  125. novelwriter/gui/doceditor.py +250 -214
  126. novelwriter/gui/dochighlight.py +66 -94
  127. novelwriter/gui/docviewer.py +71 -98
  128. novelwriter/gui/docviewerpanel.py +140 -47
  129. novelwriter/gui/editordocument.py +3 -3
  130. novelwriter/gui/itemdetails.py +9 -9
  131. novelwriter/gui/mainmenu.py +47 -47
  132. novelwriter/gui/noveltree.py +53 -61
  133. novelwriter/gui/outline.py +100 -76
  134. novelwriter/gui/projtree.py +246 -112
  135. novelwriter/gui/sidebar.py +9 -8
  136. novelwriter/gui/statusbar.py +49 -7
  137. novelwriter/gui/theme.py +74 -76
  138. novelwriter/guimain.py +175 -330
  139. novelwriter/shared.py +68 -30
  140. novelwriter/tools/dictionaries.py +7 -8
  141. novelwriter/tools/lipsum.py +34 -28
  142. novelwriter/tools/manusbuild.py +3 -4
  143. novelwriter/tools/manuscript.py +25 -32
  144. novelwriter/tools/manussettings.py +194 -225
  145. novelwriter/tools/noveldetails.py +525 -0
  146. novelwriter/tools/welcome.py +819 -0
  147. novelwriter/tools/writingstats.py +26 -13
  148. novelWriter-2.2rc1.dist-info/entry_points.txt +0 -5
  149. novelwriter/assets/icons/typicons_dark/nw_tb-markdown.svg +0 -8
  150. novelwriter/assets/icons/typicons_dark/nw_tb-shortcode.svg +0 -8
  151. novelwriter/assets/icons/typicons_light/nw_tb-markdown.svg +0 -8
  152. novelwriter/assets/icons/typicons_light/nw_tb-shortcode.svg +0 -8
  153. novelwriter/assets/images/wizard-back.jpg +0 -0
  154. novelwriter/assets/text/gplv3_en.htm +0 -641
  155. novelwriter/assets/text/release_notes.htm +0 -17
  156. novelwriter/dialogs/projdetails.py +0 -525
  157. novelwriter/dialogs/projload.py +0 -298
  158. novelwriter/dialogs/updates.py +0 -182
  159. novelwriter/extensions/pageddialog.py +0 -130
  160. novelwriter/tools/projwizard.py +0 -478
  161. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/LICENSE.md +0 -0
  162. {novelWriter-2.2rc1.dist-info → novelWriter-2.3.dist-info}/top_level.txt +0 -0
@@ -3,10 +3,11 @@ novelWriter – GUI Syntax Highlighter
3
3
  ====================================
4
4
 
5
5
  File History:
6
- Created: 2019-04-06 [0.0.1]
6
+ Created: 2019-04-06 [0.0.1] GuiDocHighlighter
7
+ Created: 2023-09-10 [2.2b1] TextBlockData
7
8
 
8
9
  This file is a part of novelWriter
9
- Copyright 2018–2023, Veronica Berglyd Olsen
10
+ Copyright 2018–2024, Veronica Berglyd Olsen
10
11
 
11
12
  This program is free software: you can redistribute it and/or modify
12
13
  it under the terms of the GNU General Public License as published by
@@ -34,10 +35,10 @@ from PyQt5.QtGui import (
34
35
  )
35
36
 
36
37
  from novelwriter import CONFIG, SHARED
38
+ from novelwriter.enum import nwComment
37
39
  from novelwriter.common import checkInt
38
40
  from novelwriter.constants import nwRegEx, nwUnicode
39
41
  from novelwriter.core.index import processComment
40
- from novelwriter.enum import nwComment
41
42
 
42
43
  logger = logging.getLogger(__name__)
43
44
 
@@ -47,6 +48,8 @@ SPELLRX.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
47
48
 
48
49
  class GuiDocHighlighter(QSyntaxHighlighter):
49
50
 
51
+ __slots__ = ("_tItem", "_tHandle", "_spellCheck", "_spellErr", "_hRules", "_hStyles")
52
+
50
53
  BLOCK_NONE = 0
51
54
  BLOCK_TEXT = 1
52
55
  BLOCK_META = 2
@@ -57,29 +60,14 @@ class GuiDocHighlighter(QSyntaxHighlighter):
57
60
 
58
61
  logger.debug("Create: GuiDocHighlighter")
59
62
 
60
- self._tItem = None
61
- self._tHandle = None
63
+ self._tHandle = None
64
+ self._isInactive = False
62
65
  self._spellCheck = False
66
+ self._spellErr = QTextCharFormat()
63
67
 
64
68
  self._hRules: list[tuple[str, dict]] = []
65
69
  self._hStyles: dict[str, QTextCharFormat] = {}
66
70
 
67
- self._colHead = QColor(0, 0, 0)
68
- self._colHeadH = QColor(0, 0, 0)
69
- self._colEmph = QColor(0, 0, 0)
70
- self._colDialN = QColor(0, 0, 0)
71
- self._colDialD = QColor(0, 0, 0)
72
- self._colDialS = QColor(0, 0, 0)
73
- self._colHidden = QColor(0, 0, 0)
74
- self._colCode = QColor(0, 0, 0)
75
- self._colKey = QColor(0, 0, 0)
76
- self._colVal = QColor(0, 0, 0)
77
- self._colSpell = QColor(0, 0, 0)
78
- self._colError = QColor(0, 0, 0)
79
- self._colRepTag = QColor(0, 0, 0)
80
- self._colMod = QColor(0, 0, 0)
81
- self._colBreak = QColor(0, 0, 0)
82
-
83
71
  self.initHighlighter()
84
72
 
85
73
  logger.debug("Ready: GuiDocHighlighter")
@@ -92,54 +80,43 @@ class GuiDocHighlighter(QSyntaxHighlighter):
92
80
  """
93
81
  logger.debug("Setting up highlighting rules")
94
82
 
95
- self._colHead = QColor(*SHARED.theme.colHead)
96
- self._colHeadH = QColor(*SHARED.theme.colHeadH)
97
- self._colDialN = QColor(*SHARED.theme.colDialN)
98
- self._colDialD = QColor(*SHARED.theme.colDialD)
99
- self._colDialS = QColor(*SHARED.theme.colDialS)
100
- self._colHidden = QColor(*SHARED.theme.colHidden)
101
- self._colCode = QColor(*SHARED.theme.colCode)
102
- self._colKey = QColor(*SHARED.theme.colKey)
103
- self._colVal = QColor(*SHARED.theme.colVal)
104
- self._colSpell = QColor(*SHARED.theme.colSpell)
105
- self._colError = QColor(*SHARED.theme.colError)
106
- self._colRepTag = QColor(*SHARED.theme.colRepTag)
107
- self._colMod = QColor(*SHARED.theme.colMod)
108
- self._colBreak = QColor(*SHARED.theme.colEmph)
109
- self._colBreak.setAlpha(64)
110
-
111
- self._colEmph = None
112
- if CONFIG.highlightEmph:
113
- self._colEmph = QColor(*SHARED.theme.colEmph)
83
+ colEmph = SHARED.theme.colEmph if CONFIG.highlightEmph else None
84
+ colBreak = QColor(SHARED.theme.colEmph)
85
+ colBreak.setAlpha(64)
114
86
 
87
+ self._hRules = []
115
88
  self._hStyles = {
116
- "header1": self._makeFormat(self._colHead, "bold", 1.8),
117
- "header2": self._makeFormat(self._colHead, "bold", 1.6),
118
- "header3": self._makeFormat(self._colHead, "bold", 1.4),
119
- "header4": self._makeFormat(self._colHead, "bold", 1.2),
120
- "header1h": self._makeFormat(self._colHeadH, "bold", 1.8),
121
- "header2h": self._makeFormat(self._colHeadH, "bold", 1.6),
122
- "header3h": self._makeFormat(self._colHeadH, "bold", 1.4),
123
- "header4h": self._makeFormat(self._colHeadH, "bold", 1.2),
124
- "bold": self._makeFormat(self._colEmph, "bold"),
125
- "italic": self._makeFormat(self._colEmph, "italic"),
126
- "strike": self._makeFormat(self._colHidden, "strike"),
127
- "mspaces": self._makeFormat(self._colError, "errline"),
128
- "nobreak": self._makeFormat(self._colBreak, "background"),
129
- "dialogue1": self._makeFormat(self._colDialN),
130
- "dialogue2": self._makeFormat(self._colDialD),
131
- "dialogue3": self._makeFormat(self._colDialS),
132
- "replace": self._makeFormat(self._colRepTag),
133
- "hidden": self._makeFormat(self._colHidden),
134
- "code": self._makeFormat(self._colCode),
135
- "keyword": self._makeFormat(self._colKey),
136
- "modifier": self._makeFormat(self._colMod),
137
- "value": self._makeFormat(self._colVal, "underline"),
138
- "codevalue": self._makeFormat(self._colVal),
89
+ "header1": self._makeFormat(SHARED.theme.colHead, "bold", 1.8),
90
+ "header2": self._makeFormat(SHARED.theme.colHead, "bold", 1.6),
91
+ "header3": self._makeFormat(SHARED.theme.colHead, "bold", 1.4),
92
+ "header4": self._makeFormat(SHARED.theme.colHead, "bold", 1.2),
93
+ "header1h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.8),
94
+ "header2h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.6),
95
+ "header3h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.4),
96
+ "header4h": self._makeFormat(SHARED.theme.colHeadH, "bold", 1.2),
97
+ "bold": self._makeFormat(colEmph, "bold"),
98
+ "italic": self._makeFormat(colEmph, "italic"),
99
+ "strike": self._makeFormat(SHARED.theme.colHidden, "strike"),
100
+ "mspaces": self._makeFormat(SHARED.theme.colError, "errline"),
101
+ "nobreak": self._makeFormat(colBreak, "background"),
102
+ "dialogue1": self._makeFormat(SHARED.theme.colDialN),
103
+ "dialogue2": self._makeFormat(SHARED.theme.colDialD),
104
+ "dialogue3": self._makeFormat(SHARED.theme.colDialS),
105
+ "replace": self._makeFormat(SHARED.theme.colRepTag),
106
+ "hidden": self._makeFormat(SHARED.theme.colHidden),
107
+ "code": self._makeFormat(SHARED.theme.colCode),
108
+ "keyword": self._makeFormat(SHARED.theme.colKey),
109
+ "modifier": self._makeFormat(SHARED.theme.colMod),
110
+ "value": self._makeFormat(SHARED.theme.colVal),
111
+ "optional": self._makeFormat(SHARED.theme.colOpt),
112
+ "codevalue": self._makeFormat(SHARED.theme.colVal),
139
113
  "codeinval": self._makeFormat(None, "errline"),
140
114
  }
141
115
 
142
- self._hRules = []
116
+ # Cache Spell Error Format
117
+ self._spellErr = QTextCharFormat()
118
+ self._spellErr.setUnderlineColor(SHARED.theme.colSpell)
119
+ self._spellErr.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
143
120
 
144
121
  # Multiple or Trailing Spaces
145
122
  if CONFIG.showMultiSpaces:
@@ -261,11 +238,10 @@ class GuiDocHighlighter(QSyntaxHighlighter):
261
238
  def setHandle(self, tHandle: str) -> None:
262
239
  """Set the handle of the currently highlighted document."""
263
240
  self._tHandle = tHandle
264
- self._tItem = SHARED.project.tree[tHandle]
265
- logger.debug(
266
- "Syntax highlighter %s for item '%s'",
267
- "enabled" if self._tItem else "disabled", tHandle
241
+ self._isInactive = (
242
+ item.isInactiveClass() if (item := SHARED.project.tree[tHandle]) else False
268
243
  )
244
+ logger.debug("Syntax highlighter enabled for item '%s'", tHandle)
269
245
  return
270
246
 
271
247
  ##
@@ -280,9 +256,9 @@ class GuiDocHighlighter(QSyntaxHighlighter):
280
256
  nBlocks = qDoc.blockCount()
281
257
  tStart = time()
282
258
  for i in range(nBlocks):
283
- theBlock = qDoc.findBlockByNumber(i)
284
- if theBlock.userState() & cType > 0:
285
- self.rehighlightBlock(theBlock)
259
+ block = qDoc.findBlockByNumber(i)
260
+ if block.userState() & cType > 0:
261
+ self.rehighlightBlock(block)
286
262
  logger.debug("Document highlighted in %.3f ms" % (1000*(time() - tStart)))
287
263
  return
288
264
 
@@ -302,24 +278,23 @@ class GuiDocHighlighter(QSyntaxHighlighter):
302
278
 
303
279
  if text.startswith("@"): # Keywords and commands
304
280
  self.setCurrentBlockState(self.BLOCK_META)
305
- if self._tItem:
306
- pIndex = SHARED.project.index
307
- isValid, theBits, thePos = pIndex.scanThis(text)
308
- isGood = pIndex.checkThese(theBits, self._tItem)
309
- if isValid:
310
- for n, theBit in enumerate(theBits):
311
- xPos = thePos[n]
312
- xLen = len(theBit)
313
- if isGood[n]:
314
- if n == 0:
315
- self.setFormat(xPos, xLen, self._hStyles["keyword"])
316
- else:
317
- self.setFormat(xPos, xLen, self._hStyles["value"])
318
- else:
319
- kwFmt = self.format(xPos)
320
- kwFmt.setUnderlineColor(self._colError)
321
- kwFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
322
- self.setFormat(xPos, xLen, kwFmt)
281
+ index = SHARED.project.index
282
+ isValid, bits, pos = index.scanThis(text)
283
+ isGood = index.checkThese(bits, self._tHandle)
284
+ if isValid:
285
+ for n, bit in enumerate(bits):
286
+ xPos = pos[n]
287
+ xLen = len(bit)
288
+ if n == 0 and isGood[n]:
289
+ self.setFormat(xPos, xLen, self._hStyles["keyword"])
290
+ elif isGood[n] and not self._isInactive:
291
+ one, two = index.parseValue(bit)
292
+ self.setFormat(xPos, len(one), self._hStyles["value"])
293
+ if two:
294
+ yPos = xPos + len(bit) - len(two)
295
+ self.setFormat(yPos, len(two), self._hStyles["optional"])
296
+ elif not self._isInactive:
297
+ self.setFormat(xPos, xLen, self._hStyles["codeinval"])
323
298
 
324
299
  # We never want to run the spell checker on keyword/values,
325
300
  # so we force a return here
@@ -401,8 +376,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
401
376
  for xPos, xLen in data.spellCheck(text):
402
377
  for x in range(xPos, xPos+xLen):
403
378
  spFmt = self.format(x)
404
- spFmt.setUnderlineColor(self._colSpell)
405
- spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
379
+ spFmt.merge(self._spellErr)
406
380
  self.setFormat(x, 1, spFmt)
407
381
 
408
382
  return
@@ -430,10 +404,8 @@ class GuiDocHighlighter(QSyntaxHighlighter):
430
404
  if "strike" in styles:
431
405
  charFormat.setFontStrikeOut(True)
432
406
  if "errline" in styles:
433
- charFormat.setUnderlineColor(self._colError)
407
+ charFormat.setUnderlineColor(SHARED.theme.colError)
434
408
  charFormat.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
435
- if "underline" in styles:
436
- charFormat.setFontUnderline(True)
437
409
  if "background" in styles and color is not None:
438
410
  charFormat.setBackground(QBrush(color, Qt.SolidPattern))
439
411
 
@@ -9,7 +9,7 @@ Created: 2020-06-09 [0.8] GuiDocViewFooter
9
9
  Created: 2020-09-08 [1.0b1] GuiDocViewHistory
10
10
 
11
11
  This file is a part of novelWriter
12
- Copyright 2018–2023, Veronica Berglyd Olsen
12
+ Copyright 2018–2024, Veronica Berglyd Olsen
13
13
 
14
14
  This program is free software: you can redistribute it and/or modify
15
15
  it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ from typing import TYPE_CHECKING
33
33
 
34
34
  from PyQt5.QtCore import pyqtSignal, pyqtSlot, QPoint, QSize, Qt, QUrl
35
35
  from PyQt5.QtGui import (
36
- QColor, QCursor, QFont, QMouseEvent, QPalette, QResizeEvent, QTextCursor,
36
+ QCursor, QFont, QMouseEvent, QPalette, QResizeEvent, QTextCursor,
37
37
  QTextOption
38
38
  )
39
39
  from PyQt5.QtWidgets import (
@@ -46,7 +46,7 @@ from novelwriter.enum import nwItemType, nwDocAction, nwDocMode
46
46
  from novelwriter.error import logException
47
47
  from novelwriter.constants import nwUnicode
48
48
  from novelwriter.core.tohtml import ToHtml
49
- from novelwriter.extensions.wheeleventfilter import WheelEventFilter
49
+ from novelwriter.extensions.eventfilters import WheelEventFilter
50
50
 
51
51
  if TYPE_CHECKING: # pragma: no cover
52
52
  from novelwriter.guimain import GuiMain
@@ -142,21 +142,21 @@ class GuiDocViewer(QTextBrowser):
142
142
  self._makeStyleSheet()
143
143
 
144
144
  # Set Font
145
- textFont = QFont()
146
- textFont.setFamily(CONFIG.textFont)
147
- textFont.setPointSize(CONFIG.textSize)
148
- self.setFont(textFont)
145
+ font = QFont()
146
+ font.setFamily(CONFIG.textFont)
147
+ font.setPointSize(CONFIG.textSize)
148
+ self.setFont(font)
149
149
 
150
150
  # Set the widget colours to match syntax theme
151
151
  mainPalette = self.palette()
152
- mainPalette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
153
- mainPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
154
- mainPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
152
+ mainPalette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
153
+ mainPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
154
+ mainPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
155
155
  self.setPalette(mainPalette)
156
156
 
157
157
  docPalette = self.viewport().palette()
158
- docPalette.setColor(QPalette.ColorRole.Base, QColor(*SHARED.theme.colBack))
159
- docPalette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
158
+ docPalette.setColor(QPalette.ColorRole.Base, SHARED.theme.colBack)
159
+ docPalette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
160
160
  self.viewport().setPalette(docPalette)
161
161
 
162
162
  self.docHeader.matchColours()
@@ -164,10 +164,10 @@ class GuiDocViewer(QTextBrowser):
164
164
 
165
165
  # Set default text margins
166
166
  self.document().setDocumentMargin(0)
167
- theOpt = QTextOption()
167
+ options = QTextOption()
168
168
  if CONFIG.doJustify:
169
- theOpt.setAlignment(Qt.AlignmentFlag.AlignJustify)
170
- self.document().setDefaultTextOption(theOpt)
169
+ options.setAlignment(Qt.AlignmentFlag.AlignJustify)
170
+ self.document().setDefaultTextOption(options)
171
171
 
172
172
  # Scroll bars
173
173
  if CONFIG.hideVScroll:
@@ -229,7 +229,7 @@ class GuiDocViewer(QTextBrowser):
229
229
  self.setDocumentTitle(tHandle)
230
230
 
231
231
  # Replace tabs before setting the HTML, and then put them back in
232
- self.setHtml(aDoc.theResult.replace("\t", "!!tab!!"))
232
+ self.setHtml(aDoc.result.replace("\t", "!!tab!!"))
233
233
  while self.find("!!tab!!"):
234
234
  self.textCursor().insertText("\t")
235
235
 
@@ -367,9 +367,9 @@ class GuiDocViewer(QTextBrowser):
367
367
  link = url.url()
368
368
  logger.debug("Clicked link: '%s'", link)
369
369
  if len(link) > 0:
370
- theBits = link.split("=")
371
- if len(theBits) == 2:
372
- self.loadDocumentTagRequest.emit(theBits[1], nwDocMode.VIEW)
370
+ bits = link.split("=")
371
+ if len(bits) == 2:
372
+ self.loadDocumentTagRequest.emit(bits[1], nwDocMode.VIEW)
373
373
  return
374
374
 
375
375
  @pyqtSlot("QPoint")
@@ -378,33 +378,34 @@ class GuiDocViewer(QTextBrowser):
378
378
  userCursor = self.textCursor()
379
379
  userSelection = userCursor.hasSelection()
380
380
 
381
- mnuContext = QMenu(self)
381
+ ctxMenu = QMenu(self)
382
382
 
383
383
  if userSelection:
384
- mnuCopy = QAction(self.tr("Copy"), mnuContext)
384
+ mnuCopy = QAction(self.tr("Copy"), ctxMenu)
385
385
  mnuCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
386
- mnuContext.addAction(mnuCopy)
386
+ ctxMenu.addAction(mnuCopy)
387
387
 
388
- mnuContext.addSeparator()
388
+ ctxMenu.addSeparator()
389
389
 
390
- mnuSelAll = QAction(self.tr("Select All"), mnuContext)
390
+ mnuSelAll = QAction(self.tr("Select All"), ctxMenu)
391
391
  mnuSelAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
392
- mnuContext.addAction(mnuSelAll)
392
+ ctxMenu.addAction(mnuSelAll)
393
393
 
394
- mnuSelWord = QAction(self.tr("Select Word"), mnuContext)
394
+ mnuSelWord = QAction(self.tr("Select Word"), ctxMenu)
395
395
  mnuSelWord.triggered.connect(
396
396
  lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, point)
397
397
  )
398
- mnuContext.addAction(mnuSelWord)
398
+ ctxMenu.addAction(mnuSelWord)
399
399
 
400
- mnuSelPara = QAction(self.tr("Select Paragraph"), mnuContext)
400
+ mnuSelPara = QAction(self.tr("Select Paragraph"), ctxMenu)
401
401
  mnuSelPara.triggered.connect(
402
402
  lambda: self._makePosSelection(QTextCursor.SelectionType.BlockUnderCursor, point)
403
403
  )
404
- mnuContext.addAction(mnuSelPara)
404
+ ctxMenu.addAction(mnuSelPara)
405
405
 
406
406
  # Open the context menu
407
- mnuContext.exec_(self.viewport().mapToGlobal(point))
407
+ ctxMenu.exec_(self.viewport().mapToGlobal(point))
408
+ ctxMenu.deleteLater()
408
409
 
409
410
  return
410
411
 
@@ -460,9 +461,16 @@ class GuiDocViewer(QTextBrowser):
460
461
 
461
462
  def _makeStyleSheet(self) -> None:
462
463
  """Generate an appropriate style sheet for the document viewer,
463
- based on the current syntax highlighter theme,
464
+ based on the current syntax highlighter theme.
464
465
  """
465
- pTheme = SHARED.theme
466
+ colText = SHARED.theme.colText
467
+ colHead = SHARED.theme.colHead
468
+ colVals = SHARED.theme.colVal
469
+ colEmph = SHARED.theme.colEmph
470
+ colKeys = SHARED.theme.colKey
471
+ colHide = SHARED.theme.colHidden
472
+ colMods = SHARED.theme.colMod
473
+ colOpts = SHARED.theme.colOpt
466
474
  styleSheet = (
467
475
  "body {{"
468
476
  " color: rgb({tColR}, {tColG}, {tColB});"
@@ -479,6 +487,9 @@ class GuiDocViewer(QTextBrowser):
479
487
  ".tags {{"
480
488
  " color: rgb({kColR}, {kColG}, {kColB});"
481
489
  "}}\n"
490
+ ".optional {{"
491
+ " color: rgb({oColR}, {oColG}, {oColB});"
492
+ "}}\n"
482
493
  ".comment {{"
483
494
  " color: rgb({cColR}, {cColG}, {cColB});"
484
495
  "}}\n"
@@ -489,27 +500,14 @@ class GuiDocViewer(QTextBrowser):
489
500
  " text-align: center;"
490
501
  "}}\n"
491
502
  ).format(
492
- tColR=pTheme.colText[0],
493
- tColG=pTheme.colText[1],
494
- tColB=pTheme.colText[2],
495
- hColR=pTheme.colHead[0],
496
- hColG=pTheme.colHead[1],
497
- hColB=pTheme.colHead[2],
498
- aColR=pTheme.colVal[0],
499
- aColG=pTheme.colVal[1],
500
- aColB=pTheme.colVal[2],
501
- eColR=pTheme.colEmph[0],
502
- eColG=pTheme.colEmph[1],
503
- eColB=pTheme.colEmph[2],
504
- kColR=pTheme.colKey[0],
505
- kColG=pTheme.colKey[1],
506
- kColB=pTheme.colKey[2],
507
- cColR=pTheme.colHidden[0],
508
- cColG=pTheme.colHidden[1],
509
- cColB=pTheme.colHidden[2],
510
- mColR=pTheme.colMod[0],
511
- mColG=pTheme.colMod[1],
512
- mColB=pTheme.colMod[2],
503
+ tColR=colText.red(), tColG=colText.green(), tColB=colText.blue(),
504
+ hColR=colHead.red(), hColG=colHead.green(), hColB=colHead.blue(),
505
+ aColR=colVals.red(), aColG=colVals.green(), aColB=colVals.blue(),
506
+ eColR=colEmph.red(), eColG=colEmph.green(), eColB=colEmph.blue(),
507
+ kColR=colKeys.red(), kColG=colKeys.green(), kColB=colKeys.blue(),
508
+ cColR=colHide.red(), cColG=colHide.green(), cColB=colHide.blue(),
509
+ mColR=colMods.red(), mColG=colMods.green(), mColB=colMods.blue(),
510
+ oColR=colOpts.red(), oColG=colOpts.green(), oColB=colOpts.blue(),
513
511
  )
514
512
  self.document().setDefaultStyleSheet(styleSheet)
515
513
 
@@ -743,10 +741,11 @@ class GuiDocViewHeader(QWidget):
743
741
  self.refreshButton.setIcon(SHARED.theme.getIcon("refresh"))
744
742
  self.closeButton.setIcon(SHARED.theme.getIcon("close"))
745
743
 
744
+ colText = SHARED.theme.colText
746
745
  buttonStyle = (
747
746
  "QToolButton {{border: none; background: transparent;}} "
748
- "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
749
- ).format(*SHARED.theme.colText)
747
+ "QToolButton:hover {{border: none; background: rgba({0}, {1}, {2}, 0.2);}}"
748
+ ).format(colText.red(), colText.green(), colText.blue())
750
749
 
751
750
  self.backButton.setStyleSheet(buttonStyle)
752
751
  self.forwardButton.setStyleSheet(buttonStyle)
@@ -762,9 +761,9 @@ class GuiDocViewHeader(QWidget):
762
761
  theme rather than the main GUI.
763
762
  """
764
763
  palette = QPalette()
765
- palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
766
- palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
767
- palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
764
+ palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
765
+ palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
766
+ palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
768
767
  self.setPalette(palette)
769
768
  self.docTitle.setPalette(palette)
770
769
  return
@@ -861,8 +860,7 @@ class GuiDocViewFooter(QWidget):
861
860
  self._docHandle = None
862
861
 
863
862
  fPx = int(0.9*SHARED.theme.fontPixelSize)
864
- bSp = CONFIG.pxInt(2)
865
- hSp = CONFIG.pxInt(8)
863
+ hSp = CONFIG.pxInt(4)
866
864
 
867
865
  # Main Widget Settings
868
866
  self.setContentsMargins(0, 0, 0, 0)
@@ -872,64 +870,41 @@ class GuiDocViewFooter(QWidget):
872
870
  self.showHide = QToolButton(self)
873
871
  self.showHide.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
874
872
  self.showHide.setIconSize(QSize(fPx, fPx))
875
- self.showHide.setFixedSize(QSize(fPx, fPx))
876
873
  self.showHide.clicked.connect(lambda: self.docViewer.togglePanelVisibility.emit())
877
874
  self.showHide.setToolTip(self.tr("Show/Hide Viewer Panel"))
878
875
 
879
876
  # Show Comments
880
877
  self.showComments = QToolButton(self)
878
+ self.showComments.setText(self.tr("Comments"))
881
879
  self.showComments.setCheckable(True)
882
880
  self.showComments.setChecked(CONFIG.viewComments)
883
- self.showComments.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
881
+ self.showComments.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
884
882
  self.showComments.setIconSize(QSize(fPx, fPx))
885
- self.showComments.setFixedSize(QSize(fPx, fPx))
886
883
  self.showComments.toggled.connect(self._doToggleComments)
887
884
  self.showComments.setToolTip(self.tr("Show Comments"))
888
885
 
889
886
  # Show Synopsis
890
887
  self.showSynopsis = QToolButton(self)
888
+ self.showSynopsis.setText(self.tr("Synopsis"))
891
889
  self.showSynopsis.setCheckable(True)
892
890
  self.showSynopsis.setChecked(CONFIG.viewSynopsis)
893
- self.showSynopsis.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
891
+ self.showSynopsis.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
894
892
  self.showSynopsis.setIconSize(QSize(fPx, fPx))
895
- self.showSynopsis.setFixedSize(QSize(fPx, fPx))
896
893
  self.showSynopsis.toggled.connect(self._doToggleSynopsis)
897
894
  self.showSynopsis.setToolTip(self.tr("Show Synopsis Comments"))
898
895
 
899
- # Labels
900
- self.lblComments = QLabel(self.tr("Comments"))
901
- self.lblComments.setBuddy(self.showComments)
902
- self.lblComments.setIndent(0)
903
- self.lblComments.setMargin(0)
904
- self.lblComments.setContentsMargins(0, 0, 0, 0)
905
- self.lblComments.setAutoFillBackground(True)
906
- self.lblComments.setFixedHeight(fPx)
907
- self.lblComments.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
908
-
909
- self.lblSynopsis = QLabel(self.tr("Synopsis"))
910
- self.lblSynopsis.setBuddy(self.showSynopsis)
911
- self.lblSynopsis.setIndent(0)
912
- self.lblSynopsis.setMargin(0)
913
- self.lblSynopsis.setContentsMargins(0, 0, 0, 0)
914
- self.lblSynopsis.setAutoFillBackground(True)
915
- self.lblSynopsis.setFixedHeight(fPx)
916
- self.lblSynopsis.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
917
-
918
896
  lblFont = self.font()
919
897
  lblFont.setPointSizeF(0.9*SHARED.theme.fontPointSize)
920
- self.lblComments.setFont(lblFont)
921
- self.lblSynopsis.setFont(lblFont)
898
+ self.showComments.setFont(lblFont)
899
+ self.showSynopsis.setFont(lblFont)
922
900
 
923
901
  # Assemble Layout
924
902
  self.outerBox = QHBoxLayout()
925
- self.outerBox.setSpacing(bSp)
926
903
  self.outerBox.addWidget(self.showHide, 0)
927
904
  self.outerBox.addStretch(1)
928
905
  self.outerBox.addWidget(self.showComments, 0)
929
- self.outerBox.addWidget(self.lblComments, 0)
930
- self.outerBox.addSpacing(hSp)
931
906
  self.outerBox.addWidget(self.showSynopsis, 0)
932
- self.outerBox.addWidget(self.lblSynopsis, 0)
907
+ self.outerBox.setSpacing(hSp)
933
908
  self.setLayout(self.outerBox)
934
909
 
935
910
  # Fix Margins and Size
@@ -960,11 +935,11 @@ class GuiDocViewFooter(QWidget):
960
935
  self.showComments.setIcon(bulletIcon)
961
936
  self.showSynopsis.setIcon(bulletIcon)
962
937
 
963
- # StyleSheets
938
+ colText = SHARED.theme.colText
964
939
  buttonStyle = (
965
940
  "QToolButton {{border: none; background: transparent;}} "
966
- "QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
967
- ).format(*SHARED.theme.colText)
941
+ "QToolButton:hover {{border: none; background: rgba({0}, {1}, {2}, 0.2);}}"
942
+ ).format(colText.red(), colText.green(), colText.blue())
968
943
 
969
944
  self.showHide.setStyleSheet(buttonStyle)
970
945
  self.showComments.setStyleSheet(buttonStyle)
@@ -979,12 +954,10 @@ class GuiDocViewFooter(QWidget):
979
954
  theme rather than the main GUI.
980
955
  """
981
956
  palette = QPalette()
982
- palette.setColor(QPalette.ColorRole.Window, QColor(*SHARED.theme.colBack))
983
- palette.setColor(QPalette.ColorRole.WindowText, QColor(*SHARED.theme.colText))
984
- palette.setColor(QPalette.ColorRole.Text, QColor(*SHARED.theme.colText))
957
+ palette.setColor(QPalette.ColorRole.Window, SHARED.theme.colBack)
958
+ palette.setColor(QPalette.ColorRole.WindowText, SHARED.theme.colText)
959
+ palette.setColor(QPalette.ColorRole.Text, SHARED.theme.colText)
985
960
  self.setPalette(palette)
986
- self.lblComments.setPalette(palette)
987
- self.lblSynopsis.setPalette(palette)
988
961
  return
989
962
 
990
963
  ##