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.
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/METADATA +4 -4
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/RECORD +114 -98
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/WHEEL +1 -1
- novelwriter/__init__.py +50 -11
- novelwriter/assets/i18n/nw_de_DE.qm +0 -0
- novelwriter/assets/i18n/nw_en_US.qm +0 -0
- novelwriter/assets/i18n/nw_es_419.qm +0 -0
- novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
- novelwriter/assets/i18n/nw_it_IT.qm +0 -0
- novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
- novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
- novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
- novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
- novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
- novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
- novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
- novelwriter/assets/i18n/project_de_DE.json +2 -2
- novelwriter/assets/i18n/project_ru_RU.json +11 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +7 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +5 -0
- novelwriter/assets/icons/typicons_light/icons.conf +7 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-height.svg +6 -0
- novelwriter/assets/icons/typicons_light/mixed_size-width.svg +6 -0
- novelwriter/assets/icons/typicons_light/nw_toolbar.svg +5 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/text/credits_en.htm +1 -0
- novelwriter/common.py +38 -3
- novelwriter/config.py +19 -13
- novelwriter/constants.py +60 -45
- novelwriter/core/buildsettings.py +1 -1
- novelwriter/core/coretools.py +112 -126
- novelwriter/core/docbuild.py +4 -3
- novelwriter/core/document.py +1 -1
- novelwriter/core/index.py +10 -20
- novelwriter/core/item.py +40 -7
- novelwriter/core/itemmodel.py +518 -0
- novelwriter/core/options.py +1 -1
- novelwriter/core/project.py +68 -90
- novelwriter/core/projectdata.py +8 -2
- novelwriter/core/projectxml.py +1 -1
- novelwriter/core/sessions.py +1 -1
- novelwriter/core/spellcheck.py +1 -1
- novelwriter/core/status.py +24 -8
- novelwriter/core/storage.py +1 -1
- novelwriter/core/tree.py +269 -288
- novelwriter/dialogs/about.py +1 -1
- novelwriter/dialogs/docmerge.py +8 -18
- novelwriter/dialogs/docsplit.py +1 -1
- novelwriter/dialogs/editlabel.py +1 -1
- novelwriter/dialogs/preferences.py +4 -4
- novelwriter/dialogs/projectsettings.py +148 -98
- novelwriter/dialogs/quotes.py +1 -1
- novelwriter/dialogs/wordlist.py +11 -10
- novelwriter/enum.py +8 -1
- novelwriter/error.py +2 -2
- novelwriter/extensions/configlayout.py +7 -5
- novelwriter/extensions/eventfilters.py +1 -1
- novelwriter/extensions/modified.py +17 -5
- novelwriter/extensions/novelselector.py +1 -1
- novelwriter/extensions/pagedsidebar.py +4 -4
- novelwriter/extensions/progressbars.py +4 -4
- novelwriter/extensions/statusled.py +3 -3
- novelwriter/extensions/switch.py +3 -3
- novelwriter/extensions/switchbox.py +1 -1
- novelwriter/extensions/versioninfo.py +1 -1
- novelwriter/formats/shared.py +1 -1
- novelwriter/formats/todocx.py +35 -39
- novelwriter/formats/tohtml.py +15 -16
- novelwriter/formats/tokenizer.py +26 -22
- novelwriter/formats/tomarkdown.py +1 -1
- novelwriter/formats/toodt.py +54 -125
- novelwriter/formats/toqdoc.py +93 -45
- novelwriter/formats/toraw.py +1 -1
- novelwriter/gui/doceditor.py +233 -220
- novelwriter/gui/dochighlight.py +1 -1
- novelwriter/gui/docviewer.py +39 -10
- novelwriter/gui/docviewerpanel.py +15 -23
- novelwriter/gui/editordocument.py +1 -1
- novelwriter/gui/itemdetails.py +20 -27
- novelwriter/gui/mainmenu.py +14 -9
- novelwriter/gui/noveltree.py +13 -13
- novelwriter/gui/outline.py +18 -20
- novelwriter/gui/projtree.py +545 -1201
- novelwriter/gui/search.py +11 -19
- novelwriter/gui/sidebar.py +1 -1
- novelwriter/gui/statusbar.py +20 -3
- novelwriter/gui/theme.py +8 -4
- novelwriter/guimain.py +60 -48
- novelwriter/shared.py +53 -24
- novelwriter/text/counting.py +1 -1
- novelwriter/text/patterns.py +18 -6
- novelwriter/tools/dictionaries.py +1 -1
- novelwriter/tools/lipsum.py +1 -1
- novelwriter/tools/manusbuild.py +14 -12
- novelwriter/tools/manuscript.py +7 -7
- novelwriter/tools/manussettings.py +43 -53
- novelwriter/tools/noveldetails.py +1 -1
- novelwriter/tools/welcome.py +1 -1
- novelwriter/tools/writingstats.py +1 -1
- novelwriter/types.py +9 -3
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/top_level.txt +0 -0
novelwriter/core/project.py
CHANGED
@@ -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
|
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
|
-
|
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,
|
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 =
|
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.
|
245
|
+
self._index.reIndexHandle(tHandle)
|
213
246
|
|
214
247
|
return True
|
215
248
|
|
216
|
-
def
|
217
|
-
"""
|
218
|
-
|
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
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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.
|
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.
|
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
|
novelwriter/core/projectdata.py
CHANGED
@@ -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
|
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,
|
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."""
|
novelwriter/core/projectxml.py
CHANGED
@@ -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
|
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
|
novelwriter/core/sessions.py
CHANGED
@@ -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
|
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
|
novelwriter/core/spellcheck.py
CHANGED
@@ -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
|
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
|
novelwriter/core/status.py
CHANGED
@@ -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
|
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
|
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:
|
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:
|
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(
|
198
|
+
painter.setRenderHint(QtPaintAntiAlias)
|
183
199
|
painter.fillPath(_SHAPES.getShape(shape), color)
|
184
200
|
painter.end()
|
185
201
|
|
novelwriter/core/storage.py
CHANGED
@@ -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
|
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
|