novelWriter 2.6b1__py3-none-any.whl → 2.6rc1__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 (114) hide show
  1. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/METADATA +4 -4
  2. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/RECORD +114 -98
  3. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +50 -11
  5. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  6. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  7. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  8. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  9. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  10. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  11. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  12. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  13. novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
  14. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  15. novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
  16. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  17. novelwriter/assets/i18n/project_de_DE.json +2 -2
  18. novelwriter/assets/i18n/project_ru_RU.json +11 -0
  19. novelwriter/assets/icons/typicons_dark/icons.conf +7 -0
  20. novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +6 -0
  21. novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +6 -0
  22. novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +6 -0
  23. novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +6 -0
  24. novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +6 -0
  25. novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +6 -0
  26. novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +5 -0
  27. novelwriter/assets/icons/typicons_light/icons.conf +7 -0
  28. novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +6 -0
  29. novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +6 -0
  30. novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +6 -0
  31. novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +6 -0
  32. novelwriter/assets/icons/typicons_light/mixed_size-height.svg +6 -0
  33. novelwriter/assets/icons/typicons_light/mixed_size-width.svg +6 -0
  34. novelwriter/assets/icons/typicons_light/nw_toolbar.svg +5 -0
  35. novelwriter/assets/manual.pdf +0 -0
  36. novelwriter/assets/sample.zip +0 -0
  37. novelwriter/assets/text/credits_en.htm +1 -0
  38. novelwriter/common.py +38 -3
  39. novelwriter/config.py +19 -13
  40. novelwriter/constants.py +60 -45
  41. novelwriter/core/buildsettings.py +1 -1
  42. novelwriter/core/coretools.py +112 -126
  43. novelwriter/core/docbuild.py +4 -3
  44. novelwriter/core/document.py +1 -1
  45. novelwriter/core/index.py +10 -20
  46. novelwriter/core/item.py +40 -7
  47. novelwriter/core/itemmodel.py +518 -0
  48. novelwriter/core/options.py +1 -1
  49. novelwriter/core/project.py +68 -90
  50. novelwriter/core/projectdata.py +8 -2
  51. novelwriter/core/projectxml.py +1 -1
  52. novelwriter/core/sessions.py +1 -1
  53. novelwriter/core/spellcheck.py +1 -1
  54. novelwriter/core/status.py +24 -8
  55. novelwriter/core/storage.py +1 -1
  56. novelwriter/core/tree.py +269 -288
  57. novelwriter/dialogs/about.py +1 -1
  58. novelwriter/dialogs/docmerge.py +8 -18
  59. novelwriter/dialogs/docsplit.py +1 -1
  60. novelwriter/dialogs/editlabel.py +1 -1
  61. novelwriter/dialogs/preferences.py +4 -4
  62. novelwriter/dialogs/projectsettings.py +148 -98
  63. novelwriter/dialogs/quotes.py +1 -1
  64. novelwriter/dialogs/wordlist.py +11 -10
  65. novelwriter/enum.py +8 -1
  66. novelwriter/error.py +2 -2
  67. novelwriter/extensions/configlayout.py +7 -5
  68. novelwriter/extensions/eventfilters.py +1 -1
  69. novelwriter/extensions/modified.py +17 -5
  70. novelwriter/extensions/novelselector.py +1 -1
  71. novelwriter/extensions/pagedsidebar.py +4 -4
  72. novelwriter/extensions/progressbars.py +4 -4
  73. novelwriter/extensions/statusled.py +3 -3
  74. novelwriter/extensions/switch.py +3 -3
  75. novelwriter/extensions/switchbox.py +1 -1
  76. novelwriter/extensions/versioninfo.py +1 -1
  77. novelwriter/formats/shared.py +1 -1
  78. novelwriter/formats/todocx.py +35 -39
  79. novelwriter/formats/tohtml.py +15 -16
  80. novelwriter/formats/tokenizer.py +26 -22
  81. novelwriter/formats/tomarkdown.py +1 -1
  82. novelwriter/formats/toodt.py +54 -125
  83. novelwriter/formats/toqdoc.py +93 -45
  84. novelwriter/formats/toraw.py +1 -1
  85. novelwriter/gui/doceditor.py +233 -220
  86. novelwriter/gui/dochighlight.py +1 -1
  87. novelwriter/gui/docviewer.py +39 -10
  88. novelwriter/gui/docviewerpanel.py +15 -23
  89. novelwriter/gui/editordocument.py +1 -1
  90. novelwriter/gui/itemdetails.py +20 -27
  91. novelwriter/gui/mainmenu.py +14 -9
  92. novelwriter/gui/noveltree.py +13 -13
  93. novelwriter/gui/outline.py +18 -20
  94. novelwriter/gui/projtree.py +545 -1201
  95. novelwriter/gui/search.py +11 -19
  96. novelwriter/gui/sidebar.py +1 -1
  97. novelwriter/gui/statusbar.py +20 -3
  98. novelwriter/gui/theme.py +8 -4
  99. novelwriter/guimain.py +60 -48
  100. novelwriter/shared.py +53 -24
  101. novelwriter/text/counting.py +1 -1
  102. novelwriter/text/patterns.py +18 -6
  103. novelwriter/tools/dictionaries.py +1 -1
  104. novelwriter/tools/lipsum.py +1 -1
  105. novelwriter/tools/manusbuild.py +14 -12
  106. novelwriter/tools/manuscript.py +7 -7
  107. novelwriter/tools/manussettings.py +43 -53
  108. novelwriter/tools/noveldetails.py +1 -1
  109. novelwriter/tools/welcome.py +1 -1
  110. novelwriter/tools/writingstats.py +1 -1
  111. novelwriter/types.py +9 -3
  112. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/LICENSE.md +0 -0
  113. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/entry_points.txt +0 -0
  114. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2018-09-29 [0.0.1] NWProject
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2018 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -26,7 +26,6 @@ from __future__ import annotations
26
26
  import json
