novelWriter 2.3.1__py3-none-any.whl → 2.4b1__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 (81) hide show
  1. {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/METADATA +1 -1
  2. {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/RECORD +81 -70
  3. novelwriter/__init__.py +5 -5
  4. novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
  5. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
  6. novelwriter/assets/icons/typicons_dark/typ_arrow-down.svg +4 -0
  7. novelwriter/assets/icons/typicons_dark/typ_arrow-right.svg +4 -0
  8. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
  9. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
  10. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
  11. novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
  12. novelwriter/assets/icons/typicons_light/icons.conf +4 -0
  13. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
  14. novelwriter/assets/icons/typicons_light/typ_arrow-down.svg +4 -0
  15. novelwriter/assets/icons/typicons_light/typ_arrow-right.svg +4 -0
  16. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
  17. novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
  18. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
  19. novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
  20. novelwriter/assets/manual.pdf +0 -0
  21. novelwriter/assets/sample.zip +0 -0
  22. novelwriter/assets/syntax/default_dark.conf +1 -0
  23. novelwriter/assets/syntax/default_light.conf +1 -0
  24. novelwriter/assets/syntax/grey_dark.conf +1 -0
  25. novelwriter/assets/syntax/grey_light.conf +1 -0
  26. novelwriter/assets/syntax/light_owl.conf +1 -0
  27. novelwriter/assets/syntax/night_owl.conf +1 -0
  28. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  29. novelwriter/assets/syntax/solarized_light.conf +1 -0
  30. novelwriter/assets/syntax/tomorrow.conf +1 -0
  31. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  32. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  33. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  34. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  35. novelwriter/assets/text/credits_en.htm +25 -23
  36. novelwriter/common.py +1 -1
  37. novelwriter/config.py +35 -12
  38. novelwriter/constants.py +5 -6
  39. novelwriter/core/buildsettings.py +60 -40
  40. novelwriter/core/coretools.py +98 -13
  41. novelwriter/core/docbuild.py +74 -7
  42. novelwriter/core/document.py +24 -3
  43. novelwriter/core/index.py +31 -112
  44. novelwriter/core/project.py +10 -15
  45. novelwriter/core/sessions.py +2 -2
  46. novelwriter/core/status.py +4 -4
  47. novelwriter/core/storage.py +8 -2
  48. novelwriter/core/tohtml.py +22 -25
  49. novelwriter/core/tokenizer.py +416 -232
  50. novelwriter/core/tomd.py +17 -8
  51. novelwriter/core/toodt.py +65 -7
  52. novelwriter/core/tree.py +8 -8
  53. novelwriter/dialogs/docsplit.py +7 -8
  54. novelwriter/dialogs/preferences.py +3 -6
  55. novelwriter/enum.py +17 -14
  56. novelwriter/extensions/modified.py +20 -2
  57. novelwriter/extensions/versioninfo.py +1 -1
  58. novelwriter/gui/doceditor.py +257 -279
  59. novelwriter/gui/dochighlight.py +29 -25
  60. novelwriter/gui/docviewer.py +139 -148
  61. novelwriter/gui/docviewerpanel.py +4 -24
  62. novelwriter/gui/editordocument.py +12 -1
  63. novelwriter/gui/itemdetails.py +6 -6
  64. novelwriter/gui/mainmenu.py +37 -16
  65. novelwriter/gui/noveltree.py +11 -19
  66. novelwriter/gui/outline.py +43 -20
  67. novelwriter/gui/projtree.py +35 -43
  68. novelwriter/gui/search.py +316 -0
  69. novelwriter/gui/sidebar.py +25 -30
  70. novelwriter/gui/theme.py +59 -6
  71. novelwriter/guimain.py +176 -173
  72. novelwriter/shared.py +26 -1
  73. novelwriter/text/__init__.py +3 -0
  74. novelwriter/text/counting.py +137 -0
  75. novelwriter/tools/manuscript.py +344 -55
  76. novelwriter/tools/manussettings.py +213 -71
  77. novelwriter/tools/welcome.py +1 -1
  78. {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/LICENSE.md +0 -0
  79. {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/WHEEL +0 -0
  80. {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/entry_points.txt +0 -0
  81. {novelWriter-2.3.1.dist-info → novelWriter-2.4b1.dist-info}/top_level.txt +0 -0
novelwriter/core/tomd.py CHANGED
@@ -27,7 +27,7 @@ import logging
27
27
 
28
28
  from pathlib import Path
29
29
 
30
- from novelwriter.constants import nwHeadFmt, nwLabels
30
+ from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode
31
31
  from novelwriter.core.project import NWProject
32
32
  from novelwriter.core.tokenizer import Tokenizer
33
33
 
@@ -49,6 +49,7 @@ class ToMarkdown(Tokenizer):
49
49
  super().__init__(project)
50
50
  self._genMode = self.M_STD
51
51
  self._fullMD: list[str] = []
52
+ self._preserveBreaks = True
52
53
  return
53
54
 
54
55
  ##
@@ -74,6 +75,11 @@ class ToMarkdown(Tokenizer):
74
75
  self._genMode = self.M_EXT
75
76
  return
76
77
 
78
+ def setPreserveBreaks(self, state: bool) -> None:
79
+ """Preserve line breaks in paragraphs."""
80
+ self._preserveBreaks = state
81
+ return
82
+
77
83
  ##
78
84
  # Class Methods
79
85
  ##
@@ -95,11 +101,14 @@ class ToMarkdown(Tokenizer):
95
101
  self.FMT_D_E: "",
96
102
  self.FMT_U_B: "",
97
103
  self.FMT_U_E: "",
104
+ self.FMT_M_B: "",
105
+ self.FMT_M_E: "",
98
106
  self.FMT_SUP_B: "",
99
107
  self.FMT_SUP_E: "",
100
108
  self.FMT_SUB_B: "",
101
109
  self.FMT_SUB_E: "",
102
110
  }
111
+ cSkip = ""
103
112
  else:
104
113
  # Extended Markdown
105
114
  mdTags = {
@@ -111,22 +120,26 @@ class ToMarkdown(Tokenizer):
111
120
  self.FMT_D_E: "~~",
112
121
  self.FMT_U_B: "",
113
122
  self.FMT_U_E: "",
123
+ self.FMT_M_B: "==",
124
+ self.FMT_M_E: "==",
114
125
  self.FMT_SUP_B: "^",
115
126
  self.FMT_SUP_E: "^",
116
127
  self.FMT_SUB_B: "~",
117
128
  self.FMT_SUB_E: "~",
118
129
  }
130
+ cSkip = nwUnicode.U_MMSP
119
131
 
120
132
  self._result = ""
121
133
 
122
134
  para = []
123
135
  lines = []
136
+ lineSep = " \n" if self._preserveBreaks else " "
124
137
 
125
138
  for tType, _, tText, tFormat, tStyle in self._tokens:
126
139
 
127
140
  if tType == self.T_EMPTY:
128
- if len(para) > 0:
129
- tTemp = (" \n".join(para)).rstrip(" ")
141
+ if para:
142
+ tTemp = (lineSep.join(para)).rstrip(" ")
130
143
  lines.append(f"{tTemp}\n\n")
131
144
  para = []
132
145
 
@@ -134,10 +147,6 @@ class ToMarkdown(Tokenizer):
134
147
  tHead = tText.replace(nwHeadFmt.BR, "\n")
135
148
  lines.append(f"# {tHead}\n\n")
136
149
 
137
- elif tType == self.T_UNNUM:
138
- tHead = tText.replace(nwHeadFmt.BR, "\n")
139
- lines.append(f"## {tHead}\n\n")
140
-
141
150
  elif tType == self.T_HEAD1:
142
151
  tHead = tText.replace(nwHeadFmt.BR, "\n")
143
152
  lines.append(f"# {tHead}\n\n")
@@ -158,7 +167,7 @@ class ToMarkdown(Tokenizer):
158
167
  lines.append(f"{tText}\n\n")
159
168
 
160
169
  elif tType == self.T_SKIP:
161
- lines.append("\n\n\n")
170
+ lines.append(f"{cSkip}\n\n")
162
171
 
163
172
  elif tType == self.T_TEXT:
164
173
  tTemp = tText
novelwriter/core/toodt.py CHANGED
@@ -84,14 +84,16 @@ X_BLD = 0x01 # Bold format
84
84
  X_ITA = 0x02 # Italic format
85
85
  X_DEL = 0x04 # Strikethrough format
86
86
  X_UND = 0x08 # Underline format
87
- X_SUP = 0x10 # Superscript
88
- X_SUB = 0x20 # Subscript
87
+ X_MRK = 0x10 # Marked format
88
+ X_SUP = 0x20 # Superscript
89
+ X_SUB = 0x40 # Subscript
89
90
 
90
91
  # Formatting Masks
91
92
  M_BLD = ~X_BLD
92
93
  M_ITA = ~X_ITA
93
94
  M_DEL = ~X_DEL
94
95
  M_UND = ~X_UND
96
+ M_MRK = ~X_MRK
95
97
  M_SUP = ~X_SUP
96
98
  M_SUB = ~X_SUB
97
99
 
@@ -136,6 +138,7 @@ class ToOdt(Tokenizer):
136
138
  self._textSize = 12
137
139
  self._textFixed = False
138
140
  self._colourHead = False
141
+ self._firstIndent = False
139
142
  self._headerFormat = ""
140
143
  self._pageOffset = 0
141
144
 
@@ -151,6 +154,7 @@ class ToOdt(Tokenizer):
151
154
  self._fSizeText = "12pt"
152
155
  self._fLineHeight = "115%"
153
156
  self._fBlockIndent = "1.693cm"
157
+ self._fTextIndent = "0.499cm"
154
158
  self._textAlign = "left"
155
159
  self._dLanguage = "en"
156
160
  self._dCountry = "GB"
@@ -189,6 +193,7 @@ class ToOdt(Tokenizer):
189
193
  self._opaHead34 = None
190
194
  self._colMetaTx = None
191
195
  self._opaMetaTx = None
196
+ self._markText = "#ffffa6"
192
197
 
193
198
  return
194
199
 
@@ -229,6 +234,11 @@ class ToOdt(Tokenizer):
229
234
  self._pageOffset = offset
230
235
  return
231
236
 
237
+ def setFirstLineIndent(self, state: bool) -> None:
238
+ """Enable or disable first line indent."""
239
+ self._firstIndent = state
240
+ return
241
+
232
242
  ##
233
243
  # Class Methods
234
244
  ##
@@ -394,6 +404,7 @@ class ToOdt(Tokenizer):
394
404
  pFmt = []
395
405
  pText = []
396
406
  pStyle = None
407
+ pIndent = True
397
408
  for tType, _, tText, tFormat, tStyle in self._tokens:
398
409
 
399
410
  # Styles
@@ -424,6 +435,9 @@ class ToOdt(Tokenizer):
424
435
  if tStyle & self.A_IND_R:
425
436
  oStyle.setMarginRight(self._fBlockIndent)
426
437
 
438
+ if tType not in (self.T_EMPTY, self.T_TEXT):
439
+ pIndent = False
440
+
427
441
  # Process Text Types
428
442
  if tType == self.T_EMPTY:
429
443
  if len(pText) > 1 and pStyle is not None:
@@ -437,7 +451,14 @@ class ToOdt(Tokenizer):
437
451
  tLen = len(tTxt)
438
452
  tTxt += f"{nText}\n"
439
453
  tFmt.extend((p+tLen, fmt) for p, fmt in nFmt)
440
- self._addTextPar("Text_20_body", pStyle, tTxt.rstrip(), tFmt=tFmt)
454
+
455
+ # Don't indent a paragraph if it has alignment set
456
+ tIndent = self._firstIndent and pIndent and pStyle.isUnaligned()
457
+ self._addTextPar(
458
+ "First_20_line_20_indent" if tIndent else "Text_20_body",
459
+ pStyle, tTxt.rstrip(), tFmt=tFmt
460
+ )
461
+ pIndent = True
441
462
 
442
463
  pFmt = []
443
464
  pText = []
@@ -447,10 +468,6 @@ class ToOdt(Tokenizer):
447
468
  tHead = tText.replace(nwHeadFmt.BR, "\n")
448
469
  self._addTextPar("Title", oStyle, tHead, isHead=False) # Title must be text:p
449
470
 
450
- elif tType == self.T_UNNUM:
451
- tHead = tText.replace(nwHeadFmt.BR, "\n")
452
- self._addTextPar("Heading_20_2", oStyle, tHead, isHead=True, oLevel="2")
453
-
454
471
  elif tType == self.T_HEAD1:
455
472
  tHead = tText.replace(nwHeadFmt.BR, "\n")
456
473
  self._addTextPar("Heading_20_1", oStyle, tHead, isHead=True, oLevel="1")
@@ -643,6 +660,10 @@ class ToOdt(Tokenizer):
643
660
  xFmt |= X_UND
644
661
  elif fFmt == self.FMT_U_E:
645
662
  xFmt &= M_UND
663
+ elif fFmt == self.FMT_M_B:
664
+ xFmt |= X_MRK
665
+ elif fFmt == self.FMT_M_E:
666
+ xFmt &= M_MRK
646
667
  elif fFmt == self.FMT_SUP_B:
647
668
  xFmt |= X_SUP
648
669
  elif fFmt == self.FMT_SUP_E:
@@ -710,6 +731,8 @@ class ToOdt(Tokenizer):
710
731
  newStyle.setUnderlineStyle("solid")
711
732
  newStyle.setUnderlineWidth("auto")
712
733
  newStyle.setUnderlineColour("font-color")
734
+ if hFmt & X_MRK:
735
+ newStyle.setBackgroundColor(self._markText)
713
736
  if hFmt & X_SUP:
714
737
  newStyle.setTextPosition("super")
715
738
  if hFmt & X_SUB:
@@ -850,6 +873,18 @@ class ToOdt(Tokenizer):
850
873
 
851
874
  self._mainPara["Text_20_body"] = oStyle
852
875
 
876
+ # Add First Line Indent Style
877
+ # ===========================
878
+
879
+ oStyle = ODTParagraphStyle()
880
+ oStyle.setDisplayName("First line indent")
881
+ oStyle.setParentStyleName("Text_20_body")
882
+ oStyle.setClass("text")
883
+ oStyle.setTextIndent(self._fTextIndent)
884
+ oStyle.packXML(self._xStyl, "First_20_line_20_indent")
885
+
886
+ self._mainPara["First_20_line_20_indent"] = oStyle
887
+
853
888
  # Add Text Meta Style
854
889
  # ===================
855
890
 
@@ -1077,6 +1112,7 @@ class ODTParagraphStyle:
1077
1112
  "margin-bottom": ["fo", None],
1078
1113
  "margin-left": ["fo", None],
1079
1114
  "margin-right": ["fo", None],
1115
+ "text-indent": ["fo", None],
1080
1116
  "line-height": ["fo", None],
1081
1117
  "text-align": ["fo", None],
1082
1118
  "break-before": ["fo", None],
@@ -1095,6 +1131,16 @@ class ODTParagraphStyle:
1095
1131
 
1096
1132
  return
1097
1133
 
1134
+ ##
1135
+ # Checkers
1136
+ ##
1137
+
1138
+ def isUnaligned(self) -> bool:
1139
+ """Check if paragraph has any sort of alignment or margins."""
1140
+ return all(
1141
+ self._pAttr[n][1] is None for n in ["text-align", "margin-left", "margin-right"]
1142
+ )
1143
+
1098
1144
  ##
1099
1145
  # Attribute Setters
1100
1146
  ##
@@ -1145,6 +1191,10 @@ class ODTParagraphStyle:
1145
1191
  self._pAttr["margin-right"][1] = value
1146
1192
  return
1147
1193
 
1194
+ def setTextIndent(self, value: str | None) -> None:
1195
+ self._pAttr["text-indent"][1] = value
1196
+ return
1197
+
1148
1198
  def setLineHeight(self, value: str | None) -> None:
1149
1199
  self._pAttr["line-height"][1] = value
1150
1200
  return
@@ -1279,6 +1329,7 @@ class ODTTextStyle:
1279
1329
  self._tAttr = {
1280
1330
  "font-weight": ["fo", None],
1281
1331
  "font-style": ["fo", None],
1332
+ "background-color": ["fo", None],
1282
1333
  "text-position": ["style", None],
1283
1334
  "text-line-through-style": ["style", None],
1284
1335
  "text-line-through-type": ["style", None],
@@ -1306,6 +1357,13 @@ class ODTTextStyle:
1306
1357
  self._tAttr["font-style"][1] = None
1307
1358
  return
1308
1359
 
1360
+ def setBackgroundColor(self, value: str | None) -> None:
1361
+ if value and len(value) == 7 and value[0] == "#":
1362
+ self._tAttr["background-color"][1] = value
1363
+ else:
1364
+ self._tAttr["background-color"][1] = None
1365
+ return
1366
+
1309
1367
  def setTextPosition(self, value: str | None) -> None:
1310
1368
  if value in self.VALID_POS:
1311
1369
  self._tAttr["text-position"][1] = f"{value} 58%"
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)
@@ -57,7 +57,7 @@ class GuiDocSplit(QDialog):
57
57
 
58
58
  self.setWindowTitle(self.tr("Split Document"))
59
59
 
60
- self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
60
+ self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headings")))
61
61
  self.helpLabel = NColourLabel(
62
62
  self.tr("Select the maximum level to split into files."),
63
63
  SHARED.theme.helpText, parent=self, wrap=True
@@ -74,17 +74,17 @@ class GuiDocSplit(QDialog):
74
74
  intoFolder = pOptions.getBool("GuiDocSplit", "intoFolder", True)
75
75
  docHierarchy = pOptions.getBool("GuiDocSplit", "docHierarchy", True)
76
76
 
77
- # Header Selection
77
+ # Heading Selection
78
78
  self.listBox = QListWidget()
79
79
  self.listBox.setDragDropMode(QAbstractItemView.NoDragDrop)
80
80
  self.listBox.setMinimumWidth(CONFIG.pxInt(400))
81
81
  self.listBox.setMinimumHeight(CONFIG.pxInt(180))
82
82
 
83
83
  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)
84
+ self.splitLevel.addItem(self.tr("Split on Heading Level 1 (Partition)"), 1)
85
+ self.splitLevel.addItem(self.tr("Split up to Heading Level 2 (Chapter)"), 2)
86
+ self.splitLevel.addItem(self.tr("Split up to Heading Level 3 (Scene)"), 3)
87
+ self.splitLevel.addItem(self.tr("Split up to Heading Level 4 (Section)"), 4)
88
88
  spIndex = self.splitLevel.findData(spLevel)
89
89
  if spIndex != -1:
90
90
  self.splitLevel.setCurrentIndex(spIndex)
@@ -214,8 +214,7 @@ class GuiDocSplit(QDialog):
214
214
 
215
215
  spLevel = self.splitLevel.currentData()
216
216
  if not self._text:
217
- inDoc = SHARED.project.storage.getDocument(sHandle)
218
- self._text = (inDoc.readDocument() or "").splitlines()
217
+ self._text = SHARED.project.storage.getDocumentText(sHandle).splitlines()
219
218
 
220
219
  for lineNo, aLine in enumerate(self._text):
221
220
 
@@ -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,
@@ -226,8 +224,7 @@ class GuiPreferences(QDialog):
226
224
  self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200))
227
225
  for syntax, name in SHARED.theme.listSyntax():
228
226
  self.guiSyntax.addItem(name, syntax)
229
- if (idx := self.guiSyntax.findData(CONFIG.guiSyntax)) != -1:
230
- self.guiSyntax.setCurrentIndex(idx)
227
+ self.guiSyntax.setCurrentData(CONFIG.guiSyntax, "default_light")
231
228
 
232
229
  self.mainForm.addRow(
233
230
  self.tr("Document colour theme"), self.guiSyntax,
novelwriter/enum.py CHANGED
@@ -112,20 +112,22 @@ class nwDocAction(Enum):
112
112
  BLOCK_TXT = 19
113
113
  BLOCK_TTL = 20
114
114
  BLOCK_UNN = 21
115
- REPL_SNG = 22
116
- REPL_DBL = 23
117
- RM_BREAKS = 24
118
- ALIGN_L = 25
119
- ALIGN_C = 26
120
- ALIGN_R = 27
121
- INDENT_L = 28
122
- INDENT_R = 29
123
- SC_ITALIC = 30
124
- SC_BOLD = 31
125
- SC_STRIKE = 32
126
- SC_ULINE = 33
127
- SC_SUP = 34
128
- SC_SUB = 35
115
+ BLOCK_HSC = 22
116
+ REPL_SNG = 23
117
+ REPL_DBL = 24
118
+ RM_BREAKS = 25
119
+ ALIGN_L = 26
120
+ ALIGN_C = 27
121
+ ALIGN_R = 28
122
+ INDENT_L = 29
123
+ INDENT_R = 30
124
+ SC_ITALIC = 31
125
+ SC_BOLD = 32
126
+ SC_STRIKE = 33
127
+ SC_ULINE = 34
128
+ SC_MARK = 35
129
+ SC_SUP = 36
130
+ SC_SUB = 37
129
131
 
130
132
  # END Enum nwDocAction
131
133
 
@@ -153,6 +155,7 @@ class nwView(Enum):
153
155
  PROJECT = 1
154
156
  NOVEL = 2
155
157
  OUTLINE = 3
158
+ SEARCH = 4
156
159
 
157
160
  # END Enum nwView
158
161
 
@@ -25,9 +25,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
25
25
  """
26
26
  from __future__ import annotations
27
27
 
28
- from PyQt5.QtCore import Qt
28
+ from PyQt5.QtCore import QSize, Qt
29
29
  from PyQt5.QtGui import QWheelEvent
30
- from PyQt5.QtWidgets import QComboBox, QDoubleSpinBox, QSpinBox, QWidget
30
+ from PyQt5.QtWidgets import QComboBox, QDoubleSpinBox, QSpinBox, QToolButton, QWidget
31
31
 
32
32
 
33
33
  class NComboBox(QComboBox):
@@ -44,6 +44,12 @@ class NComboBox(QComboBox):
44
44
  event.ignore()
45
45
  return
46
46
 
47
+ def setCurrentData(self, data: str, default: str) -> None:
48
+ """Set the current index from data, with a fallback."""
49
+ idx = self.findData(data)
50
+ self.setCurrentIndex(self.findData(default) if idx < 0 else idx)
51
+ return
52
+
47
53
  # END Class NComboBox
48
54
 
49
55
 
@@ -79,3 +85,15 @@ class NDoubleSpinBox(QDoubleSpinBox):
79
85
  return
80
86
 
81
87
  # END Class NDoubleSpinBox
88
+
89
+
90
+ class NIconToolButton(QToolButton):
91
+
92
+ def __init__(self, parent: QWidget, iconSize: int) -> None:
93
+ super().__init__(parent=parent)
94
+ self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
95
+ self.setIconSize(QSize(iconSize, iconSize))
96
+ self.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
97
+ return
98
+
99
+ # END Class NIconToolButton
@@ -57,7 +57,7 @@ class VersionInfoWidget(QWidget):
57
57
  # Labels
58
58
  self._lblInfo = QLabel("{0} {1} \u2013 {2} {3} \u2013 {4}".format(
59
59
  self.tr("Version"), formatVersion(__version__),
60
- self.tr("Released on"), datetime.strptime(__date__, "%Y-%m-%d").strftime("%x"),
60
+ self.tr("Released on"), CONFIG.localDate(datetime.strptime(__date__, "%Y-%m-%d")),
61
61
  "<a href='#notes'>{0}</a>".format(self.tr("Release Notes")),
62
62
  ), self)
63
63
  self._lblInfo.linkActivated.connect(self._processLink)