Annotate core modules
This commit is contained in:
@@ -30,7 +30,7 @@ import logging
|
|||||||
import unicodedata
|
import unicodedata
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from typing import Any, Literal
|
from typing import Any, Literal, TypeGuard
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
@@ -134,7 +134,7 @@ def checkPath(value: Any, default: Path) -> Path:
|
|||||||
# Validator Functions
|
# Validator Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def isHandle(value: Any) -> bool:
|
def isHandle(value: Any) -> TypeGuard[str]:
|
||||||
"""Check if a string is a valid novelWriter handle.
|
"""Check if a string is a valid novelWriter handle.
|
||||||
Note: This is case sensitive. Must be lower case!
|
Note: This is case sensitive. Must be lower case!
|
||||||
"""
|
"""
|
||||||
@@ -148,7 +148,7 @@ def isHandle(value: Any) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def isTitleTag(value: Any) -> bool:
|
def isTitleTag(value: Any) -> TypeGuard[str]:
|
||||||
"""Check if a string is a valid title tag string."""
|
"""Check if a string is a valid title tag string."""
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
return False
|
return False
|
||||||
@@ -288,8 +288,7 @@ def transferCase(source: str, target: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def fuzzyTime(seconds: int) -> str:
|
def fuzzyTime(seconds: int) -> str:
|
||||||
"""Converts a time difference in seconds into a fuzzy time string.
|
"""Convert a time difference in seconds into a fuzzy time string."""
|
||||||
"""
|
|
||||||
if seconds < 0:
|
if seconds < 0:
|
||||||
return QCoreApplication.translate(
|
return QCoreApplication.translate(
|
||||||
"Common", "in the future"
|
"Common", "in the future"
|
||||||
@@ -349,8 +348,7 @@ def fuzzyTime(seconds: int) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def numberToRoman(value: int, toLower: bool = False) -> str:
|
def numberToRoman(value: int, toLower: bool = False) -> str:
|
||||||
"""Convert an integer to a Roman number.
|
"""Convert an integer to a Roman number."""
|
||||||
"""
|
|
||||||
if not isinstance(value, int):
|
if not isinstance(value, int):
|
||||||
return "NAN"
|
return "NAN"
|
||||||
if value < 1 or value > 4999:
|
if value < 1 or value > 4999:
|
||||||
@@ -423,12 +421,14 @@ def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
|
|||||||
return "".join(buffer)
|
return "".join(buffer)
|
||||||
|
|
||||||
|
|
||||||
def xmlIndent(tree: ET.Element | ET.ElementTree):
|
def xmlIndent(tree: ET.Element | ET.ElementTree) -> None:
|
||||||
"""A modified version of the XML indent function in the standard
|
"""A modified version of the XML indent function in the standard
|
||||||
library. It behaves more closely to how the one from lxml does.
|
library. It behaves more closely to how the one from lxml does.
|
||||||
"""
|
"""
|
||||||
if isinstance(tree, ET.ElementTree):
|
if isinstance(tree, ET.ElementTree):
|
||||||
tree = tree.getroot()
|
tree = tree.getroot()
|
||||||
|
if not isinstance(tree, ET.Element):
|
||||||
|
return
|
||||||
|
|
||||||
indentations = ["\n"]
|
indentations = ["\n"]
|
||||||
|
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ class BuildSettings:
|
|||||||
The settings can be packed/unpacked to/from a dictionary for JSON.
|
The settings can be packed/unpacked to/from a dictionary for JSON.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self._name = ""
|
self._name = ""
|
||||||
self._uuid = str(uuid.uuid4())
|
self._uuid = str(uuid.uuid4())
|
||||||
self._path = Path.home()
|
self._path = Path.home()
|
||||||
@@ -239,12 +239,12 @@ class BuildSettings:
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setName(self, name: str):
|
def setName(self, name: str) -> None:
|
||||||
"""Set the build setting display name."""
|
"""Set the build setting display name."""
|
||||||
self._name = str(name)
|
self._name = str(name)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBuildID(self, value: str | uuid.UUID):
|
def setBuildID(self, value: str | uuid.UUID) -> None:
|
||||||
"""Set a UUID build ID."""
|
"""Set a UUID build ID."""
|
||||||
value = checkUuid(value, "")
|
value = checkUuid(value, "")
|
||||||
if not value:
|
if not value:
|
||||||
@@ -253,7 +253,7 @@ class BuildSettings:
|
|||||||
self._uuid = value
|
self._uuid = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLastPath(self, path: Path | str | None):
|
def setLastPath(self, path: Path | str | None) -> None:
|
||||||
"""Set the last used build path."""
|
"""Set the last used build path."""
|
||||||
if isinstance(path, str):
|
if isinstance(path, str):
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
@@ -264,41 +264,41 @@ class BuildSettings:
|
|||||||
self._changed = True
|
self._changed = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLastBuildName(self, name: str):
|
def setLastBuildName(self, name: str) -> None:
|
||||||
"""Set the last used build name."""
|
"""Set the last used build name."""
|
||||||
self._build = str(name).strip()
|
self._build = str(name).strip()
|
||||||
self._changed = True
|
self._changed = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLastFormat(self, value: nwBuildFmt):
|
def setLastFormat(self, value: nwBuildFmt) -> None:
|
||||||
"""Set the last used build format."""
|
"""Set the last used build format."""
|
||||||
if isinstance(value, nwBuildFmt):
|
if isinstance(value, nwBuildFmt):
|
||||||
self._format = value
|
self._format = value
|
||||||
self._changed = True
|
self._changed = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFiltered(self, tHandle: str):
|
def setFiltered(self, tHandle: str) -> None:
|
||||||
"""Set an item as filtered."""
|
"""Set an item as filtered."""
|
||||||
self._excluded.discard(tHandle)
|
self._excluded.discard(tHandle)
|
||||||
self._included.discard(tHandle)
|
self._included.discard(tHandle)
|
||||||
self._changed = True
|
self._changed = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def setIncluded(self, tHandle: str):
|
def setIncluded(self, tHandle: str) -> None:
|
||||||
"""Set an item as explicitly included."""
|
"""Set an item as explicitly included."""
|
||||||
self._excluded.discard(tHandle)
|
self._excluded.discard(tHandle)
|
||||||
self._included.add(tHandle)
|
self._included.add(tHandle)
|
||||||
self._changed = True
|
self._changed = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def setExcluded(self, tHandle: str):
|
def setExcluded(self, tHandle: str) -> None:
|
||||||
"""Set an item as explicitly excluded."""
|
"""Set an item as explicitly excluded."""
|
||||||
self._excluded.add(tHandle)
|
self._excluded.add(tHandle)
|
||||||
self._included.discard(tHandle)
|
self._included.discard(tHandle)
|
||||||
self._changed = True
|
self._changed = True
|
||||||
return
|
return
|
||||||
|
|
||||||
def setAllowRoot(self, tHandle: str, state: bool):
|
def setAllowRoot(self, tHandle: str, state: bool) -> None:
|
||||||
"""Set a specific root folder as allowed or not."""
|
"""Set a specific root folder as allowed or not."""
|
||||||
if state is True:
|
if state is True:
|
||||||
self._skipRoot.discard(tHandle)
|
self._skipRoot.discard(tHandle)
|
||||||
@@ -386,7 +386,7 @@ class BuildSettings:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def resetChangedState(self):
|
def resetChangedState(self) -> None:
|
||||||
"""Reset the changed status of the settings object. This must be
|
"""Reset the changed status of the settings object. This must be
|
||||||
called when the changes have been safely saved or passed on.
|
called when the changes have been safely saved or passed on.
|
||||||
"""
|
"""
|
||||||
@@ -410,7 +410,7 @@ class BuildSettings:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def unpack(self, data: dict):
|
def unpack(self, data: dict) -> None:
|
||||||
"""Unpack a dictionary and populate the class."""
|
"""Unpack a dictionary and populate the class."""
|
||||||
settings = data.get("settings", {})
|
settings = data.get("settings", {})
|
||||||
content = data.get("content", {})
|
content = data.get("content", {})
|
||||||
@@ -454,13 +454,13 @@ class BuildCollection:
|
|||||||
project folder.
|
project folder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._builds = {}
|
self._builds = {}
|
||||||
self._loadCollection()
|
self._loadCollection()
|
||||||
return
|
return
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self) -> int:
|
||||||
"""Return the number of builds."""
|
"""Return the number of builds."""
|
||||||
return len(self._builds)
|
return len(self._builds)
|
||||||
|
|
||||||
@@ -476,7 +476,7 @@ class BuildCollection:
|
|||||||
build.unpack(self._builds[buildID])
|
build.unpack(self._builds[buildID])
|
||||||
return build
|
return build
|
||||||
|
|
||||||
def setBuild(self, build: BuildSettings):
|
def setBuild(self, build: BuildSettings) -> None:
|
||||||
"""Set build settings data in the collection."""
|
"""Set build settings data in the collection."""
|
||||||
if isinstance(build, BuildSettings):
|
if isinstance(build, BuildSettings):
|
||||||
buildID = build.buildID
|
buildID = build.buildID
|
||||||
@@ -484,7 +484,7 @@ class BuildCollection:
|
|||||||
self._saveCollection()
|
self._saveCollection()
|
||||||
return
|
return
|
||||||
|
|
||||||
def removeBuild(self, buildID: str):
|
def removeBuild(self, buildID: str) -> None:
|
||||||
"""Remove the a build from the collection."""
|
"""Remove the a build from the collection."""
|
||||||
self._builds.pop(buildID, None)
|
self._builds.pop(buildID, None)
|
||||||
self._saveCollection()
|
self._saveCollection()
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class DocMerger:
|
|||||||
and a new doc label. Calling this function resets the class.
|
and a new doc label. Calling this function resets the class.
|
||||||
"""
|
"""
|
||||||
srcItem = self._project.tree[srcHandle]
|
srcItem = self._project.tree[srcHandle]
|
||||||
if srcItem is None:
|
if srcItem is None or srcItem.itemParent is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
newHandle = self._project.newFile(docLabel, srcItem.itemParent)
|
newHandle = self._project.newFile(docLabel, srcItem.itemParent)
|
||||||
@@ -210,7 +210,7 @@ class DocSplitter:
|
|||||||
"""An iterator that will write each document in the buffer, and
|
"""An iterator that will write each document in the buffer, and
|
||||||
return its new handle, parent handle, and sibling handle.
|
return its new handle, parent handle, and sibling handle.
|
||||||
"""
|
"""
|
||||||
if self._srcHandle is None or self._srcItem is None:
|
if self._srcHandle is None or self._srcItem is None or self._parHandle is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
pHandle = self._parHandle
|
pHandle = self._parHandle
|
||||||
@@ -385,9 +385,10 @@ class ProjectBuilder:
|
|||||||
aDoc = project.storage.getDocument(hChapter)
|
aDoc = project.storage.getDocument(hChapter)
|
||||||
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
|
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
|
||||||
|
|
||||||
hScene = project.newFile(lblNewScene, hChapter)
|
if hChapter:
|
||||||
aDoc = project.storage.getDocument(hScene)
|
hScene = project.newFile(lblNewScene, hChapter)
|
||||||
aDoc.writeDocument(f"### {lblNewScene}\n\n")
|
aDoc = project.storage.getDocument(hScene)
|
||||||
|
aDoc.writeDocument(f"### {lblNewScene}\n\n")
|
||||||
|
|
||||||
project.newRoot(nwItemClass.PLOT)
|
project.newRoot(nwItemClass.PLOT)
|
||||||
project.newRoot(nwItemClass.CHARACTER)
|
project.newRoot(nwItemClass.CHARACTER)
|
||||||
@@ -418,7 +419,7 @@ class ProjectBuilder:
|
|||||||
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
|
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
|
||||||
|
|
||||||
# Create chapter scenes
|
# Create chapter scenes
|
||||||
if numScenes > 0:
|
if numScenes > 0 and cHandle:
|
||||||
for sc in range(numScenes):
|
for sc in range(numScenes):
|
||||||
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
||||||
sHandle = project.newFile(scTitle, cHandle)
|
sHandle = project.newFile(scTitle, cHandle)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class NWBuildDocument:
|
|||||||
|
|
||||||
__slots__ = ("_project", "_build", "_queue", "_error", "_cache")
|
__slots__ = ("_project", "_build", "_queue", "_error", "_cache")
|
||||||
|
|
||||||
def __init__(self, project: NWProject, build: BuildSettings):
|
def __init__(self, project: NWProject, build: BuildSettings) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._build = build
|
self._build = build
|
||||||
self._queue = []
|
self._queue = []
|
||||||
@@ -91,12 +91,12 @@ class NWBuildDocument:
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def addDocument(self, tHandle: str):
|
def addDocument(self, tHandle: str) -> None:
|
||||||
"""Add a document to the build queue manually."""
|
"""Add a document to the build queue manually."""
|
||||||
self._queue.append(tHandle)
|
self._queue.append(tHandle)
|
||||||
return
|
return
|
||||||
|
|
||||||
def queueAll(self):
|
def queueAll(self) -> None:
|
||||||
"""Queue all document as defined by the build settings."""
|
"""Queue all document as defined by the build settings."""
|
||||||
self._queue = []
|
self._queue = []
|
||||||
filtered = self._build.buildItemFilter(self._project)
|
filtered = self._build.buildItemFilter(self._project)
|
||||||
|
|||||||
@@ -26,13 +26,16 @@ from __future__ import annotations
|
|||||||
import uuid
|
import uuid
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from typing import Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
|
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
|
||||||
)
|
)
|
||||||
from novelwriter.core.status import NWStatus
|
from novelwriter.core.status import NWStatus
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -43,7 +46,7 @@ class NWProjectData:
|
|||||||
the list of project items.
|
the list of project items.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project):
|
def __init__(self, project: NWProject) -> None:
|
||||||
|
|
||||||
self._project = project
|
self._project = project
|
||||||
|
|
||||||
@@ -184,13 +187,13 @@ class NWProjectData:
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def incSaveCount(self):
|
def incSaveCount(self) -> None:
|
||||||
"""Increment the save count by one."""
|
"""Increment the save count by one."""
|
||||||
self._saveCount += 1
|
self._saveCount += 1
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def incAutoCount(self):
|
def incAutoCount(self) -> None:
|
||||||
"""Increment the auto save count by one."""
|
"""Increment the auto save count by one."""
|
||||||
self._autoCount += 1
|
self._autoCount += 1
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
@@ -208,7 +211,7 @@ class NWProjectData:
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setUuid(self, value: Any):
|
def setUuid(self, value: Any) -> None:
|
||||||
"""Set the project id."""
|
"""Set the project id."""
|
||||||
value = checkUuid(value, "")
|
value = checkUuid(value, "")
|
||||||
if not value:
|
if not value:
|
||||||
@@ -218,74 +221,74 @@ class NWProjectData:
|
|||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setName(self, value: str | None):
|
def setName(self, value: str | None) -> None:
|
||||||
"""Set a new project name."""
|
"""Set a new project name."""
|
||||||
if value != self._name:
|
if value != self._name:
|
||||||
self._name = simplified(str(value or ""))
|
self._name = simplified(str(value or ""))
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTitle(self, value: str | None):
|
def setTitle(self, value: str | None) -> None:
|
||||||
"""Set a new novel title."""
|
"""Set a new novel title."""
|
||||||
if value != self._title:
|
if value != self._title:
|
||||||
self._title = simplified(str(value or ""))
|
self._title = simplified(str(value or ""))
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setAuthor(self, value: str | None):
|
def setAuthor(self, value: str | None) -> None:
|
||||||
"""Set the author value."""
|
"""Set the author value."""
|
||||||
if value != self._title:
|
if value != self._title:
|
||||||
self._author = simplified(str(value or ""))
|
self._author = simplified(str(value or ""))
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSaveCount(self, value: Any):
|
def setSaveCount(self, value: Any) -> None:
|
||||||
"""Set the save count from last session."""
|
"""Set the save count from last session."""
|
||||||
self._saveCount = checkInt(value, 0)
|
self._saveCount = checkInt(value, 0)
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setAutoCount(self, value: Any):
|
def setAutoCount(self, value: Any) -> None:
|
||||||
"""Set the auto save count from last session."""
|
"""Set the auto save count from last session."""
|
||||||
self._autoCount = checkInt(value, 0)
|
self._autoCount = checkInt(value, 0)
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setEditTime(self, value: Any):
|
def setEditTime(self, value: Any) -> None:
|
||||||
"""Set the edit time from last session."""
|
"""Set the edit time from last session."""
|
||||||
self._editTime = checkInt(value, 0)
|
self._editTime = checkInt(value, 0)
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setDoBackup(self, value: Any):
|
def setDoBackup(self, value: Any) -> None:
|
||||||
"""Set the do write backup flag."""
|
"""Set the do write backup flag."""
|
||||||
if value != self._doBackup:
|
if value != self._doBackup:
|
||||||
self._doBackup = checkBool(value, False)
|
self._doBackup = checkBool(value, False)
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLanguage(self, value: str | None):
|
def setLanguage(self, value: str | None) -> None:
|
||||||
"""Set the project language."""
|
"""Set the project language."""
|
||||||
if value != self._language:
|
if value != self._language:
|
||||||
self._language = checkStringNone(value, None)
|
self._language = checkStringNone(value, None)
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSpellCheck(self, value: Any):
|
def setSpellCheck(self, value: Any) -> None:
|
||||||
"""Set the spell check flag."""
|
"""Set the spell check flag."""
|
||||||
if value != self._spellCheck:
|
if value != self._spellCheck:
|
||||||
self._spellCheck = checkBool(value, False)
|
self._spellCheck = checkBool(value, False)
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSpellLang(self, value: str | None):
|
def setSpellLang(self, value: str | None) -> None:
|
||||||
"""Set the spell check language."""
|
"""Set the spell check language."""
|
||||||
if value != self._spellLang:
|
if value != self._spellLang:
|
||||||
self._spellLang = checkStringNone(value, None)
|
self._spellLang = checkStringNone(value, None)
|
||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLastHandle(self, value: str | None, component: str):
|
def setLastHandle(self, value: str | None, component: str) -> None:
|
||||||
"""Set a last used handle into the handle registry for a given
|
"""Set a last used handle into the handle registry for a given
|
||||||
component.
|
component.
|
||||||
"""
|
"""
|
||||||
@@ -294,7 +297,7 @@ class NWProjectData:
|
|||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLastHandles(self, value: dict):
|
def setLastHandles(self, value: dict) -> None:
|
||||||
"""Set the full last handles dictionary to a new set of values.
|
"""Set the full last handles dictionary to a new set of values.
|
||||||
This is intended to be used at project load.
|
This is intended to be used at project load.
|
||||||
"""
|
"""
|
||||||
@@ -305,7 +308,7 @@ class NWProjectData:
|
|||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setInitCounts(self, novel: Any = None, notes: Any = None):
|
def setInitCounts(self, novel: Any = None, notes: Any = None) -> None:
|
||||||
"""Set the word count totals for novel and note files."""
|
"""Set the word count totals for novel and note files."""
|
||||||
if novel is not None:
|
if novel is not None:
|
||||||
self._initCounts[0] = checkInt(novel, 0)
|
self._initCounts[0] = checkInt(novel, 0)
|
||||||
@@ -315,7 +318,7 @@ class NWProjectData:
|
|||||||
self._currCounts[1] = checkInt(notes, 0)
|
self._currCounts[1] = checkInt(notes, 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setCurrCounts(self, novel: Any = None, notes: Any = None):
|
def setCurrCounts(self, novel: Any = None, notes: Any = None) -> None:
|
||||||
"""Set the word count totals for novel and note files."""
|
"""Set the word count totals for novel and note files."""
|
||||||
if novel is not None:
|
if novel is not None:
|
||||||
self._currCounts[0] = checkInt(novel, 0)
|
self._currCounts[0] = checkInt(novel, 0)
|
||||||
@@ -323,7 +326,7 @@ class NWProjectData:
|
|||||||
self._currCounts[1] = checkInt(notes, 0)
|
self._currCounts[1] = checkInt(notes, 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setAutoReplace(self, value: dict):
|
def setAutoReplace(self, value: dict) -> None:
|
||||||
"""Set the auto-replace dictionary."""
|
"""Set the auto-replace dictionary."""
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
self._autoReplace = {}
|
self._autoReplace = {}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class ProjectXMLReader:
|
|||||||
Rev 1: Drops the titleFormat section of settings.
|
Rev 1: Drops the titleFormat section of settings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path: str | Path) -> None:
|
||||||
self._path = Path(path)
|
self._path = Path(path)
|
||||||
self._state = XMLReadState.NO_ACTION
|
self._state = XMLReadState.NO_ACTION
|
||||||
self._root = ""
|
self._root = ""
|
||||||
@@ -236,7 +236,7 @@ class ProjectXMLReader:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData):
|
def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData) -> None:
|
||||||
"""Parse the project section of the XML file."""
|
"""Parse the project section of the XML file."""
|
||||||
logger.debug("Parsing <project> section")
|
logger.debug("Parsing <project> section")
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ class ProjectXMLReader:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData):
|
def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData) -> None:
|
||||||
"""Parse the settings section of the XML file."""
|
"""Parse the settings section of the XML file."""
|
||||||
logger.debug("Parsing <settings> section")
|
logger.debug("Parsing <settings> section")
|
||||||
|
|
||||||
@@ -307,7 +307,9 @@ class ProjectXMLReader:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseProjectContent(self, xSection: ET.Element, data: NWProjectData, content: list):
|
def _parseProjectContent(
|
||||||
|
self, xSection: ET.Element, data: NWProjectData, content: list
|
||||||
|
) -> None:
|
||||||
"""Parse the content section of the XML file."""
|
"""Parse the content section of the XML file."""
|
||||||
logger.debug("Parsing <content> section")
|
logger.debug("Parsing <content> section")
|
||||||
|
|
||||||
@@ -362,7 +364,9 @@ class ProjectXMLReader:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseProjectContentLegacy(self, xSection: ET.Element, data: NWProjectData, content: list):
|
def _parseProjectContentLegacy(
|
||||||
|
self, xSection: ET.Element, data: NWProjectData, content: list
|
||||||
|
) -> None:
|
||||||
"""Parse the content section of the XML file for older versions."""
|
"""Parse the content section of the XML file for older versions."""
|
||||||
logger.debug("Parsing <content> section (legacy format)")
|
logger.debug("Parsing <content> section (legacy format)")
|
||||||
|
|
||||||
@@ -438,7 +442,7 @@ class ProjectXMLReader:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus):
|
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus) -> None:
|
||||||
"""Parse a status or importance entry."""
|
"""Parse a status or importance entry."""
|
||||||
for xEntry in xItem:
|
for xEntry in xItem:
|
||||||
if xEntry.tag == "entry":
|
if xEntry.tag == "entry":
|
||||||
@@ -447,7 +451,7 @@ class ProjectXMLReader:
|
|||||||
green = checkInt(xEntry.attrib.get("green", 0), 0)
|
green = checkInt(xEntry.attrib.get("green", 0), 0)
|
||||||
blue = checkInt(xEntry.attrib.get("blue", 0), 0)
|
blue = checkInt(xEntry.attrib.get("blue", 0), 0)
|
||||||
count = checkInt(xEntry.attrib.get("count", 0), 0)
|
count = checkInt(xEntry.attrib.get("count", 0), 0)
|
||||||
sObject.write(key, xEntry.text, (red, green, blue), count)
|
sObject.write(key, xEntry.text or "", (red, green, blue), count)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
|
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
|
||||||
@@ -460,7 +464,7 @@ class ProjectXMLReader:
|
|||||||
result[xEntry.attrib["key"]] = checkString(xEntry.text, "")
|
result[xEntry.attrib["key"]] = checkString(xEntry.text, "")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _parseDictTagText(self, xItem):
|
def _parseDictTagText(self, xItem) -> dict:
|
||||||
"""Parse a dictionary stored with key as the tag and the value
|
"""Parse a dictionary stored with key as the tag and the value
|
||||||
as the text property.
|
as the text property.
|
||||||
"""
|
"""
|
||||||
@@ -476,7 +480,7 @@ class ProjectXMLWriter:
|
|||||||
very latest spec.
|
very latest spec.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path: str | Path) -> None:
|
||||||
self._path = Path(path)
|
self._path = Path(path)
|
||||||
self._error = None
|
self._error = None
|
||||||
return
|
return
|
||||||
@@ -585,13 +589,13 @@ class ProjectXMLWriter:
|
|||||||
|
|
||||||
def _packSingleValue(
|
def _packSingleValue(
|
||||||
self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None
|
self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None
|
||||||
):
|
) -> None:
|
||||||
"""Pack a single value into an XML element."""
|
"""Pack a single value into an XML element."""
|
||||||
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
||||||
xItem.text = str(value) or ""
|
xItem.text = str(value) or ""
|
||||||
return
|
return
|
||||||
|
|
||||||
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict):
|
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict) -> None:
|
||||||
"""Pack the entries of a dictionary into an XML element."""
|
"""Pack the entries of a dictionary into an XML element."""
|
||||||
xItem = ET.SubElement(xParent, name)
|
xItem = ET.SubElement(xParent, name)
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class NWSessionLog:
|
|||||||
format. That is, one JSON object per line.
|
format. That is, one JSON object per line.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._start = 0.0
|
self._start = 0.0
|
||||||
return
|
return
|
||||||
@@ -65,7 +65,7 @@ class NWSessionLog:
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def startSession(self):
|
def startSession(self) -> None:
|
||||||
"""Start the writing session."""
|
"""Start the writing session."""
|
||||||
self._start = time()
|
self._start = time()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class NWSpellEnchant:
|
|||||||
between spell check tools.
|
between spell check tools.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._dictObj = FakeEnchant()
|
self._dictObj = FakeEnchant()
|
||||||
self._userDict = UserDictionary(project)
|
self._userDict = UserDictionary(project)
|
||||||
@@ -165,7 +165,7 @@ class NWSpellEnchant:
|
|||||||
|
|
||||||
class FakeEnchant:
|
class FakeEnchant:
|
||||||
"""Fallback for when Enchant is selected, but not installed."""
|
"""Fallback for when Enchant is selected, but not installed."""
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
|
|
||||||
class FakeProvider:
|
class FakeProvider:
|
||||||
name = ""
|
name = ""
|
||||||
@@ -189,7 +189,7 @@ class FakeEnchant:
|
|||||||
|
|
||||||
class UserDictionary:
|
class UserDictionary:
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._words = set()
|
self._words = set()
|
||||||
self._path = None
|
self._path = None
|
||||||
@@ -210,7 +210,7 @@ class UserDictionary:
|
|||||||
self._words.add(word)
|
self._words.add(word)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def load(self):
|
def load(self) -> None:
|
||||||
"""Load the user's dictionary."""
|
"""Load the user's dictionary."""
|
||||||
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
|
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
|
||||||
if not isinstance(self._path, Path):
|
if not isinstance(self._path, Path):
|
||||||
@@ -224,7 +224,7 @@ class UserDictionary:
|
|||||||
logException()
|
logException()
|
||||||
return
|
return
|
||||||
|
|
||||||
def save(self):
|
def save(self) -> None:
|
||||||
"""Save the user's dictionary."""
|
"""Save the user's dictionary."""
|
||||||
if self._path is None:
|
if self._path is None:
|
||||||
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
|
self._path = self._project.storage.getMetaFile(nwFiles.DICT_FILE)
|
||||||
|
|||||||
+43
-61
@@ -27,6 +27,8 @@ from __future__ import annotations
|
|||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from typing import ItemsView, Iterator, KeysView, Literal, TypeGuard, ValuesView
|
||||||
|
|
||||||
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
|
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
|
||||||
from PyQt5.QtCore import QRectF, Qt
|
from PyQt5.QtCore import QRectF, Qt
|
||||||
|
|
||||||
@@ -41,9 +43,9 @@ class NWStatus:
|
|||||||
STATUS = 1
|
STATUS = 1
|
||||||
IMPORT = 2
|
IMPORT = 2
|
||||||
|
|
||||||
def __init__(self, type):
|
def __init__(self, kind: Literal[1, 2]) -> None:
|
||||||
|
|
||||||
self._type = type
|
self._type = kind
|
||||||
self._store = {}
|
self._store = {}
|
||||||
self._default = None
|
self._default = None
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ class NWStatus:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def write(self, key, name, col, count=None):
|
def write(self, key: str | None, name: str, col: tuple, count: int | None = None) -> str:
|
||||||
"""Add or update a status entry. If the key is invalid, a new
|
"""Add or update a status entry. If the key is invalid, a new
|
||||||
key is generated.
|
key is generated.
|
||||||
"""
|
"""
|
||||||
@@ -96,10 +98,8 @@ class NWStatus:
|
|||||||
|
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def remove(self, key):
|
def remove(self, key: str) -> bool:
|
||||||
"""Remove an entry in the list, but not if the count is larger
|
"""Remove an entry in the list, except if the count > 0."""
|
||||||
than 0.
|
|
||||||
"""
|
|
||||||
if key not in self._store:
|
if key not in self._store:
|
||||||
return False
|
return False
|
||||||
if self._store[key]["count"] > 0:
|
if self._store[key]["count"] > 0:
|
||||||
@@ -116,59 +116,49 @@ class NWStatus:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: str) -> str:
|
||||||
"""Check the key against the stored status names.
|
"""Check the key against the stored status names."""
|
||||||
"""
|
|
||||||
if self._isKey(value) and value in self._store:
|
if self._isKey(value) and value in self._store:
|
||||||
return value
|
return value
|
||||||
elif self._default is not None:
|
elif self._default is not None:
|
||||||
return self._default
|
return self._default
|
||||||
else:
|
return ""
|
||||||
return ""
|
|
||||||
|
|
||||||
def name(self, key):
|
def name(self, key: str) -> str:
|
||||||
"""Return the name associated with a given key.
|
"""Return the name associated with a given key."""
|
||||||
"""
|
|
||||||
if key in self._store:
|
if key in self._store:
|
||||||
return self._store[key]["name"]
|
return self._store[key]["name"]
|
||||||
elif self._default is not None:
|
elif self._default is not None:
|
||||||
return self._store[self._default]["name"]
|
return self._store[self._default]["name"]
|
||||||
else:
|
return ""
|
||||||
return ""
|
|
||||||
|
|
||||||
def cols(self, key):
|
def cols(self, key: str) -> tuple[int, int, int]:
|
||||||
"""Return the colours associated with a given key.
|
"""Return the colours associated with a given key.
|
||||||
"""
|
"""
|
||||||
if key in self._store:
|
if key in self._store:
|
||||||
return self._store[key]["cols"]
|
return self._store[key]["cols"]
|
||||||
elif self._default is not None:
|
elif self._default is not None:
|
||||||
return self._store[self._default]["cols"]
|
return self._store[self._default]["cols"]
|
||||||
else:
|
return 100, 100, 100
|
||||||
return (100, 100, 100)
|
|
||||||
|
|
||||||
def count(self, key):
|
def count(self, key: str) -> int:
|
||||||
"""Return the count associated with a given key.
|
"""Return the count associated with a given key."""
|
||||||
"""
|
|
||||||
if key in self._store:
|
if key in self._store:
|
||||||
return self._store[key]["count"]
|
return self._store[key]["count"]
|
||||||
elif self._default is not None:
|
elif self._default is not None:
|
||||||
return self._store[self._default]["count"]
|
return self._store[self._default]["count"]
|
||||||
else:
|
return 0
|
||||||
return 0
|
|
||||||
|
|
||||||
def icon(self, key):
|
def icon(self, key: str) -> QIcon:
|
||||||
"""Return the icon associated with a given key.
|
"""Return the icon associated with a given key."""
|
||||||
"""
|
|
||||||
if key in self._store:
|
if key in self._store:
|
||||||
return self._store[key]["icon"]
|
return self._store[key]["icon"]
|
||||||
elif self._default is not None:
|
elif self._default is not None:
|
||||||
return self._store[self._default]["icon"]
|
return self._store[self._default]["icon"]
|
||||||
else:
|
return self._defaultIcon
|
||||||
return self._defaultIcon
|
|
||||||
|
|
||||||
def reorder(self, order):
|
def reorder(self, order: list[str]) -> bool:
|
||||||
"""Reorder the items according to list.
|
"""Reorder the items according to list."""
|
||||||
"""
|
|
||||||
if len(order) != len(self._store):
|
if len(order) != len(self._store):
|
||||||
logger.error("Length mismatch between new and old order")
|
logger.error("Length mismatch between new and old order")
|
||||||
return False
|
return False
|
||||||
@@ -188,23 +178,20 @@ class NWStatus:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def resetCounts(self):
|
def resetCounts(self) -> None:
|
||||||
"""Clear the counts of references to the status entries.
|
"""Clear the counts of references to the status entries."""
|
||||||
"""
|
|
||||||
for key in self._store:
|
for key in self._store:
|
||||||
self._store[key]["count"] = 0
|
self._store[key]["count"] = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def increment(self, key):
|
def increment(self, key: str) -> None:
|
||||||
"""Increment the counter for a given entry.
|
"""Increment the counter for a given entry."""
|
||||||
"""
|
|
||||||
if key in self._store:
|
if key in self._store:
|
||||||
self._store[key]["count"] += 1
|
self._store[key]["count"] += 1
|
||||||
return
|
return
|
||||||
|
|
||||||
def pack(self):
|
def pack(self) -> Iterator[tuple[str, dict]]:
|
||||||
"""Pack the status entries into a dictionary.
|
"""Pack the status entries into a dictionary."""
|
||||||
"""
|
|
||||||
for key, data in self._store.items():
|
for key, data in self._store.items():
|
||||||
yield (data["name"], {
|
yield (data["name"], {
|
||||||
"key": key,
|
"key": key,
|
||||||
@@ -215,25 +202,22 @@ class NWStatus:
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
|
||||||
def unpack(self, data):
|
def unpack(self, data: dict) -> None:
|
||||||
"""Unpack a data dictionary and set the class values.
|
"""Unpack a data dictionary and set the class values."""
|
||||||
"""
|
|
||||||
self._store = {}
|
self._store = {}
|
||||||
self._default = None
|
self._default = None
|
||||||
|
|
||||||
for key, entry in data.items():
|
for key, entry in data.items():
|
||||||
label = entry.get("label", "")
|
label = entry.get("label", "")
|
||||||
colour = entry.get("colour", (100, 100, 100))
|
colour = entry.get("colour", (100, 100, 100))
|
||||||
count = entry.get("count", 0)
|
count = entry.get("count", 0)
|
||||||
self.write(key, label, colour, count)
|
self.write(key, label, colour, count)
|
||||||
|
return
|
||||||
return True
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _newKey(self):
|
def _newKey(self) -> str:
|
||||||
"""Generate a new key for a status flag. This method is
|
"""Generate a new key for a status flag. This method is
|
||||||
recursive, but should only fail if there is an issue with the
|
recursive, but should only fail if there is an issue with the
|
||||||
random number generator or the user has added a lot of status
|
random number generator or the user has added a lot of status
|
||||||
@@ -245,9 +229,8 @@ class NWStatus:
|
|||||||
key = self._newKey()
|
key = self._newKey()
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def _isKey(self, value):
|
def _isKey(self, value: str | None) -> TypeGuard[str]:
|
||||||
"""Check if a value is a key or not.
|
"""Check if a value is a key or not."""
|
||||||
"""
|
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
return False
|
return False
|
||||||
if len(value) != 7:
|
if len(value) != 7:
|
||||||
@@ -259,9 +242,8 @@ class NWStatus:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _createIcon(self, red, green, blue):
|
def _createIcon(self, red: int, green: int, blue: int) -> QIcon:
|
||||||
"""Generate an icon for a status label.
|
"""Generate an icon for a status label."""
|
||||||
"""
|
|
||||||
pixmap = QPixmap(self._iPX, self._iPX)
|
pixmap = QPixmap(self._iPX, self._iPX)
|
||||||
pixmap.fill(Qt.transparent)
|
pixmap.fill(Qt.transparent)
|
||||||
|
|
||||||
@@ -276,22 +258,22 @@ class NWStatus:
|
|||||||
# Iterator Bits
|
# Iterator Bits
|
||||||
##
|
##
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self) -> int:
|
||||||
return len(self._store)
|
return len(self._store)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key: str) -> dict:
|
||||||
return self._store[key]
|
return self._store[key]
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self) -> Iterator[dict]:
|
||||||
return iter(self._store)
|
return iter(self._store)
|
||||||
|
|
||||||
def keys(self):
|
def keys(self) -> KeysView[str]:
|
||||||
return self._store.keys()
|
return self._store.keys()
|
||||||
|
|
||||||
def items(self):
|
def items(self) -> ItemsView[str, dict]:
|
||||||
return self._store.items()
|
return self._store.items()
|
||||||
|
|
||||||
def values(self):
|
def values(self) -> ValuesView[dict]:
|
||||||
return self._store.values()
|
return self._store.values()
|
||||||
|
|
||||||
# END Class NWStatus
|
# END Class NWStatus
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class NWStorage:
|
|||||||
MODE_INPLACE = 1
|
MODE_INPLACE = 1
|
||||||
MODE_ARCHIVE = 2
|
MODE_ARCHIVE = 2
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._storagePath = None
|
self._storagePath = None
|
||||||
self._runtimePath = None
|
self._runtimePath = None
|
||||||
@@ -63,7 +63,7 @@ class NWStorage:
|
|||||||
self._openMode = self.MODE_INACTIVE
|
self._openMode = self.MODE_INACTIVE
|
||||||
return
|
return
|
||||||
|
|
||||||
def clear(self):
|
def clear(self) -> None:
|
||||||
"""Reset internal variables."""
|
"""Reset internal variables."""
|
||||||
self._storagePath = None
|
self._storagePath = None
|
||||||
self._runtimePath = None
|
self._runtimePath = None
|
||||||
@@ -145,7 +145,7 @@ class NWStorage:
|
|||||||
return True
|
return True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def closeSession(self):
|
def closeSession(self) -> None:
|
||||||
"""Run tasks related to closing the session."""
|
"""Run tasks related to closing the session."""
|
||||||
self.clearLockFile()
|
self.clearLockFile()
|
||||||
self.clear()
|
self.clear()
|
||||||
@@ -353,11 +353,11 @@ class _LegacyStorage:
|
|||||||
file/folder layout to the current project format.
|
file/folder layout to the current project format.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
return
|
return
|
||||||
|
|
||||||
def legacyDataFolder(self, path: Path, child: Path):
|
def legacyDataFolder(self, path: Path, child: Path) -> None:
|
||||||
"""Handle the content of a legacy data folder from a version 1.0
|
"""Handle the content of a legacy data folder from a version 1.0
|
||||||
project.
|
project.
|
||||||
"""
|
"""
|
||||||
@@ -396,7 +396,7 @@ class _LegacyStorage:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def deprecatedFiles(self, path: Path):
|
def deprecatedFiles(self, path: Path) -> None:
|
||||||
"""Handle files that are no longer used by novelWriter."""
|
"""Handle files that are no longer used by novelWriter."""
|
||||||
self._convertOldWordList( # Changed in 2.1 Beta 1
|
self._convertOldWordList( # Changed in 2.1 Beta 1
|
||||||
path / "meta" / "wordlist.txt",
|
path / "meta" / "wordlist.txt",
|
||||||
@@ -440,7 +440,7 @@ class _LegacyStorage:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _convertOldWordList(self, wordList: Path, wordJson: Path):
|
def _convertOldWordList(self, wordList: Path, wordJson: Path) -> None:
|
||||||
"""Convert the old word list plain text file to new format."""
|
"""Convert the old word list plain text file to new format."""
|
||||||
if wordJson.exists() or not wordList.exists():
|
if wordJson.exists() or not wordList.exists():
|
||||||
# If the new file already exists, we won't overwrite it
|
# If the new file already exists, we won't overwrite it
|
||||||
@@ -466,7 +466,7 @@ class _LegacyStorage:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _convertOldLogFile(self, sessLog: Path, sessJson: Path):
|
def _convertOldLogFile(self, sessLog: Path, sessJson: Path) -> None:
|
||||||
"""Convert the old text log file format to the new JSON Lines
|
"""Convert the old text log file format to the new JSON Lines
|
||||||
format.
|
format.
|
||||||
"""
|
"""
|
||||||
@@ -507,7 +507,7 @@ class _LegacyStorage:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _convertOldOptionsFile(self, optsOld: Path, optsNew: Path):
|
def _convertOldOptionsFile(self, optsOld: Path, optsNew: Path) -> None:
|
||||||
"""Convert the old options state file format to the format."""
|
"""Convert the old options state file format to the format."""
|
||||||
if optsNew.exists() or not optsOld.exists():
|
if optsNew.exists() or not optsOld.exists():
|
||||||
# If the new file already exists, we won't overwrite it
|
# If the new file already exists, we won't overwrite it
|
||||||
|
|||||||
+12
-12
@@ -49,12 +49,12 @@ class ToHtml(Tokenizer):
|
|||||||
M_EXPORT = 1 # Tweak output for saving to HTML or printing
|
M_EXPORT = 1 # Tweak output for saving to HTML or printing
|
||||||
M_EBOOK = 2 # Tweak output for converting to epub
|
M_EBOOK = 2 # Tweak output for converting to epub
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
super().__init__(project)
|
super().__init__(project)
|
||||||
|
|
||||||
self._genMode = self.M_EXPORT
|
self._genMode = self.M_EXPORT
|
||||||
self._cssStyles = True
|
self._cssStyles = True
|
||||||
self._fullHTML = []
|
self._fullHTML: list[str] = []
|
||||||
|
|
||||||
# Internals
|
# Internals
|
||||||
self._trMap = {}
|
self._trMap = {}
|
||||||
@@ -67,14 +67,14 @@ class ToHtml(Tokenizer):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fullHTML(self):
|
def fullHTML(self) -> list[str]:
|
||||||
return self._fullHTML
|
return self._fullHTML
|
||||||
|
|
||||||
##
|
##
|
||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setPreview(self, doComments: bool, doSynopsis: bool):
|
def setPreview(self, doComments: bool, doSynopsis: bool) -> None:
|
||||||
"""If we're using this class to generate markdown preview, we
|
"""If we're using this class to generate markdown preview, we
|
||||||
need to make a few changes to formatting, which is managed by
|
need to make a few changes to formatting, which is managed by
|
||||||
these flags.
|
these flags.
|
||||||
@@ -85,14 +85,14 @@ class ToHtml(Tokenizer):
|
|||||||
self._doSynopsis = doSynopsis
|
self._doSynopsis = doSynopsis
|
||||||
return
|
return
|
||||||
|
|
||||||
def setStyles(self, cssStyles: bool):
|
def setStyles(self, cssStyles: bool) -> None:
|
||||||
"""Enable or disable CSS styling. Some elements may still have
|
"""Enable or disable CSS styling. Some elements may still have
|
||||||
class tags.
|
class tags.
|
||||||
"""
|
"""
|
||||||
self._cssStyles = cssStyles
|
self._cssStyles = cssStyles
|
||||||
return
|
return
|
||||||
|
|
||||||
def setReplaceUnicode(self, doReplace: bool):
|
def setReplaceUnicode(self, doReplace: bool) -> None:
|
||||||
"""Set the translation map to either minimal or full unicode for
|
"""Set the translation map to either minimal or full unicode for
|
||||||
html entities replacement.
|
html entities replacement.
|
||||||
"""
|
"""
|
||||||
@@ -113,7 +113,7 @@ class ToHtml(Tokenizer):
|
|||||||
"""Return the size of the full HTML result."""
|
"""Return the size of the full HTML result."""
|
||||||
return sum([len(x) for x in self._fullHTML])
|
return sum([len(x) for x in self._fullHTML])
|
||||||
|
|
||||||
def doPreProcessing(self):
|
def doPreProcessing(self) -> None:
|
||||||
"""Extend the auto-replace to also properly encode some unicode
|
"""Extend the auto-replace to also properly encode some unicode
|
||||||
characters into their respective HTML entities.
|
characters into their respective HTML entities.
|
||||||
"""
|
"""
|
||||||
@@ -121,7 +121,7 @@ class ToHtml(Tokenizer):
|
|||||||
self._text = self._text.translate(self._trMap)
|
self._text = self._text.translate(self._trMap)
|
||||||
return
|
return
|
||||||
|
|
||||||
def doConvert(self):
|
def doConvert(self) -> None:
|
||||||
"""Convert the list of text tokens into a HTML document saved
|
"""Convert the list of text tokens into a HTML document saved
|
||||||
to _result.
|
to _result.
|
||||||
"""
|
"""
|
||||||
@@ -299,7 +299,7 @@ class ToHtml(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveHtml5(self, path: str | Path):
|
def saveHtml5(self, path: str | Path) -> None:
|
||||||
"""Save the data to an HTML file."""
|
"""Save the data to an HTML file."""
|
||||||
with open(path, mode="w", encoding="utf-8") as fObj:
|
with open(path, mode="w", encoding="utf-8") as fObj:
|
||||||
fObj.write((
|
fObj.write((
|
||||||
@@ -326,7 +326,7 @@ class ToHtml(Tokenizer):
|
|||||||
logger.info("Wrote file: %s", path)
|
logger.info("Wrote file: %s", path)
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveHtmlJson(self, path: str | Path):
|
def saveHtmlJson(self, path: str | Path) -> None:
|
||||||
"""Save the data to a JSON file."""
|
"""Save the data to a JSON file."""
|
||||||
timeStamp = time()
|
timeStamp = time()
|
||||||
data = {
|
data = {
|
||||||
@@ -347,7 +347,7 @@ class ToHtml(Tokenizer):
|
|||||||
logger.info("Wrote file: %s", path)
|
logger.info("Wrote file: %s", path)
|
||||||
return
|
return
|
||||||
|
|
||||||
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "):
|
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
|
||||||
"""Replace tabs with spaces in the html."""
|
"""Replace tabs with spaces in the html."""
|
||||||
htmlText = []
|
htmlText = []
|
||||||
tabSpace = spaceChar*nSpaces
|
tabSpace = spaceChar*nSpaces
|
||||||
@@ -357,7 +357,7 @@ class ToHtml(Tokenizer):
|
|||||||
self._fullHTML = htmlText
|
self._fullHTML = htmlText
|
||||||
return
|
return
|
||||||
|
|
||||||
def getStyleSheet(self) -> list:
|
def getStyleSheet(self) -> list[str]:
|
||||||
"""Generate a stylesheet for the current settings."""
|
"""Generate a stylesheet for the current settings."""
|
||||||
styles = []
|
styles = []
|
||||||
if not self._cssStyles:
|
if not self._cssStyles:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ from novelwriter.core.project import NWProject
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def stripEscape(text):
|
def stripEscape(text) -> str:
|
||||||
"""Helper function to strip escaped Markdown characters from
|
"""Helper function to strip escaped Markdown characters from
|
||||||
paragraph text.
|
paragraph text.
|
||||||
"""
|
"""
|
||||||
@@ -100,7 +100,7 @@ class Tokenizer(ABC):
|
|||||||
A_IND_L = 0x0100 # Left indentation
|
A_IND_L = 0x0100 # Left indentation
|
||||||
A_IND_R = 0x0200 # Right indentation
|
A_IND_R = 0x0200 # Right indentation
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
|
|
||||||
self._project = project
|
self._project = project
|
||||||
|
|
||||||
@@ -191,116 +191,116 @@ class Tokenizer(ABC):
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setTitleFormat(self, hFormat: str):
|
def setTitleFormat(self, hFormat: str) -> None:
|
||||||
"""Set the title format pattern."""
|
"""Set the title format pattern."""
|
||||||
self._fmtTitle = hFormat.strip()
|
self._fmtTitle = hFormat.strip()
|
||||||
return
|
return
|
||||||
|
|
||||||
def setChapterFormat(self, hFormat: str):
|
def setChapterFormat(self, hFormat: str) -> None:
|
||||||
"""Set the chapert format pattern."""
|
"""Set the chapert format pattern."""
|
||||||
self._fmtChapter = hFormat.strip()
|
self._fmtChapter = hFormat.strip()
|
||||||
return
|
return
|
||||||
|
|
||||||
def setUnNumberedFormat(self, hFormat: str):
|
def setUnNumberedFormat(self, hFormat: str) -> None:
|
||||||
"""Set the unnumbered format pattern."""
|
"""Set the unnumbered format pattern."""
|
||||||
self._fmtUnNum = hFormat.strip()
|
self._fmtUnNum = hFormat.strip()
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSceneFormat(self, hFormat: str, hide: bool):
|
def setSceneFormat(self, hFormat: str, hide: bool) -> None:
|
||||||
"""Set the scene format pattern and hidden status."""
|
"""Set the scene format pattern and hidden status."""
|
||||||
self._fmtScene = hFormat.strip()
|
self._fmtScene = hFormat.strip()
|
||||||
self._hideScene = hide
|
self._hideScene = hide
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSectionFormat(self, hFormat: str, hide: bool):
|
def setSectionFormat(self, hFormat: str, hide: bool) -> None:
|
||||||
"""Set the section format pattern and hidden status."""
|
"""Set the section format pattern and hidden status."""
|
||||||
self._fmtSection = hFormat.strip()
|
self._fmtSection = hFormat.strip()
|
||||||
self._hideSection = hide
|
self._hideSection = hide
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFont(self, family: str, size: int, isFixed: bool = False):
|
def setFont(self, family: str, size: int, isFixed: bool = False) -> None:
|
||||||
"""Set the build font."""
|
"""Set the build font."""
|
||||||
self._textFont = family
|
self._textFont = family
|
||||||
self._textSize = round(int(size))
|
self._textSize = round(int(size))
|
||||||
self._textFixed = isFixed
|
self._textFixed = isFixed
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLineHeight(self, height: float):
|
def setLineHeight(self, height: float) -> None:
|
||||||
"""Set the line height between 0.5 and 5.0."""
|
"""Set the line height between 0.5 and 5.0."""
|
||||||
self._lineHeight = min(max(float(height), 0.5), 5.0)
|
self._lineHeight = min(max(float(height), 0.5), 5.0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBlockIndent(self, indent: float):
|
def setBlockIndent(self, indent: float) -> None:
|
||||||
"""Set the block indent between 0.0 and 10.0."""
|
"""Set the block indent between 0.0 and 10.0."""
|
||||||
self._blockIndent = min(max(float(indent), 0.0), 10.0)
|
self._blockIndent = min(max(float(indent), 0.0), 10.0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setJustify(self, state: bool):
|
def setJustify(self, state: bool) -> None:
|
||||||
"""Enable or disable text justification."""
|
"""Enable or disable text justification."""
|
||||||
self._doJustify = state
|
self._doJustify = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTitleMargins(self, upper: float, lower: float):
|
def setTitleMargins(self, upper: float, lower: float) -> None:
|
||||||
"""Set the upper and lower title margin."""
|
"""Set the upper and lower title margin."""
|
||||||
self._marginTitle = (float(upper), float(lower))
|
self._marginTitle = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHead1Margins(self, upper: float, lower: float):
|
def setHead1Margins(self, upper: float, lower: float) -> None:
|
||||||
"""Set the upper and lower header 1 margin."""
|
"""Set the upper and lower header 1 margin."""
|
||||||
self._marginHead1 = (float(upper), float(lower))
|
self._marginHead1 = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHead2Margins(self, upper: float, lower: float):
|
def setHead2Margins(self, upper: float, lower: float) -> None:
|
||||||
"""Set the upper and lower header 2 margin."""
|
"""Set the upper and lower header 2 margin."""
|
||||||
self._marginHead2 = (float(upper), float(lower))
|
self._marginHead2 = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHead3Margins(self, upper: float, lower: float):
|
def setHead3Margins(self, upper: float, lower: float) -> None:
|
||||||
"""Set the upper and lower header 3 margin."""
|
"""Set the upper and lower header 3 margin."""
|
||||||
self._marginHead3 = (float(upper), float(lower))
|
self._marginHead3 = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHead4Margins(self, upper: float, lower: float):
|
def setHead4Margins(self, upper: float, lower: float) -> None:
|
||||||
"""Set the upper and lower header 4 margin."""
|
"""Set the upper and lower header 4 margin."""
|
||||||
self._marginHead4 = (float(upper), float(lower))
|
self._marginHead4 = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTextMargins(self, upper: float, lower: float):
|
def setTextMargins(self, upper: float, lower: float) -> None:
|
||||||
"""Set the upper and lower text margin."""
|
"""Set the upper and lower text margin."""
|
||||||
self._marginText = (float(upper), float(lower))
|
self._marginText = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setMetaMargins(self, upper: float, lower: float):
|
def setMetaMargins(self, upper: float, lower: float) -> None:
|
||||||
"""Set the upper and lower meta text margin."""
|
"""Set the upper and lower meta text margin."""
|
||||||
self._marginMeta = (float(upper), float(lower))
|
self._marginMeta = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLinkHeaders(self, state: bool):
|
def setLinkHeaders(self, state: bool) -> None:
|
||||||
"""Enable or disable adding an anchor before headers."""
|
"""Enable or disable adding an anchor before headers."""
|
||||||
self._linkHeaders = state
|
self._linkHeaders = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBodyText(self, state: bool):
|
def setBodyText(self, state: bool) -> None:
|
||||||
"""Include body text in build."""
|
"""Include body text in build."""
|
||||||
self._doBodyText = state
|
self._doBodyText = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSynopsis(self, state: bool):
|
def setSynopsis(self, state: bool) -> None:
|
||||||
"""Include synopsis comments in build."""
|
"""Include synopsis comments in build."""
|
||||||
self._doSynopsis = state
|
self._doSynopsis = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setComments(self, state: bool):
|
def setComments(self, state: bool) -> None:
|
||||||
"""Include comments in build."""
|
"""Include comments in build."""
|
||||||
self._doComments = state
|
self._doComments = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setKeywords(self, state: bool):
|
def setKeywords(self, state: bool) -> None:
|
||||||
"""Include keywords in build."""
|
"""Include keywords in build."""
|
||||||
self._doKeywords = state
|
self._doKeywords = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setKeepMarkdown(self, state: bool):
|
def setKeepMarkdown(self, state: bool) -> None:
|
||||||
"""Keep original markdown during build."""
|
"""Keep original markdown during build."""
|
||||||
self._keepMarkdown = state
|
self._keepMarkdown = state
|
||||||
return
|
return
|
||||||
@@ -310,7 +310,7 @@ class Tokenizer(ABC):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def doConvert(self):
|
def doConvert(self) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def addRootHeading(self, tHandle: str) -> bool:
|
def addRootHeading(self, tHandle: str) -> bool:
|
||||||
@@ -365,7 +365,7 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def doPreProcessing(self):
|
def doPreProcessing(self) -> None:
|
||||||
"""Run trough the various replace dictionaries."""
|
"""Run trough the various replace dictionaries."""
|
||||||
# Process the user's auto-replace dictionary
|
# Process the user's auto-replace dictionary
|
||||||
autoReplace = self._project.data.autoReplace
|
autoReplace = self._project.data.autoReplace
|
||||||
@@ -382,7 +382,7 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def tokenizeText(self):
|
def tokenizeText(self) -> None:
|
||||||
"""Scan the text for either lines starting with specific
|
"""Scan the text for either lines starting with specific
|
||||||
characters that indicate headers, comments, commands etc, or
|
characters that indicate headers, comments, commands etc, or
|
||||||
just contain plain text. In the case of plain text, apply the
|
just contain plain text. In the case of plain text, apply the
|
||||||
@@ -742,14 +742,14 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveRawMarkdown(self, path: str | Path):
|
def saveRawMarkdown(self, path: str | Path) -> None:
|
||||||
"""Save the raw text to a plain text file."""
|
"""Save the raw text to a plain text file."""
|
||||||
with open(path, mode="w", encoding="utf-8") as outFile:
|
with open(path, mode="w", encoding="utf-8") as outFile:
|
||||||
for nwdPage in self._allMarkdown:
|
for nwdPage in self._allMarkdown:
|
||||||
outFile.write(nwdPage)
|
outFile.write(nwdPage)
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveRawMarkdownJSON(self, path: str | Path):
|
def saveRawMarkdownJSON(self, path: str | Path) -> None:
|
||||||
"""Save the raw text to a JSON file."""
|
"""Save the raw text to a JSON file."""
|
||||||
timeStamp = time()
|
timeStamp = time()
|
||||||
data = {
|
data = {
|
||||||
@@ -773,30 +773,30 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
class HeadingFormatter:
|
class HeadingFormatter:
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._chCount = 0
|
self._chCount = 0
|
||||||
self._scChCount = 0
|
self._scChCount = 0
|
||||||
self._scAbsCount = 0
|
self._scAbsCount = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def incChapter(self):
|
def incChapter(self) -> None:
|
||||||
"""Increment the chapter counter."""
|
"""Increment the chapter counter."""
|
||||||
self._chCount += 1
|
self._chCount += 1
|
||||||
return
|
return
|
||||||
|
|
||||||
def incScene(self):
|
def incScene(self) -> None:
|
||||||
"""Increment the scene counters."""
|
"""Increment the scene counters."""
|
||||||
self._scChCount += 1
|
self._scChCount += 1
|
||||||
self._scAbsCount += 1
|
self._scAbsCount += 1
|
||||||
return
|
return
|
||||||
|
|
||||||
def resetScene(self):
|
def resetScene(self) -> None:
|
||||||
"""Reset the chapter scene counter."""
|
"""Reset the chapter scene counter."""
|
||||||
self._scChCount = 0
|
self._scChCount = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def apply(self, hFormat: str, text: str):
|
def apply(self, hFormat: str, text: str) -> str:
|
||||||
"""Apply formatting to a specific heading."""
|
"""Apply formatting to a specific heading."""
|
||||||
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
||||||
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
|
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
|
||||||
|
|||||||
@@ -45,12 +45,10 @@ class ToMarkdown(Tokenizer):
|
|||||||
M_STD = 0 # Standard Markdown
|
M_STD = 0 # Standard Markdown
|
||||||
M_GH = 1 # GitHub Markdown
|
M_GH = 1 # GitHub Markdown
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
super().__init__(project)
|
super().__init__(project)
|
||||||
|
|
||||||
self._genMode = self.M_STD
|
self._genMode = self.M_STD
|
||||||
self._fullMD = []
|
self._fullMD: list[str] = []
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -58,7 +56,7 @@ class ToMarkdown(Tokenizer):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fullMD(self) -> list:
|
def fullMD(self) -> list[str]:
|
||||||
"""Return the markdown as a list."""
|
"""Return the markdown as a list."""
|
||||||
return self._fullMD
|
return self._fullMD
|
||||||
|
|
||||||
@@ -66,11 +64,11 @@ class ToMarkdown(Tokenizer):
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setStandardMarkdown(self):
|
def setStandardMarkdown(self) -> None:
|
||||||
self._genMode = self.M_STD
|
self._genMode = self.M_STD
|
||||||
return
|
return
|
||||||
|
|
||||||
def setGitHubMarkdown(self):
|
def setGitHubMarkdown(self) -> None:
|
||||||
self._genMode = self.M_GH
|
self._genMode = self.M_GH
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -82,7 +80,7 @@ class ToMarkdown(Tokenizer):
|
|||||||
"""Return the size of the full Markdown result."""
|
"""Return the size of the full Markdown result."""
|
||||||
return sum([len(x) for x in self._fullMD])
|
return sum([len(x) for x in self._fullMD])
|
||||||
|
|
||||||
def doConvert(self):
|
def doConvert(self) -> None:
|
||||||
"""Convert the list of text tokens into a HTML document saved
|
"""Convert the list of text tokens into a HTML document saved
|
||||||
to theResult.
|
to theResult.
|
||||||
"""
|
"""
|
||||||
@@ -175,14 +173,14 @@ class ToMarkdown(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveMarkdown(self, path: str | Path):
|
def saveMarkdown(self, path: str | Path) -> None:
|
||||||
"""Save the data to a plain text file."""
|
"""Save the data to a plain text file."""
|
||||||
with open(path, mode="w", encoding="utf-8") as outFile:
|
with open(path, mode="w", encoding="utf-8") as outFile:
|
||||||
outFile.write("".join(self._fullMD))
|
outFile.write("".join(self._fullMD))
|
||||||
logger.info("Wrote file: %s", path)
|
logger.info("Wrote file: %s", path)
|
||||||
return
|
return
|
||||||
|
|
||||||
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "):
|
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
|
||||||
"""Replace tabs with spaces."""
|
"""Replace tabs with spaces."""
|
||||||
spaces = spaceChar*nSpaces
|
spaces = spaceChar*nSpaces
|
||||||
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
|
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
|
||||||
|
|||||||
+44
-46
@@ -98,7 +98,7 @@ class ToOdt(Tokenizer):
|
|||||||
Test with: https://odfvalidator.org/
|
Test with: https://odfvalidator.org/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project: NWProject, isFlat: bool):
|
def __init__(self, project: NWProject, isFlat: bool) -> None:
|
||||||
super().__init__(project)
|
super().__init__(project)
|
||||||
|
|
||||||
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
|
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
|
||||||
@@ -188,7 +188,7 @@ class ToOdt(Tokenizer):
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setLanguage(self, language: str):
|
def setLanguage(self, language: str) -> None:
|
||||||
"""Set language for the document."""
|
"""Set language for the document."""
|
||||||
if language:
|
if language:
|
||||||
langBits = language.split("_")
|
langBits = language.split("_")
|
||||||
@@ -197,7 +197,7 @@ class ToOdt(Tokenizer):
|
|||||||
self._dCountry = langBits[1]
|
self._dCountry = langBits[1]
|
||||||
return
|
return
|
||||||
|
|
||||||
def setColourHeaders(self, state: bool):
|
def setColourHeaders(self, state: bool) -> None:
|
||||||
"""Enable/disable coloured headings and comments."""
|
"""Enable/disable coloured headings and comments."""
|
||||||
self._colourHead = state
|
self._colourHead = state
|
||||||
return
|
return
|
||||||
@@ -205,7 +205,7 @@ class ToOdt(Tokenizer):
|
|||||||
def setPageLayout(
|
def setPageLayout(
|
||||||
self, width: int | float, height: int | float,
|
self, width: int | float, height: int | float,
|
||||||
top: int | float, bottom: int | float, left: int | float, right: int | float
|
top: int | float, bottom: int | float, left: int | float, right: int | float
|
||||||
):
|
) -> None:
|
||||||
"""Set the document page size and margins in millimetres."""
|
"""Set the document page size and margins in millimetres."""
|
||||||
self._mDocWidth = f"{width/10.0:.3f}cm"
|
self._mDocWidth = f"{width/10.0:.3f}cm"
|
||||||
self._mDocHeight = f"{height/10.0:.3f}cm"
|
self._mDocHeight = f"{height/10.0:.3f}cm"
|
||||||
@@ -219,7 +219,7 @@ class ToOdt(Tokenizer):
|
|||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def initDocument(self):
|
def initDocument(self) -> None:
|
||||||
"""Initialises a new open document XML tree."""
|
"""Initialises a new open document XML tree."""
|
||||||
# Initialise Variables
|
# Initialise Variables
|
||||||
# ====================
|
# ====================
|
||||||
@@ -381,7 +381,7 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def doConvert(self):
|
def doConvert(self) -> None:
|
||||||
"""Convert the list of text tokens into XML elements."""
|
"""Convert the list of text tokens into XML elements."""
|
||||||
self._result = "" # Not used, but cleared just in case
|
self._result = "" # Not used, but cleared just in case
|
||||||
|
|
||||||
@@ -599,7 +599,7 @@ class ToOdt(Tokenizer):
|
|||||||
def _addTextPar(
|
def _addTextPar(
|
||||||
self, styleName: str, oStyle: ODTParagraphStyle, tText: str, tFmt: str = "",
|
self, styleName: str, oStyle: ODTParagraphStyle, tText: str, tFmt: str = "",
|
||||||
isHead: bool = False, oLevel: str | None = None
|
isHead: bool = False, oLevel: str | None = None
|
||||||
):
|
) -> None:
|
||||||
"""Add a text paragraph to the text XML element."""
|
"""Add a text paragraph to the text XML element."""
|
||||||
tAttr = {}
|
tAttr = {}
|
||||||
tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
|
tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
|
||||||
@@ -726,7 +726,7 @@ class ToOdt(Tokenizer):
|
|||||||
# Style Elements
|
# Style Elements
|
||||||
##
|
##
|
||||||
|
|
||||||
def _pageStyles(self):
|
def _pageStyles(self) -> None:
|
||||||
"""Set the default page style."""
|
"""Set the default page style."""
|
||||||
tAttr = {}
|
tAttr = {}
|
||||||
tAttr[_mkTag("style", "name")] = "PM1"
|
tAttr[_mkTag("style", "name")] = "PM1"
|
||||||
@@ -756,7 +756,7 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _defaultStyles(self):
|
def _defaultStyles(self) -> None:
|
||||||
"""Set the default styles."""
|
"""Set the default styles."""
|
||||||
# Add Paragraph Family Style
|
# Add Paragraph Family Style
|
||||||
# ==========================
|
# ==========================
|
||||||
@@ -829,7 +829,7 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _useableStyles(self):
|
def _useableStyles(self) -> None:
|
||||||
"""Set the usable styles."""
|
"""Set the usable styles."""
|
||||||
# Add Text Body Style
|
# Add Text Body Style
|
||||||
# ===================
|
# ===================
|
||||||
@@ -1002,7 +1002,7 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _writeHeader(self):
|
def _writeHeader(self) -> None:
|
||||||
"""Write the header elements."""
|
"""Write the header elements."""
|
||||||
tAttr = {}
|
tAttr = {}
|
||||||
tAttr[_mkTag("style", "name")] = "Standard"
|
tAttr[_mkTag("style", "name")] = "Standard"
|
||||||
@@ -1048,7 +1048,7 @@ class ODTParagraphStyle:
|
|||||||
VALID_CLASS = ["text", "chapter"]
|
VALID_CLASS = ["text", "chapter"]
|
||||||
VALID_WEIGHT = ["normal", "inherit", "bold"]
|
VALID_WEIGHT = ["normal", "inherit", "bold"]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
|
|
||||||
# Attributes
|
# Attributes
|
||||||
self._mAttr = {
|
self._mAttr = {
|
||||||
@@ -1087,26 +1087,26 @@ class ODTParagraphStyle:
|
|||||||
# Attribute Setters
|
# Attribute Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setDisplayName(self, value: str | None):
|
def setDisplayName(self, value: str | None) -> None:
|
||||||
self._mAttr["display-name"][1] = value
|
self._mAttr["display-name"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setParentStyleName(self, value: str | None):
|
def setParentStyleName(self, value: str | None) -> None:
|
||||||
self._mAttr["parent-style-name"][1] = value
|
self._mAttr["parent-style-name"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setNextStyleName(self, value: str | None):
|
def setNextStyleName(self, value: str | None) -> None:
|
||||||
self._mAttr["next-style-name"][1] = value
|
self._mAttr["next-style-name"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setOutlineLevel(self, value: str | None):
|
def setOutlineLevel(self, value: str | None) -> None:
|
||||||
if value in self.VALID_LEVEL:
|
if value in self.VALID_LEVEL:
|
||||||
self._mAttr["default-outline-level"][1] = value
|
self._mAttr["default-outline-level"][1] = value
|
||||||
else:
|
else:
|
||||||
self._mAttr["default-outline-level"][1] = None
|
self._mAttr["default-outline-level"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setClass(self, value: str | None):
|
def setClass(self, value: str | None) -> None:
|
||||||
if value in self.VALID_CLASS:
|
if value in self.VALID_CLASS:
|
||||||
self._mAttr["class"][1] = value
|
self._mAttr["class"][1] = value
|
||||||
else:
|
else:
|
||||||
@@ -1117,41 +1117,41 @@ class ODTParagraphStyle:
|
|||||||
# Paragraph Setters
|
# Paragraph Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setMarginTop(self, value: str | None):
|
def setMarginTop(self, value: str | None) -> None:
|
||||||
self._pAttr["margin-top"][1] = value
|
self._pAttr["margin-top"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setMarginBottom(self, value: str | None):
|
def setMarginBottom(self, value: str | None) -> None:
|
||||||
self._pAttr["margin-bottom"][1] = value
|
self._pAttr["margin-bottom"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setMarginLeft(self, value: str | None):
|
def setMarginLeft(self, value: str | None) -> None:
|
||||||
self._pAttr["margin-left"][1] = value
|
self._pAttr["margin-left"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setMarginRight(self, value: str | None):
|
def setMarginRight(self, value: str | None) -> None:
|
||||||
self._pAttr["margin-right"][1] = value
|
self._pAttr["margin-right"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLineHeight(self, value: str | None):
|
def setLineHeight(self, value: str | None) -> None:
|
||||||
self._pAttr["line-height"][1] = value
|
self._pAttr["line-height"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTextAlign(self, value: str | None):
|
def setTextAlign(self, value: str | None) -> None:
|
||||||
if value in self.VALID_ALIGN:
|
if value in self.VALID_ALIGN:
|
||||||
self._pAttr["text-align"][1] = value
|
self._pAttr["text-align"][1] = value
|
||||||
else:
|
else:
|
||||||
self._pAttr["text-align"][1] = None
|
self._pAttr["text-align"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBreakBefore(self, value: str | None):
|
def setBreakBefore(self, value: str | None) -> None:
|
||||||
if value in self.VALID_BREAK:
|
if value in self.VALID_BREAK:
|
||||||
self._pAttr["break-before"][1] = value
|
self._pAttr["break-before"][1] = value
|
||||||
else:
|
else:
|
||||||
self._pAttr["break-before"][1] = None
|
self._pAttr["break-before"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBreakAfter(self, value: str | None):
|
def setBreakAfter(self, value: str | None) -> None:
|
||||||
if value in self.VALID_BREAK:
|
if value in self.VALID_BREAK:
|
||||||
self._pAttr["break-after"][1] = value
|
self._pAttr["break-after"][1] = value
|
||||||
else:
|
else:
|
||||||
@@ -1162,30 +1162,30 @@ class ODTParagraphStyle:
|
|||||||
# Text Setters
|
# Text Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setFontName(self, value: str | None):
|
def setFontName(self, value: str | None) -> None:
|
||||||
self._tAttr["font-name"][1] = value
|
self._tAttr["font-name"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFontFamily(self, value: str | None):
|
def setFontFamily(self, value: str | None) -> None:
|
||||||
self._tAttr["font-family"][1] = value
|
self._tAttr["font-family"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFontSize(self, value: str | None):
|
def setFontSize(self, value: str | None) -> None:
|
||||||
self._tAttr["font-size"][1] = value
|
self._tAttr["font-size"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFontWeight(self, value: str | None):
|
def setFontWeight(self, value: str | None) -> None:
|
||||||
if value in self.VALID_WEIGHT:
|
if value in self.VALID_WEIGHT:
|
||||||
self._tAttr["font-weight"][1] = value
|
self._tAttr["font-weight"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["font-weight"][1] = None
|
self._tAttr["font-weight"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setColor(self, value: str | None):
|
def setColor(self, value: str | None) -> None:
|
||||||
self._tAttr["color"][1] = value
|
self._tAttr["color"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setOpacity(self, value: str | None):
|
def setOpacity(self, value: str | None) -> None:
|
||||||
self._tAttr["opacity"][1] = value
|
self._tAttr["opacity"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1193,7 +1193,7 @@ class ODTParagraphStyle:
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def checkNew(self, refStyle: ODTParagraphStyle):
|
def checkNew(self, refStyle: ODTParagraphStyle) -> bool:
|
||||||
"""Check if there are new settings in refStyle that differ from
|
"""Check if there are new settings in refStyle that differ from
|
||||||
those in the current object.
|
those in the current object.
|
||||||
"""
|
"""
|
||||||
@@ -1217,7 +1217,7 @@ class ODTParagraphStyle:
|
|||||||
)
|
)
|
||||||
return sha256(theString.encode()).hexdigest()
|
return sha256(theString.encode()).hexdigest()
|
||||||
|
|
||||||
def packXML(self, xParent: ET.Element, name: str):
|
def packXML(self, xParent: ET.Element, name: str) -> None:
|
||||||
"""Pack the content into an xml element."""
|
"""Pack the content into an xml element."""
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = name
|
theAttr[_mkTag("style", "name")] = name
|
||||||
@@ -1259,8 +1259,7 @@ class ODTTextStyle:
|
|||||||
VALID_LSTYLE = ["none", "solid"]
|
VALID_LSTYLE = ["none", "solid"]
|
||||||
VALID_LTYPE = ["none", "single", "double"]
|
VALID_LTYPE = ["none", "single", "double"]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
|
|
||||||
# Text Attributes
|
# Text Attributes
|
||||||
self._tAttr = {
|
self._tAttr = {
|
||||||
"font-weight": ["fo", None],
|
"font-weight": ["fo", None],
|
||||||
@@ -1268,35 +1267,34 @@ class ODTTextStyle:
|
|||||||
"text-line-through-style": ["style", None],
|
"text-line-through-style": ["style", None],
|
||||||
"text-line-through-type": ["style", None],
|
"text-line-through-type": ["style", None],
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setFontWeight(self, value: str | None):
|
def setFontWeight(self, value: str | None) -> None:
|
||||||
if value in self.VALID_WEIGHT:
|
if value in self.VALID_WEIGHT:
|
||||||
self._tAttr["font-weight"][1] = value
|
self._tAttr["font-weight"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["font-weight"][1] = None
|
self._tAttr["font-weight"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFontStyle(self, value: str | None):
|
def setFontStyle(self, value: str | None) -> None:
|
||||||
if value in self.VALID_STYLE:
|
if value in self.VALID_STYLE:
|
||||||
self._tAttr["font-style"][1] = value
|
self._tAttr["font-style"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["font-style"][1] = None
|
self._tAttr["font-style"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setStrikeStyle(self, value: str | None):
|
def setStrikeStyle(self, value: str | None) -> None:
|
||||||
if value in self.VALID_LSTYLE:
|
if value in self.VALID_LSTYLE:
|
||||||
self._tAttr["text-line-through-style"][1] = value
|
self._tAttr["text-line-through-style"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["text-line-through-style"][1] = None
|
self._tAttr["text-line-through-style"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setStrikeType(self, value: str | None):
|
def setStrikeType(self, value: str | None) -> None:
|
||||||
if value in self.VALID_LTYPE:
|
if value in self.VALID_LTYPE:
|
||||||
self._tAttr["text-line-through-type"][1] = value
|
self._tAttr["text-line-through-type"][1] = value
|
||||||
else:
|
else:
|
||||||
@@ -1307,7 +1305,7 @@ class ODTTextStyle:
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def packXML(self, xParent: ET.Element, name: str):
|
def packXML(self, xParent: ET.Element, name: str) -> None:
|
||||||
"""Pack the content into an xml element."""
|
"""Pack the content into an xml element."""
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = name
|
theAttr[_mkTag("style", "name")] = name
|
||||||
@@ -1357,7 +1355,7 @@ class XMLParagraph:
|
|||||||
object and attribute is written to,
|
object and attribute is written to,
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, xRoot: ET.Element):
|
def __init__(self, xRoot: ET.Element) -> None:
|
||||||
|
|
||||||
self._xRoot = xRoot
|
self._xRoot = xRoot
|
||||||
self._xTail = ET.Element("")
|
self._xTail = ET.Element("")
|
||||||
@@ -1370,7 +1368,7 @@ class XMLParagraph:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def appendText(self, tText: str):
|
def appendText(self, tText: str) -> None:
|
||||||
"""Append text to the XML element. We do this one character at
|
"""Append text to the XML element. We do this one character at
|
||||||
the time in order to be able to process line breaks, tabs and
|
the time in order to be able to process line breaks, tabs and
|
||||||
spaces separately. Multiple spaces are concatenated into a
|
spaces separately. Multiple spaces are concatenated into a
|
||||||
@@ -1435,7 +1433,7 @@ class XMLParagraph:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def appendSpan(self, tText: str, tFmt: str):
|
def appendSpan(self, tText: str, tFmt: str) -> None:
|
||||||
"""Append a text span to the XML element. The span is always
|
"""Append a text span to the XML element. The span is always
|
||||||
closed since we do not allow nested spans (like Libre Office).
|
closed since we do not allow nested spans (like Libre Office).
|
||||||
Therefore we return to the root element level when we're done
|
Therefore we return to the root element level when we're done
|
||||||
@@ -1449,7 +1447,7 @@ class XMLParagraph:
|
|||||||
self._nState = X_ROOT_TAIL
|
self._nState = X_ROOT_TAIL
|
||||||
return
|
return
|
||||||
|
|
||||||
def checkError(self):
|
def checkError(self) -> tuple[int, str]:
|
||||||
"""Check that the number of characters written matches the
|
"""Check that the number of characters written matches the
|
||||||
number of characters received.
|
number of characters received.
|
||||||
"""
|
"""
|
||||||
@@ -1463,7 +1461,7 @@ class XMLParagraph:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _processSpaces(self, nSpaces: int):
|
def _processSpaces(self, nSpaces: int) -> None:
|
||||||
"""Add spaces to paragraph. The first space is always written
|
"""Add spaces to paragraph. The first space is always written
|
||||||
as-is (unless it's the first character of the paragraph). The
|
as-is (unless it's the first character of the paragraph). The
|
||||||
second space uses the dedicated tag for spaces, and from the
|
second space uses the dedicated tag for spaces, and from the
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.1-beta1" hexVersion="0x020100b1" fileVersion="1.5" fileRevision="1" timeStamp="2023-08-08 10:33:12">
|
<novelWriterXML appVersion="2.1-beta1" hexVersion="0x020100b1" fileVersion="1.5" fileRevision="1" timeStamp="2023-08-08 22:19:23">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1516" autoCount="237" editTime="75236">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1517" autoCount="237" editTime="75241">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ def testCoreStatus_PackUnpack(mockRnd):
|
|||||||
|
|
||||||
# Unpack
|
# Unpack
|
||||||
theStatus = NWStatus(NWStatus.STATUS)
|
theStatus = NWStatus(NWStatus.STATUS)
|
||||||
assert theStatus.unpack({
|
theStatus.unpack({
|
||||||
statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
|
statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
|
||||||
statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
|
statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
|
||||||
statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]},
|
statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]},
|
||||||
|
|||||||
Reference in New Issue
Block a user