27
27
  import logging
28
28
 
29
- from collections.abc import Iterable
30
29
  from enum import Enum
31
30
  from functools import partial
32
31
  from pathlib import Path
@@ -52,7 +51,8 @@ from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
52
51
  from novelwriter.error import logException
53
52
 
54
53
  if TYPE_CHECKING: # pragma: no cover
55
- from novelwriter.core.item import NWItem
54
+ # Requires Python 3.10
55
+ from novelwriter.core.status import T_StatusKind, T_UpdateEntry
56
56
 
57
57
  logger = logging.getLogger(__name__)
58
58
 
@@ -67,6 +67,11 @@ class NWProjectState(Enum):
67
67
 
68
68
  class NWProject:
69
69
 
70
+ __slots__ = (
71
+ "_options", "_storage", "_data", "_tree", "_index", "_session",
72
+ "_langData", "_changed", "_valid", "_state", "tr",
73
+ )
74
+
70
75
  def __init__(self) -> None:
71
76
 
72
77
  # Core Elements
@@ -94,6 +99,12 @@ class NWProject:
94
99
  logger.debug("Delete: NWProject")
95
100
  return
96
101
 
102
+ def clear(self) -> None:
103
+ """Clear the project."""
104
+ self._tree.clear()
105
+ self._index.clear()
106
+ return
107
+
97
108
  ##
98
109
  # Properties
99
110
  ##
@@ -150,24 +161,46 @@ class NWProject:
150
161
  """Return total edit time, including the current session."""
151
162
  return self._data.editTime + round(time() - self._session.start)
152
163
 
164
+ @property
165
+ def currentTotalCount(self) -> int:
166
+ """Return the current total word count from the tree."""
167
+ return self._tree.model.root.count
168
+
153
169
  ##
154
170
  # Item Methods
155
171
  ##
156
172
 
157
- def newRoot(self, itemClass: nwItemClass, label: str | None = None) -> str:
173
+ def newRoot(self, itemClass: nwItemClass, pos: int = -1) -> str:
158
174
  """Add a new root folder to the project. If label is not set,
159
175
  use the class label.
160
176
  """
161
- label = label or trConst(nwLabels.CLASS_NAME[itemClass])
162
- return self._tree.create(label, None, nwItemType.ROOT, itemClass)
177
+ label = trConst(nwLabels.CLASS_NAME[itemClass])
178
+ return self._tree.create(label, None, nwItemType.ROOT, itemClass=itemClass, pos=pos)
163
179
 
164
- def newFolder(self, label: str, parent: str) -> str | None:
180
+ def newFolder(self, label: str, parent: str, pos: int = -1) -> str | None:
165
181
  """Add a new folder with a given label and parent item."""
166
- return self._tree.create(label, parent, nwItemType.FOLDER)
182
+ return self._tree.create(label, parent, nwItemType.FOLDER, pos=pos)
167
183
 
