novelWriter 2.5.1__py3-none-any.whl → 2.6b1__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 (64) hide show
  1. {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/METADATA +2 -1
  2. {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/RECORD +61 -56
  3. {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +3 -3
  5. novelwriter/assets/i18n/project_en_GB.json +1 -0
  6. novelwriter/assets/icons/typicons_dark/icons.conf +1 -0
  7. novelwriter/assets/icons/typicons_dark/mixed_copy.svg +4 -0
  8. novelwriter/assets/icons/typicons_light/icons.conf +1 -0
  9. novelwriter/assets/icons/typicons_light/mixed_copy.svg +4 -0
  10. novelwriter/assets/manual.pdf +0 -0
  11. novelwriter/assets/sample.zip +0 -0
  12. novelwriter/assets/themes/default_light.conf +2 -2
  13. novelwriter/common.py +63 -0
  14. novelwriter/config.py +10 -3
  15. novelwriter/constants.py +153 -60
  16. novelwriter/core/buildsettings.py +66 -39
  17. novelwriter/core/coretools.py +34 -22
  18. novelwriter/core/docbuild.py +130 -169
  19. novelwriter/core/index.py +29 -18
  20. novelwriter/core/item.py +2 -2
  21. novelwriter/core/options.py +4 -1
  22. novelwriter/core/spellcheck.py +9 -14
  23. novelwriter/dialogs/preferences.py +45 -32
  24. novelwriter/dialogs/projectsettings.py +3 -3
  25. novelwriter/enum.py +29 -23
  26. novelwriter/extensions/configlayout.py +24 -11
  27. novelwriter/extensions/modified.py +13 -1
  28. novelwriter/extensions/pagedsidebar.py +5 -5
  29. novelwriter/formats/shared.py +155 -0
  30. novelwriter/formats/todocx.py +1195 -0
  31. novelwriter/formats/tohtml.py +452 -0
  32. novelwriter/{core → formats}/tokenizer.py +483 -485
  33. novelwriter/formats/tomarkdown.py +217 -0
  34. novelwriter/{core → formats}/toodt.py +270 -320
  35. novelwriter/formats/toqdoc.py +436 -0
  36. novelwriter/formats/toraw.py +91 -0
  37. novelwriter/gui/doceditor.py +240 -193
  38. novelwriter/gui/dochighlight.py +96 -84
  39. novelwriter/gui/docviewer.py +56 -30
  40. novelwriter/gui/docviewerpanel.py +3 -3
  41. novelwriter/gui/editordocument.py +17 -2
  42. novelwriter/gui/itemdetails.py +8 -4
  43. novelwriter/gui/mainmenu.py +121 -60
  44. novelwriter/gui/noveltree.py +35 -37
  45. novelwriter/gui/outline.py +186 -238
  46. novelwriter/gui/projtree.py +142 -131
  47. novelwriter/gui/sidebar.py +7 -6
  48. novelwriter/gui/theme.py +5 -4
  49. novelwriter/guimain.py +43 -155
  50. novelwriter/shared.py +14 -4
  51. novelwriter/text/counting.py +2 -0
  52. novelwriter/text/patterns.py +155 -59
  53. novelwriter/tools/manusbuild.py +1 -1
  54. novelwriter/tools/manuscript.py +121 -78
  55. novelwriter/tools/manussettings.py +403 -260
  56. novelwriter/tools/welcome.py +4 -4
  57. novelwriter/tools/writingstats.py +3 -3
  58. novelwriter/types.py +16 -6
  59. novelwriter/core/tohtml.py +0 -530
  60. novelwriter/core/tomarkdown.py +0 -252
  61. novelwriter/core/toqdoc.py +0 -419
  62. {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/LICENSE.md +0 -0
  63. {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/entry_points.txt +0 -0
  64. {novelWriter-2.5.1.dist-info → novelWriter-2.6b1.dist-info}/top_level.txt +0 -0
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
34
34
  )
35
35
 
36
36
  from novelwriter import CONFIG, SHARED
37
- from novelwriter.common import describeFont, uniqueCompact
37
+ from novelwriter.common import compact, describeFont, uniqueCompact
38
38
  from novelwriter.constants import nwUnicode
39
39
  from novelwriter.dialogs.quotes import GuiQuoteSelect
40
40
  from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
@@ -133,7 +133,7 @@ class GuiPreferences(NDialog):
133
133
  """Build the settings form."""
134
134
  section = 0
135
135
  iSz = SHARED.theme.baseIconSize
136
- boxFixed = 5*SHARED.theme.textNWidth
136
+ boxFixed = 6*SHARED.theme.textNWidth
137
137
  minWidth = CONFIG.pxInt(200)
138
138
  fontWidth = CONFIG.pxInt(162)
139
139
 
@@ -543,6 +543,23 @@ class GuiPreferences(NDialog):
543
543
  self.tr("Applies to the selected quote styles.")
544
544
  )
545
545
 
546
+ self.altDialogOpen = QLineEdit(self)
547
+ self.altDialogOpen.setMaxLength(4)
548
+ self.altDialogOpen.setFixedWidth(boxFixed)
549
+ self.altDialogOpen.setAlignment(QtAlignCenter)
550
+ self.altDialogOpen.setText(CONFIG.altDialogOpen)
551
+
552
+ self.altDialogClose = QLineEdit(self)
553
+ self.altDialogClose.setMaxLength(4)
554
+ self.altDialogClose.setFixedWidth(boxFixed)
555
+ self.altDialogClose.setAlignment(QtAlignCenter)
556
+ self.altDialogClose.setText(CONFIG.altDialogClose)
557
+
558
+ self.mainForm.addRow(
559
+ self.tr("Alternative dialogue symbols"), [self.altDialogOpen, self.altDialogClose],
560
+ self.tr("Custom highlighting of dialogue text.")
561
+ )
562
+
546
563
  self.allowOpenDial = NSwitch(self)
547
564
  self.allowOpenDial.setChecked(CONFIG.allowOpenDial)
548
565
  self.mainForm.addRow(
@@ -550,6 +567,16 @@ class GuiPreferences(NDialog):
550
567
  self.tr("Highlight dialogue line with no closing quote.")
551
568
  )
552
569
 
570
+ self.dialogLine = QLineEdit(self)
571
+ self.dialogLine.setMaxLength(4)
572
+ self.dialogLine.setFixedWidth(boxFixed)
573
+ self.dialogLine.setAlignment(QtAlignCenter)
574
+ self.dialogLine.setText(CONFIG.dialogLine)
575
+ self.mainForm.addRow(
576
+ self.tr("Dialogue line symbols"), self.dialogLine,
577
+ self.tr("Lines starting with these symbols are always dialogue.")
578
+ )
579
+
553
580
  self.narratorBreak = QLineEdit(self)
554
581
  self.narratorBreak.setMaxLength(1)
555
582
  self.narratorBreak.setFixedWidth(boxFixed)
@@ -557,34 +584,17 @@ class GuiPreferences(NDialog):
557
584
  self.narratorBreak.setText(CONFIG.narratorBreak)
558
585
  self.mainForm.addRow(
559
586
  self.tr("Dialogue narrator break symbol"), self.narratorBreak,
560
- self.tr("Symbol to indicate injected narrator break.")
587
+ self.tr("Symbol to indicate injected narrator break in dialogue")
561
588
  )
562
589
 
563
- self.dialogLine = QLineEdit(self)
564
- self.dialogLine.setMaxLength(1)
565
- self.dialogLine.setFixedWidth(boxFixed)
566
- self.dialogLine.setAlignment(QtAlignCenter)
567
- self.dialogLine.setText(CONFIG.dialogLine)
568
- self.mainForm.addRow(
569
- self.tr("Dialogue line symbol"), self.dialogLine,
570
- self.tr("Lines starting with this symbol are dialogue.")
571
- )
572
-
573
- self.altDialogOpen = QLineEdit(self)
574
- self.altDialogOpen.setMaxLength(4)
575
- self.altDialogOpen.setFixedWidth(boxFixed)
576
- self.altDialogOpen.setAlignment(QtAlignCenter)
577
- self.altDialogOpen.setText(CONFIG.altDialogOpen)
578
-
579
- self.altDialogClose = QLineEdit(self)
580
- self.altDialogClose.setMaxLength(4)
581
- self.altDialogClose.setFixedWidth(boxFixed)
582
- self.altDialogClose.setAlignment(QtAlignCenter)
583
- self.altDialogClose.setText(CONFIG.altDialogClose)
584
-
590
+ self.narratorDialog = QLineEdit(self)
591
+ self.narratorDialog.setMaxLength(1)
592
+ self.narratorDialog.setFixedWidth(boxFixed)
593
+ self.narratorDialog.setAlignment(QtAlignCenter)
594
+ self.narratorDialog.setText(CONFIG.narratorDialog)
585
595
  self.mainForm.addRow(
586
- self.tr("Alternative dialogue symbols"), [self.altDialogOpen, self.altDialogClose],
587
- self.tr("Custom highlighting of dialogue text.")
596
+ self.tr("Alternating dialogue/narration symbol"), self.narratorDialog,
597
+ self.tr("Alternates dialogue highlighting within any paragraph.")
588
598
  )
589
599
 
590
600
  self.highlightEmph = NSwitch(self)
@@ -952,17 +962,19 @@ class GuiPreferences(NDialog):
952
962
  # Text Highlighting
953
963
  dialogueStyle = self.dialogStyle.currentData()
954
964
  allowOpenDial = self.allowOpenDial.isChecked()
965
+ dialogueLine = uniqueCompact(self.dialogLine.text())
955
966
  narratorBreak = self.narratorBreak.text().strip()
956
- dialogueLine = self.dialogLine.text().strip()
957
- altDialogOpen = self.altDialogOpen.text()
958
- altDialogClose = self.altDialogClose.text()
967
+ narratorDialog = self.narratorDialog.text().strip()
968
+ altDialogOpen = compact(self.altDialogOpen.text())
969
+ altDialogClose = compact(self.altDialogClose.text())
959
970
  highlightEmph = self.highlightEmph.isChecked()
960
971
  showMultiSpaces = self.showMultiSpaces.isChecked()
961
972
 
962
973
  updateSyntax |= CONFIG.dialogStyle != dialogueStyle
963
974
  updateSyntax |= CONFIG.allowOpenDial != allowOpenDial
964
- updateSyntax |= CONFIG.narratorBreak != narratorBreak
965
975
  updateSyntax |= CONFIG.dialogLine != dialogueLine
976
+ updateSyntax |= CONFIG.narratorBreak != narratorBreak
977
+ updateSyntax |= CONFIG.narratorDialog != narratorDialog
966
978
  updateSyntax |= CONFIG.altDialogOpen != altDialogOpen
967
979
  updateSyntax |= CONFIG.altDialogClose != altDialogClose
968
980
  updateSyntax |= CONFIG.highlightEmph != highlightEmph
@@ -970,8 +982,9 @@ class GuiPreferences(NDialog):
970
982
 
971
983
  CONFIG.dialogStyle = dialogueStyle
972
984
  CONFIG.allowOpenDial = allowOpenDial
973
- CONFIG.narratorBreak = narratorBreak
974
985
  CONFIG.dialogLine = dialogueLine
986
+ CONFIG.narratorBreak = narratorBreak
987
+ CONFIG.narratorDialog = narratorDialog
975
988
  CONFIG.altDialogOpen = altDialogOpen
976
989
  CONFIG.altDialogClose = altDialogClose
977
990
  CONFIG.highlightEmph = highlightEmph
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import (
35
35
  )
36
36
 
37
37
  from novelwriter import CONFIG, SHARED
38
- from novelwriter.common import simplified
38
+ from novelwriter.common import qtLambda, simplified
39
39
  from novelwriter.constants import nwLabels, trConst
40
40
  from novelwriter.core.status import NWStatus, StatusEntry
41
41
  from novelwriter.enum import nwStatusShape
@@ -365,10 +365,10 @@ class _StatusPage(NFixedPage):
365
365
  self.delButton.clicked.connect(self._delItem)
366
366
 
367
367
  self.upButton = NIconToolButton(self, iSz, "up")
368
- self.upButton.clicked.connect(lambda: self._moveItem(-1))
368
+ self.upButton.clicked.connect(qtLambda(self._moveItem, -1))
369
369
 
370
370
  self.dnButton = NIconToolButton(self, iSz, "down")
371
- self.dnButton.clicked.connect(lambda: self._moveItem(1))
371
+ self.dnButton.clicked.connect(qtLambda(self._moveItem, 1))
372
372
 
373
373
  # Edit Form
374
374
  self.editName = QLineEdit(self)
novelwriter/enum.py CHANGED
@@ -137,6 +137,7 @@ class nwDocInsert(Enum):
137
137
  VSPACE_M = 9
138
138
  LIPSUM = 10
139
139
  FOOTNOTE = 11
140
+ LINE_BRK = 12
140
141
 
141
142
 
142
143
  class nwView(Enum):
@@ -157,35 +158,40 @@ class nwFocus(Enum):
157
158
 
158
159
  class nwOutline(Enum):
159
160
 
160
- TITLE = 0
161
- LEVEL = 1
162
- LABEL = 2
163
- LINE = 3
164
- CCOUNT = 4
165
- WCOUNT = 5
166
- PCOUNT = 6
167
- POV = 7
168
- FOCUS = 8
169
- CHAR = 9
170
- PLOT = 10
171
- TIME = 11
172
- WORLD = 12
173
- OBJECT = 13
174
- ENTITY = 14
175
- CUSTOM = 15
176
- SYNOP = 16
161
+ TITLE = 0
162
+ LEVEL = 1
163
+ LABEL = 2
164
+ LINE = 3
165
+ STATUS = 4
166
+ CCOUNT = 5
167
+ WCOUNT = 6
168
+ PCOUNT = 7
169
+ POV = 8
170
+ FOCUS = 9
171
+ CHAR = 10
172
+ PLOT = 11
173
+ TIME = 12
174
+ WORLD = 13
175
+ OBJECT = 14
176
+ ENTITY = 15
177
+ CUSTOM = 16
178
+ STORY = 17
179
+ MENTION = 18
180
+ SYNOP = 19
177
181
 
178
182
 
179
183
  class nwBuildFmt(Enum):
180
184
 
181
185
  ODT = 0
182
186
  FODT = 1
183
- HTML = 2
184
- NWD = 3
185
- STD_MD = 4
186
- EXT_MD = 5
187
- J_HTML = 6
188
- J_NWD = 7
187
+ DOCX = 2
188
+ PDF = 3
189
+ HTML = 4
190
+ STD_MD = 5
191
+ EXT_MD = 6
192
+ NWD = 7
193
+ J_HTML = 8
194
+ J_NWD = 9
189
195
 
190
196
 
191
197
  class nwStatusShape(Enum):
@@ -27,7 +27,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
27
27
  """
28
28
  from __future__ import annotations
29
29
 
30
- from PyQt5.QtCore import Qt
31
30
  from PyQt5.QtGui import QColor, QFont, QPalette
32
31
  from PyQt5.QtWidgets import (
33
32
  QAbstractButton, QFrame, QHBoxLayout, QLabel, QLayout, QScrollArea,
@@ -35,6 +34,7 @@ from PyQt5.QtWidgets import (
35
34
  )
36
35
 
37
36
  from novelwriter import CONFIG
37
+ from novelwriter.types import QtScrollAsNeeded
38
38
 
39
39
  DEFAULT_SCALE = 0.9
40
40
 
@@ -76,8 +76,8 @@ class NScrollablePage(QScrollArea):
76
76
  self._widget = QWidget(self)
77
77
  self.setWidget(self._widget)
78
78
  self.setWidgetResizable(True)
79
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
80
- self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
79
+ self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
80
+ self.setVerticalScrollBarPolicy(QtScrollAsNeeded)
81
81
  self.setFrameShadow(QFrame.Shadow.Sunken)
82
82
  self.setFrameShape(QFrame.Shape.StyledPanel)
83
83
  return
@@ -113,8 +113,8 @@ class NScrollableForm(QScrollArea):
113
113
 
114
114
  self.setWidget(self._widget)
115
115
  self.setWidgetResizable(True)
116
- self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
117
- self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
116
+ self.setHorizontalScrollBarPolicy(QtScrollAsNeeded)
117
+ self.setVerticalScrollBarPolicy(QtScrollAsNeeded)
118
118
  self.setFrameShadow(QFrame.Shadow.Sunken)
119
119
  self.setFrameShape(QFrame.Shape.StyledPanel)
120
120
 
@@ -180,9 +180,16 @@ class NScrollableForm(QScrollArea):
180
180
  self._sections[identifier] = qLabel
181
181
  return
182
182
 
183
- def addRow(self, label: str, widget: QWidget | list[QWidget], helpText: str = "",
184
- unit: str | None = None, button: QWidget | None = None, editable: str | None = None,
185
- stretch: tuple[int, int] = (1, 0)) -> None:
183
+ def addRow(
184
+ self,
185
+ label: str | None,
186
+ widget: QWidget | list[QWidget | str | int],
187
+ helpText: str = "",
188
+ unit: str | None = None,
189
+ button: QWidget | None = None,
190
+ editable: str | None = None,
191
+ stretch: tuple[int, int] = (1, 0),
192
+ ) -> None:
186
193
  """Add a label and a widget as a new row of the form."""
187
194
  row = QHBoxLayout()
188
195
  row.setSpacing(CONFIG.pxInt(12))
@@ -191,13 +198,18 @@ class NScrollableForm(QScrollArea):
191
198
  wBox = QHBoxLayout()
192
199
  wBox.setContentsMargins(0, 0, 0, 0)
193
200
  for item in widget:
194
- wBox.addWidget(item)
201
+ if isinstance(item, QWidget):
202
+ wBox.addWidget(item)
203
+ elif isinstance(item, str):
204
+ wBox.addWidget(QLabel(item, self))
205
+ elif isinstance(item, int):
206
+ wBox.addSpacing(CONFIG.pxInt(item))
195
207
  qWidget = QWidget(self)
196
208
  qWidget.setLayout(wBox)
197
209
  else:
198
210
  qWidget = widget
199
211
 
200
- qLabel = QLabel(label, self)
212
+ qLabel = QLabel(label or "", self)
201
213
  qLabel.setIndent(self._indent)
202
214
  qLabel.setBuddy(qWidget)
203
215
 
@@ -230,7 +242,8 @@ class NScrollableForm(QScrollArea):
230
242
  row.addWidget(qWidget, stretch[1])
231
243
 
232
244
  self._layout.addLayout(row)
233
- self._index[label.strip()] = qWidget
245
+ if label:
246
+ self._index[label.strip()] = qWidget
234
247
  self._first = False
235
248
 
236
249
  return
@@ -136,9 +136,21 @@ class NSpinBox(QSpinBox):
136
136
 
137
137
  class NDoubleSpinBox(QDoubleSpinBox):
138
138
 
139
- def __init__(self, parent: QWidget | None = None) -> None:
139
+ def __init__(
140
+ self,
141
+ parent: QWidget | None = None,
142
+ *,
143
+ min: float = 0.0,
144
+ max: float = 15.0,
145
+ step: float = 0.1,
146
+ prec: int = 2,
147
+ ) -> None:
140
148
  super().__init__(parent=parent)
141
149
  self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
150
+ self.setMinimum(min)
151
+ self.setMaximum(max)
152
+ self.setSingleStep(step)
153
+ self.setDecimals(prec)
142
154
  return
143
155
 
144
156
  def wheelEvent(self, event: QWheelEvent) -> None:
@@ -52,7 +52,7 @@ class NPagedSideBar(QToolBar):
52
52
 
53
53
  self._labelCol = None
54
54
  self._spacerHeight = self.fontMetrics().height() // 2
55
- self._buttons: dict[int, _NPagedToolButton] = {}
55
+ self._buttons: dict[int, _PagedToolButton] = {}
56
56
 
57
57
  self._group = QButtonGroup(self)
58
58
  self._group.setExclusive(True)
@@ -67,7 +67,7 @@ class NPagedSideBar(QToolBar):
67
67
 
68
68
  return
69
69
 
70
- def button(self, buttonId: int) -> _NPagedToolButton:
70
+ def button(self, buttonId: int) -> _PagedToolButton:
71
71
  """Return a specific button."""
72
72
  return self._buttons[buttonId]
73
73
 
@@ -85,7 +85,7 @@ class NPagedSideBar(QToolBar):
85
85
 
86
86
  def addButton(self, text: str, buttonId: int = -1) -> QAction:
87
87
  """Add a new button to the toolbar."""
88
- button = _NPagedToolButton(self)
88
+ button = _PagedToolButton(self)
89
89
  button.setText(text)
90
90
 
91
91
  action = self.insertWidget(self._stretchAction, button)
@@ -113,7 +113,7 @@ class NPagedSideBar(QToolBar):
113
113
  return
114
114
 
115
115
 
116
- class _NPagedToolButton(QToolButton):
116
+ class _PagedToolButton(QToolButton):
117
117
 
118
118
  __slots__ = ("_bH", "_tM", "_lM", "_cR", "_aH")
119
119
 
@@ -154,7 +154,7 @@ class _NPagedToolButton(QToolButton):
154
154
  height = self.height()
155
155
  palette = self.palette()
156
156
 
157
- if opt.state & QtMouseOver == QtMouseOver:
157
+ if opt.state & QtMouseOver == QtMouseOver: # pragma: no cover
158
158
  backCol = palette.base()
159
159
  paint.setBrush(backCol)
160
160
  paint.setOpacity(0.75)
@@ -0,0 +1,155 @@
1
+ """
2
+ novelWriter – Formats Shared
3
+ ============================
4
+
5
+ File History:
6
+ Created: 2024-10-21 [2.6b1]
7
+
8
+ This file is a part of novelWriter
9
+ Copyright 2018–2024, Veronica Berglyd Olsen
10
+
11
+ This program is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ This program is distributed in the hope that it will be useful, but
17
+ WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
+ General Public License for more details.
20
+
21
+ You should have received a copy of the GNU General Public License
22
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import re
27
+
28
+ from enum import Flag, IntEnum
29
+
30
+ from PyQt5.QtGui import QColor
31
+
32
+ ESCAPES = {r"\*": "*", r"\~": "~", r"\_": "_", r"\[": "[", r"\]": "]", r"\ ": ""}
33
+ RX_ESC = re.compile("|".join([re.escape(k) for k in ESCAPES.keys()]), flags=re.DOTALL)
34
+
35
+
36
+ def stripEscape(text: str) -> str:
37
+ """Strip escaped Markdown characters from paragraph text."""
38
+ if "\\" in text:
39
+ return RX_ESC.sub(lambda x: ESCAPES[x.group(0)], text)
40
+ return text
41
+
42
+
43
+ class TextDocumentTheme:
44
+ """Default document theme."""
45
+
46
+ text: QColor = QColor(0, 0, 0)
47
+ highlight: QColor = QColor(255, 255, 166)
48
+ head: QColor = QColor(66, 113, 174)
49
+ link: QColor = QColor(66, 113, 174)
50
+ comment: QColor = QColor(100, 100, 100)
51
+ note: QColor = QColor(129, 55, 9)
52
+ code: QColor = QColor(66, 113, 174)
53
+ modifier: QColor = QColor(129, 55, 9)
54
+ keyword: QColor = QColor(245, 135, 31)
55
+ tag: QColor = QColor(66, 113, 174)
56
+ optional: QColor = QColor(66, 113, 174)
57
+ dialog: QColor = QColor(66, 113, 174)
58
+ altdialog: QColor = QColor(129, 55, 9)
59
+
60
+
61
+ # Enums
62
+ # =====
63
+
64
+ class TextFmt(IntEnum):
65
+ """Text Format.
66
+
67
+ An enum indicating the beginning or end of a text format region.
68
+ They must be paired with a position, and apply to locations in a
69
+ text block.
70
+ """
71
+
72
+ B_B = 1 # Begin bold
73
+ B_E = 2 # End bold
74
+ I_B = 3 # Begin italics
75
+ I_E = 4 # End italics
76
+ D_B = 5 # Begin strikeout
77
+ D_E = 6 # End strikeout
78
+ U_B = 7 # Begin underline
79
+ U_E = 8 # End underline
80
+ M_B = 9 # Begin mark
81
+ M_E = 10 # End mark
82
+ SUP_B = 11 # Begin superscript
83
+ SUP_E = 12 # End superscript
84
+ SUB_B = 13 # Begin subscript
85
+ SUB_E = 14 # End subscript
86
+ COL_B = 15 # Begin colour
87
+ COL_E = 16 # End colour
88
+ ANM_B = 17 # Begin anchor name
89
+ ANM_E = 18 # End anchor name
90
+ ARF_B = 19 # Begin anchor link
91
+ ARF_E = 20 # End anchor link
92
+ HRF_B = 21 # Begin href link
93
+ HRF_E = 22 # End href link
94
+ FNOTE = 23 # Footnote marker
95
+ FIELD = 24 # Data field
96
+ STRIP = 25 # Strip the format code
97
+
98
+
99
+ class BlockTyp(IntEnum):
100
+ """Text Block Type.
101
+
102
+ An enum indicating the type of a text block.
103
+ """
104
+
105
+ EMPTY = 1 # Empty line (new paragraph)
106
+ TITLE = 2 # Title
107
+ HEAD1 = 3 # Heading 1
108
+ HEAD2 = 4 # Heading 2
109
+ HEAD3 = 5 # Heading 3
110
+ HEAD4 = 6 # Heading 4
111
+ TEXT = 7 # Text line
112
+ SEP = 8 # Scene separator
113
+ SKIP = 9 # Paragraph break
114
+ SUMMARY = 10 # Synopsis/short comment
115
+ NOTE = 11 # Note
116
+ COMMENT = 12 # Comment
117
+ KEYWORD = 13 # Tag/reference keywords
118
+
119
+
120
+ class BlockFmt(Flag):
121
+ """Text Block Format.
122
+
123
+ An enum of flags that can be combined to format a text block.
124
+ """
125
+
126
+ NONE = 0x0000 # No special style
127
+ LEFT = 0x0001 # Left aligned
128
+ RIGHT = 0x0002 # Right aligned
129
+ CENTRE = 0x0004 # Centred
130
+ JUSTIFY = 0x0008 # Justified
131
+ PBB = 0x0010 # Page break before
132
+ PBA = 0x0020 # Page break after
133
+ Z_TOP = 0x0040 # Zero top margin
134
+ Z_BTM = 0x0080 # Zero bottom margin
135
+ IND_L = 0x0100 # Left indentation
136
+ IND_R = 0x0200 # Right indentation
137
+ IND_T = 0x0400 # Text indentation
138
+
139
+ # Masks
140
+ ALIGNED = LEFT | RIGHT | CENTRE | JUSTIFY
141
+
142
+
143
+ # Types
144
+ # =====
145
+
146
+ # A list of formats for a single text string, consisting of:
147
+ # text position, text format, and meta data
148
+ T_Formats = list[tuple[int, TextFmt, str]]
149
+
150
+ # A note or comment with text and associated text formats
151
+ T_Note = tuple[str, T_Formats]
152
+
153
+ # A tokenized text block, consisting of:
154
+ # type, header number, text, text formats, and block format
155
+ T_Block = tuple[BlockTyp, str, str, T_Formats, BlockFmt]