168
- def newFile(self, label: str, parent: str) -> str | None:
184
+ def newFile(self, label: str, parent: str, pos: int = -1) -> str | None:
169
185
  """Add a new file with a given label and parent item."""
170
- return self._tree.create(label, parent, nwItemType.FILE)
186
+ return self._tree.create(label, parent, nwItemType.FILE, pos=pos)
187
+
188
+ def removeItem(self, tHandle: str) -> bool:
189
+ """Remove an item from the project. This will delete both the
190
+ project entry and a document file if it exists.
191
+ """
192
+ if self._tree.checkType(tHandle, nwItemType.FILE):
193
+ SHARED.closeDocument(tHandle)
194
+ doc = self._storage.getDocument(tHandle)
195
+ if not doc.deleteDocument():
196
+ SHARED.error(
197
+ self.tr("Could not delete document file."),
198
+ info=doc.getError()
199
+ )
200
+ return False
201
+ self._index.deleteHandle(tHandle)
202
+ self._tree.remove(tHandle)
203
+ return True
171
204
 
172
205
  def writeNewFile(self, tHandle: str, hLevel: int, isDocument: bool, text: str = "") -> bool:
173
206
  """Write content to a new document after it is created. This
@@ -209,35 +242,21 @@ class NWProject:
209
242
  text = self._storage.getDocumentText(sHandle)
210
243
  self._storage.getDocument(tHandle).writeDocument(text)
211
244
  sItem.setLayout(tItem.itemLayout)
212
- self._index.scanText(tHandle, text)
245
+ self._index.reIndexHandle(tHandle)
213
246
 
214
247
  return True
215
248
 
216
- def removeItem(self, tHandle: str) -> bool:
217
- """Remove an item from the project. This will delete both the
218
- project entry and a document file if it exists.
249
+ def createNewNote(self, tag: str, itemClass: nwItemClass) -> None:
250
+ """Create a new note. This function is used by the document
251
+ editor to create note files for unknown tags.
219
252
  """
220
- if self._tree.checkType(tHandle, nwItemType.FILE):
221
- delDoc = self._storage.getDocument(tHandle)
222
- if not delDoc.deleteDocument():
223
- SHARED.error(
224
- self.tr("Could not delete document file."),
225
- info=delDoc.getError()
226
- )
227
- return False
228
-
229
- self._index.deleteHandle(tHandle)
230
- del self._tree[tHandle]
231
-
232
- return True
233
-
234
- def trashFolder(self) -> str:
235
- """Add the special trash root folder to the project."""
236
- trashHandle = self._tree.trashRoot
237
- if trashHandle is None:
238
- label = trConst(nwLabels.CLASS_NAME[nwItemClass.TRASH])
239
- return self._tree.create(label, None, nwItemType.ROOT, nwItemClass.TRASH)
240
- return trashHandle
253
+ if itemClass != nwItemClass.NO_CLASS:
254
+ if not (rHandle := self._tree.findRoot(itemClass)):
255
+ rHandle = self.newRoot(itemClass)
256
+ if rHandle and (tHandle := SHARED.project.newFile(tag.title(), rHandle)):
257
+ self.writeNewFile(tHandle, 1, False, f"@tag: {tag}\n\n")
258
+ self._tree.refreshItems([tHandle])
259
+ return
241
260
 
242
261
  ##
243
262
  # Project Methods
@@ -349,7 +368,7 @@ class NWProject:
349
368
  self._index.loadIndex()
350
369
  if xmlReader.state == XMLReadState.WAS_LEGACY:
351
370
  # Often, the index needs to be rebuilt when updating format
352
- self._index.rebuildIndex()
371
+ self._index.rebuild()
353
372
 
354
373
  self.updateWordCounts()
355
374
  self._session.startSession()
@@ -414,12 +433,11 @@ class NWProject:
414
433
  def closeProject(self, idleTime: float = 0.0) -> None:
415
434
  """Close the project."""
416
435
  logger.info("Closing project")
417
- self._index.clearIndex() # Triggers clear signal, see #1718
436
+ self._index.clear() # Triggers clear signal, see #1718
418
437
  self._options.saveSettings()
419
438
  self._tree.writeToCFile()
420
439
  self._session.appendSession(idleTime)
421
440
  self._storage.closeSession()
422
- self._lockedBy = None
423
441
  return
424
442
 
425
443
  def backupProject(self, doNotify: bool) -> bool:
@@ -489,17 +507,6 @@ class NWProject:
489
507
  self.setProjectChanged(True)
490
508
  return
491
509
 
492
- def setTreeOrder(self, order: list[str]) -> None:
493
- """A list representing the linear/flattened order of project
494
- items in the GUI project tree. The user can rearrange the order
495
- by drag-and-drop. Forwarded to the NWTree class.
496
- """
497
- if len(self._tree) != len(order):
498
- logger.warning("Sizes of new and old tree order do not match")
499
- self._tree.setOrder(order)
500
- self.setProjectChanged(True)
501
- return
502
-
503
510
  def setProjectChanged(self, status: bool) -> bool:
504
511
  """Toggle the project changed flag, and propagate the
505
512
  information to the GUI statusbar.
@@ -513,47 +520,6 @@ class NWProject:
513
520
  # Class Methods
514
521
  ##
515
522
 
516
- def iterProjectItems(self) -> Iterable[NWItem]:
517
- """This function ensures that the item tree loaded is sent to
518
- the GUI tree view in such a way that the tree can be built. That
519
- is, the parent item must be sent before its child. In principle,
520
- a proper XML file will already ensure that, but in the event the
521
- order has been altered, or a file is orphaned, this function is
522
- capable of handling it.
523
- """
524
- sentItems = set()
525
- iterItems = self._tree.handles()
526
- n = 0
527
- nMax = min(len(iterItems), 10000)
528
- while n < nMax:
529
- tHandle = iterItems[n]
530
- tItem = self._tree[tHandle]
531
- n += 1
532
- if tItem is None:
533
- # Technically a bug
534
- continue
535
- elif tItem.itemParent is None:
536
- # Item is a root, or already been identified as orphaned
537
- sentItems.add(tHandle)
538
- yield tItem
539
- elif tItem.itemParent in sentItems:
540
- # Item's parent has been sent, so all is fine
541
- sentItems.add(tHandle)
542
- yield tItem
543
- elif tItem.itemParent in iterItems:
544
- # Item's parent exists, but hasn't been sent yet, so add
545
- # it again to the end, but make sure this doesn't get
546
- # out hand, so we cap at 10000 items
547
- logger.warning("Item '%s' found before its parent", tHandle)
548
- iterItems.append(tHandle)
549
- nMax = min(len(iterItems), 10000)
550
- else:
551
- # Item is orphaned
552
- logger.error("Item '%s' has no parent in current tree", tHandle)
553
- tItem.setParent(None)
554
- yield tItem
555
- return
556
-
557
523
  def updateWordCounts(self) -> None:
558
524
  """Update the total word count values."""
559
525
  novel, notes = self._tree.sumWords()
@@ -574,6 +540,18 @@ class NWProject:
574
540
  self._data.itemImport.increment(nwItem.itemImport)
575
541
  return
576
542
 
543
+ def updateStatus(self, kind: T_StatusKind, update: T_UpdateEntry) -> None:
544
+ """Update status or import entries."""
545
+ if kind == "s":
546
+ self._data.itemStatus.update(update)
547
+ SHARED.emitStatusLabelsChanged(self, kind)
548
+ self._tree.refreshAllItems()
549
+ elif kind == "i":
550
+ self._data.itemImport.update(update)
551
+ SHARED.emitStatusLabelsChanged(self, kind)
552
+ self._tree.refreshAllItems()
553
+ return
554
+
577
555
  def localLookup(self, word: str | int) -> str:
578
556
  """Look up a word or number in the translation map for the
579
557
  project and return it. The variable is cast to a string before
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2022-10-30 [2.0rc2] NWProjectData
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -29,7 +29,8 @@ import uuid
29
29
  from typing import TYPE_CHECKING, Any
30
30
 
31
31
  from novelwriter.common import (
32
- checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
32
+ checkBool, checkInt, checkStringNone, checkUuid, isHandle,
33
+ makeFileNameSafe, simplified
33
34
  )
34
35
  from novelwriter.core.status import NWStatus
35
36
 
@@ -101,6 +102,11 @@ class NWProjectData:
101
102
  """Return the project name."""
102
103
  return self._name
103
104
 
105
+ @property
106
+ def fileSafeName(self) -> str:
107
+ """Return the project name in a file name safe format."""
108
+ return makeFileNameSafe(self._name)
109
+
104
110
  @property
105
111
  def author(self) -> str:
106
112
  """Return the project author."""
@@ -8,7 +8,7 @@ Created: 2022-09-28 [2.0rc2] ProjectXMLReader
8
8
  Created: 2022-10-31 [2.0rc2] ProjectXMLWriter
9
9
 
10
10
  This file is a part of novelWriter
11
- Copyright 2018–2024, Veronica Berglyd Olsen
11
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
12
12
 
13
13
  This program is free software: you can redistribute it and/or modify
14
14
  it under the terms of the GNU General Public License as published by
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2023-06-11 [2.1b1] NWSessionLog
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -7,7 +7,7 @@ Created: 2019-06-11 [0.1.5] NWSpellEnchant
7
7
  Created: 2023-06-13 [2.1b1] UserDictionary
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
11
11
 
12
12
  This program is free software: you can redistribute it and/or modify
13
13
  it under the terms of the GNU General Public License as published by
@@ -7,7 +7,7 @@ Created: 2019-05-19 [0.1.3] NWStatus
7
7
  Rewritten: 2022-04-05 [2.0b1] NWStatus
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
11
11
 
12
12
  This program is free software: you can redistribute it and/or modify
13
13
  it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@ from PyQt5.QtGui import QColor, QIcon, QPainter, QPainterPath, QPixmap, QPolygon
37
37
  from novelwriter import SHARED
38
38
  from novelwriter.common import simplified
39
39
  from novelwriter.enum import nwStatusShape
40
- from novelwriter.types import QtPaintAnitAlias, QtTransparent
40
+ from novelwriter.types import QtPaintAntiAlias, QtTransparent
41
41
 
42
42
  if TYPE_CHECKING: # pragma: no cover
43
43
  from typing import TypeGuard # Requires Python 3.10
@@ -65,6 +65,11 @@ class StatusEntry:
65
65
 
66
66
  NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
67
67
 
68
+ if TYPE_CHECKING: # pragma: no cover
69
+ # Requires Python 3.10
70
+ T_UpdateEntry = list[tuple[str | None, StatusEntry]]
71
+ T_StatusKind = Literal["s", "i"]
72
+
68
73
 
69
74
  class NWStatus:
70
75
 
@@ -73,7 +78,7 @@ class NWStatus:
73
78
 
74
79
  __slots__ = ("_store", "_default", "_prefix", "_height")
75
80
 
76
- def __init__(self, prefix: Literal["s", "i"]) -> None:
81
+ def __init__(self, prefix: T_StatusKind) -> None:
77
82
  self._store: dict[str, StatusEntry] = {}
78
83
  self._default = None
79
84
  self._prefix = prefix[:1]
@@ -120,7 +125,7 @@ class NWStatus:
120
125
 
121
126
  return key
122
127
 
123
- def update(self, update: list[tuple[str | None, StatusEntry]]) -> None:
128
+ def update(self, update: T_UpdateEntry) -> None:
124
129
  """Update the list of statuses."""
125
130
  self._store.clear()
126
131
  for key, entry in update:
@@ -130,9 +135,6 @@ class NWStatus:
130
135
  if self._default not in self._store:
131
136
  self._default = next(iter(self._store)) if self._store else None
132
137
 
133
- # Emit the change signal
134
- SHARED.projectSingalProxy({"event": "statusLabels", "kind": self._prefix})
135
-
136
138
  return
137
139
 
138
140
  def check(self, value: str) -> str:
@@ -172,6 +174,20 @@ class NWStatus:
172
174
  """Yield entries from the status icons."""
173
175
  yield from self._store.items()
174
176
 
177
+ def fromRaw(self, data: list[str]) -> StatusEntry | None:
178
+ """Create a StatusEntry from a list of three strings consisting
179
+ of shape, colour, and name. This entry is not automatically
180
+ added to the list of entries.
181
+ """
182
+ try:
183
+ shape = nwStatusShape[str(data[0])]
184
+ color = QColor(str(data[1]))
185
+ icon = NWStatus.createIcon(self._height, color, shape)
186
+ return StatusEntry(simplified(data[2]), color, shape, icon)
187
+ except Exception:
188
+ logger.error("Could not parse entry %s", str(data))
189
+ return None
190
+
175
191
  @staticmethod
176
192
  def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
177
193
  """Generate an icon for a status label."""
@@ -179,7 +195,7 @@ class NWStatus:
179
195
  pixmap.fill(QtTransparent)
180
196
 
181
197
  painter = QPainter(pixmap)
182
- painter.setRenderHint(QtPaintAnitAlias)
198
+ painter.setRenderHint(QtPaintAntiAlias)
183
199
  painter.fillPath(_SHAPES.getShape(shape), color)
184
200
  painter.end()
185
201
 
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2022-11-01 [2.0rc2] NWStorage
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by