Update linting for main code
This commit is contained in:
@@ -21,7 +21,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
@@ -211,7 +211,7 @@ class FilterMode(Enum):
|
||||
|
||||
|
||||
class BuildSettings:
|
||||
"""Core: Build Settings Class
|
||||
"""Core: Build Settings Class.
|
||||
|
||||
This class manages the build settings for a Manuscript build job.
|
||||
The settings can be packed/unpacked to/from a dictionary for JSON.
|
||||
@@ -229,7 +229,6 @@ class BuildSettings:
|
||||
self._included = set()
|
||||
self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()}
|
||||
self._changed = False
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def fromDict(cls, data: dict) -> BuildSettings:
|
||||
@@ -315,7 +314,6 @@ class BuildSettings:
|
||||
def setName(self, name: str) -> None:
|
||||
"""Set the build setting display name."""
|
||||
self._name = str(name)
|
||||
return
|
||||
|
||||
def setBuildID(self, value: str | uuid.UUID) -> None:
|
||||
"""Set a UUID build ID."""
|
||||
@@ -324,13 +322,11 @@ class BuildSettings:
|
||||
self._uuid = str(uuid.uuid4())
|
||||
elif value != self._uuid:
|
||||
self._uuid = value
|
||||
return
|
||||
|
||||
def setOrder(self, value: int) -> None:
|
||||
"""Set the build order."""
|
||||
if isinstance(value, int):
|
||||
self._order = value
|
||||
return
|
||||
|
||||
def setLastBuildPath(self, path: Path | str | None) -> None:
|
||||
"""Set the last used build path."""
|
||||
@@ -341,41 +337,35 @@ class BuildSettings:
|
||||
else:
|
||||
self._path = CONFIG.homePath()
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
def setLastBuildName(self, name: str) -> None:
|
||||
"""Set the last used build name."""
|
||||
self._build = str(name).strip()
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
def setLastFormat(self, value: nwBuildFmt) -> None:
|
||||
"""Set the last used build format."""
|
||||
if isinstance(value, nwBuildFmt):
|
||||
self._format = value
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
def setFiltered(self, tHandle: str) -> None:
|
||||
"""Set an item as filtered."""
|
||||
self._excluded.discard(tHandle)
|
||||
self._included.discard(tHandle)
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
def setIncluded(self, tHandle: str) -> None:
|
||||
"""Set an item as explicitly included."""
|
||||
self._excluded.discard(tHandle)
|
||||
self._included.add(tHandle)
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
def setExcluded(self, tHandle: str) -> None:
|
||||
"""Set an item as explicitly excluded."""
|
||||
self._excluded.add(tHandle)
|
||||
self._included.discard(tHandle)
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
def setAllowRoot(self, tHandle: str, state: bool) -> None:
|
||||
"""Set a specific root folder as allowed or not."""
|
||||
@@ -385,14 +375,12 @@ class BuildSettings:
|
||||
elif state is False:
|
||||
self._skipRoot.add(tHandle)
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
def setValue(self, key: str, value: T_BuildValue) -> None:
|
||||
"""Set a specific value for a build setting."""
|
||||
if (d := SETTINGS_TEMPLATE.get(key)) and len(d) == 2 and isinstance(value, d[0]):
|
||||
self._changed |= (value != self._settings[key])
|
||||
self._settings[key] = value
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
@@ -463,7 +451,6 @@ class BuildSettings:
|
||||
called when the changes have been safely saved or passed on.
|
||||
"""
|
||||
self._changed = False
|
||||
return
|
||||
|
||||
def pack(self) -> dict:
|
||||
"""Pack all content into a JSON compatible dictionary."""
|
||||
@@ -516,8 +503,6 @@ class BuildSettings:
|
||||
|
||||
self._changed = False
|
||||
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def duplicate(cls, source: BuildSettings) -> BuildSettings:
|
||||
"""Make a copy of another build."""
|
||||
@@ -529,7 +514,7 @@ class BuildSettings:
|
||||
|
||||
|
||||
class BuildCollection:
|
||||
"""Core: Build Collection Class
|
||||
"""Core: Build Collection Class.
|
||||
|
||||
This object holds all the build setting objects defined by the given
|
||||
project. The build settings are saved as a single JSON file in the
|
||||
@@ -542,7 +527,6 @@ class BuildCollection:
|
||||
self._defaultBuild = ""
|
||||
self._builds: dict[str, BuildSettings] = {}
|
||||
self._loadCollection()
|
||||
return
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""Return the number of builds."""
|
||||
@@ -581,21 +565,18 @@ class BuildCollection:
|
||||
build.setOrder(i)
|
||||
self._lastBuild = lastBuild
|
||||
self._saveCollection()
|
||||
return
|
||||
|
||||
def setDefaultBuild(self, buildID: str) -> None:
|
||||
"""Set the default build id."""
|
||||
if buildID != self._defaultBuild:
|
||||
self._defaultBuild = buildID
|
||||
self._saveCollection()
|
||||
return
|
||||
|
||||
def setBuild(self, build: BuildSettings) -> None:
|
||||
"""Set build settings data in the collection."""
|
||||
if isinstance(build, BuildSettings):
|
||||
self._builds[build.buildID] = build
|
||||
self._saveCollection()
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
@@ -605,7 +586,6 @@ class BuildCollection:
|
||||
"""Remove a build from the collection."""
|
||||
self._builds.pop(buildID, None)
|
||||
self._saveCollection()
|
||||
return
|
||||
|
||||
def builds(self) -> Iterable[tuple[str, str]]:
|
||||
"""Iterate over all available builds."""
|
||||
|
||||
@@ -23,7 +23,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -52,7 +52,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DocMerger:
|
||||
"""Document tool for merging a set of documents into a single new
|
||||
"""Tool: Merge Documents.
|
||||
|
||||
Document tool for merging a set of documents into a single new
|
||||
document. The parameters are defined by the user using the
|
||||
GuiDocMerge dialog.
|
||||
"""
|
||||
@@ -62,7 +64,6 @@ class DocMerger:
|
||||
self._error = ""
|
||||
self._target = None
|
||||
self._text = []
|
||||
return
|
||||
|
||||
@property
|
||||
def targetHandle(self) -> str | None:
|
||||
@@ -85,7 +86,6 @@ class DocMerger:
|
||||
"""
|
||||
self._target = self._project.tree[tHandle]
|
||||
self._text = []
|
||||
return
|
||||
|
||||
def newTargetDoc(self, sHandle: str, label: str) -> None:
|
||||
"""Create a brand new target document based on a source handle
|
||||
@@ -101,7 +101,6 @@ class DocMerger:
|
||||
nwItem.notifyToRefresh()
|
||||
self._target = nwItem
|
||||
self._text = []
|
||||
return
|
||||
|
||||
def appendText(self, sHandle: str, addComment: bool, cmtPrefix: str) -> None:
|
||||
"""Append text from an existing document to the text buffer."""
|
||||
@@ -112,7 +111,6 @@ class DocMerger:
|
||||
status, _ = item.getImportStatus()
|
||||
text = f"% {cmtPrefix} {info}: {item.itemName} [{status}]\n\n{text}"
|
||||
self._text.append(text)
|
||||
return
|
||||
|
||||
def writeTargetDoc(self) -> bool:
|
||||
"""Write the accumulated text into the designated target
|
||||
@@ -158,10 +156,7 @@ class DocSplitter:
|
||||
self._srcHandle = sHandle
|
||||
self._srcItem = srcItem
|
||||
|
||||
return
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""The length of the split job."""
|
||||
return len(self._rawData)
|
||||
|
||||
##
|
||||
@@ -178,7 +173,6 @@ class DocSplitter:
|
||||
"""
|
||||
self._parHandle = pHandle
|
||||
self._inFolder = False
|
||||
return
|
||||
|
||||
def newParentFolder(self, pHandle: str, folderLabel: str) -> None:
|
||||
"""Create a new folder that will be the top level parent item
|
||||
@@ -192,7 +186,6 @@ class DocSplitter:
|
||||
nwItem.notifyToRefresh()
|
||||
self._parHandle = nHandle
|
||||
self._inFolder = True
|
||||
return
|
||||
|
||||
def splitDocument(self, splitData: list, splitText: list[str]) -> None:
|
||||
"""Loop through the split data record and perform the split job
|
||||
@@ -204,12 +197,9 @@ class DocSplitter:
|
||||
chunk = buffer[lineNo:]
|
||||
buffer = buffer[:lineNo]
|
||||
self._rawData.insert(0, (chunk, hLevel, hLabel))
|
||||
return
|
||||
|
||||
def writeDocuments(self, docHierarchy: bool) -> Iterable[bool]:
|
||||
"""An iterator that will write each document in the buffer, and
|
||||
return its new handle, parent handle, and sibling handle.
|
||||
"""
|
||||
"""Write each document in the buffer and yield if successful."""
|
||||
if self._srcHandle and self._srcItem and self._parHandle:
|
||||
pHandle = self._parHandle
|
||||
hHandle = [self._parHandle, None, None, None, None]
|
||||
@@ -260,7 +250,6 @@ class DocDuplicator:
|
||||
|
||||
def __init__(self, project: NWProject) -> None:
|
||||
self._project = project
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
@@ -293,13 +282,16 @@ class DocDuplicator:
|
||||
|
||||
|
||||
class DocSearch:
|
||||
"""Tool> Search Documents.
|
||||
|
||||
A global document search class.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._regEx = re.compile(r"")
|
||||
self._opts = re.IGNORECASE
|
||||
self._words = False
|
||||
self._escape = True
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
@@ -308,22 +300,19 @@ class DocSearch:
|
||||
def setCaseSensitive(self, state: bool) -> None:
|
||||
"""Set the case sensitive search flag."""
|
||||
self._opts = 0 if state else re.IGNORECASE
|
||||
return
|
||||
|
||||
def setWholeWords(self, state: bool) -> None:
|
||||
"""Set the whole words search flag."""
|
||||
self._words = state
|
||||
return
|
||||
|
||||
def setUserRegEx(self, state: bool) -> None:
|
||||
"""Set the escape flag to the opposite state."""
|
||||
self._escape = not state
|
||||
return
|
||||
|
||||
def iterSearch(
|
||||
self, project: NWProject, search: str
|
||||
) -> Iterable[tuple[NWItem, list[tuple[int, int, str]], bool]]:
|
||||
"""Iteratively search through documents in a project."""
|
||||
"""Iterate through documents in a project and apply search."""
|
||||
self._regEx = re.compile(self._buildPattern(search), self._opts)
|
||||
logger.debug("Searching with pattern '%s'", self._regEx.pattern)
|
||||
storage = project.storage
|
||||
@@ -376,7 +365,6 @@ class ProjectBuilder:
|
||||
def __init__(self) -> None:
|
||||
self._path = None
|
||||
self.tr = partial(QCoreApplication.translate, "ProjectBuilder")
|
||||
return
|
||||
|
||||
@property
|
||||
def projPath(self) -> Path | None:
|
||||
@@ -620,4 +608,3 @@ class ProjectBuilder:
|
||||
project.index.rebuild()
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
return
|
||||
|
||||
@@ -20,7 +20,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -53,7 +53,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWBuildDocument:
|
||||
"""Core: Manuscript Document Build Class
|
||||
"""Core: Manuscript Document Build Class.
|
||||
|
||||
This is the core tool that assembles a project and outputs a
|
||||
manuscript, based on a build definition object (BuildSettings).
|
||||
@@ -72,7 +72,6 @@ class NWBuildDocument:
|
||||
self._cache = None
|
||||
self._count = False
|
||||
self._outline = False
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -106,7 +105,6 @@ class NWBuildDocument:
|
||||
def addDocument(self, tHandle: str) -> None:
|
||||
"""Add a document to the build queue manually."""
|
||||
self._queue.append(tHandle)
|
||||
return
|
||||
|
||||
def queueAll(self) -> None:
|
||||
"""Queue all document as defined by the build settings."""
|
||||
@@ -115,7 +113,6 @@ class NWBuildDocument:
|
||||
for item in self._project.tree:
|
||||
if filtered.get(item.itemHandle, False):
|
||||
self._queue.append(item.itemHandle)
|
||||
return
|
||||
|
||||
def iterBuildPreview(self, newPage: bool) -> Iterable[tuple[int, bool]]:
|
||||
"""Build a preview QTextDocument."""
|
||||
@@ -131,7 +128,7 @@ class NWBuildDocument:
|
||||
return
|
||||
|
||||
def iterBuildDocument(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]:
|
||||
"""Wrapper for builders based on format."""
|
||||
"""Select a builder based on format."""
|
||||
self._error = None
|
||||
self._cache = None
|
||||
|
||||
@@ -341,12 +338,10 @@ class NWBuildDocument:
|
||||
scale*self._build.getFloat("format.rightMargin"),
|
||||
)
|
||||
|
||||
filtered = self._build.buildItemFilter(
|
||||
return self._build.buildItemFilter(
|
||||
self._project, withRoots=self._build.getBool("text.addNoteHeadings")
|
||||
)
|
||||
|
||||
return filtered
|
||||
|
||||
def _doBuild(self, bldObj: Tokenizer, tHandle: str, convert: bool = True) -> bool:
|
||||
"""Build a single document and add it to the build object."""
|
||||
tItem = self._project.tree[tHandle]
|
||||
|
||||
@@ -20,7 +20,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
@@ -42,7 +42,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWDocument:
|
||||
"""Core: Document Class
|
||||
"""Core: Document Class.
|
||||
|
||||
A Class wrapping a single novelWriter document file. It represents
|
||||
a project item of nwItemType FILE. The file is not guaranteed to
|
||||
@@ -68,8 +68,6 @@ class NWDocument:
|
||||
if self._handle is not None:
|
||||
self._item = self._project.tree[tHandle]
|
||||
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<NWDocument handle={self._handle}>"
|
||||
|
||||
@@ -357,5 +355,3 @@ class NWDocument:
|
||||
|
||||
else:
|
||||
logger.debug("Unknown meta data: '%s'", metaLine.strip())
|
||||
|
||||
return
|
||||
|
||||
@@ -22,7 +22,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
@@ -56,7 +56,7 @@ KEY_SOURCE = "0123456789bcdfghjklmnpqrstvwxz"
|
||||
|
||||
|
||||
class Index:
|
||||
"""Core: Project Index
|
||||
"""Core: Project Index.
|
||||
|
||||
This class holds the entire index for a given project. The index
|
||||
contains the data that isn't stored in the project items themselves.
|
||||
@@ -99,8 +99,6 @@ class Index:
|
||||
self._indexChange = 0.0
|
||||
self._rootChange = {}
|
||||
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Index project='{self._project.data.name}'>"
|
||||
|
||||
@@ -129,7 +127,6 @@ class Index:
|
||||
def setNovelModelExtraColumn(self, extra: nwNovelExtra) -> None:
|
||||
"""Set the data content type of the novel model extra column."""
|
||||
self._novelExtra = extra
|
||||
return
|
||||
|
||||
##
|
||||
# Public Methods
|
||||
@@ -142,7 +139,6 @@ class Index:
|
||||
self._indexChange = 0.0
|
||||
self._rootChange = {}
|
||||
SHARED.emitIndexCleared(self._project)
|
||||
return
|
||||
|
||||
def rebuild(self) -> None:
|
||||
"""Rebuild the entire index from scratch."""
|
||||
@@ -158,7 +154,6 @@ class Index:
|
||||
for tHandle in self._novelModels:
|
||||
self.refreshNovelModel(tHandle)
|
||||
SHARED.clearMainProgress()
|
||||
return
|
||||
|
||||
def deleteHandle(self, tHandle: str) -> None:
|
||||
"""Delete all entries of a given document handle."""
|
||||
@@ -168,7 +163,6 @@ class Index:
|
||||
del self._tagsIndex[tTag]
|
||||
del self._itemIndex[tHandle]
|
||||
SHARED.emitIndexChangedTags(self._project, [], delTags)
|
||||
return
|
||||
|
||||
def reIndexHandle(self, tHandle: str | None) -> None:
|
||||
"""Put a file back into the index. This is used when files are
|
||||
@@ -178,7 +172,6 @@ class Index:
|
||||
if tHandle and self._project.tree.checkType(tHandle, nwItemType.FILE):
|
||||
logger.debug("Re-indexing item '%s'", tHandle)
|
||||
self.scanText(tHandle, self._project.storage.getDocumentText(tHandle))
|
||||
return
|
||||
|
||||
def refreshHandle(self, tHandle: str) -> None:
|
||||
"""Update the class for all tags of a handle."""
|
||||
@@ -188,7 +181,6 @@ class Index:
|
||||
self.deleteHandle(tHandle)
|
||||
else:
|
||||
self._tagsIndex.updateClass(tHandle, item.itemClass.name)
|
||||
return
|
||||
|
||||
def indexChangedSince(self, checkTime: int | float) -> bool:
|
||||
"""Check if the index has changed since a given time."""
|
||||
@@ -211,7 +203,6 @@ class Index:
|
||||
model.setExtraColumn(self._novelExtra)
|
||||
self._appendSubTreeToModel(tHandle, model)
|
||||
model.endResetModel()
|
||||
return
|
||||
|
||||
def updateNovelModelData(self, nwItem: NWItem) -> bool:
|
||||
"""Refresh a novel model."""
|
||||
@@ -428,8 +419,6 @@ class Index:
|
||||
if updated or deleted:
|
||||
SHARED.emitIndexChangedTags(self._project, updated, deleted)
|
||||
|
||||
return
|
||||
|
||||
def _scanInactive(self, nwItem: NWItem, text: str) -> None:
|
||||
"""Scan an inactive document for meta data."""
|
||||
for line in text.splitlines():
|
||||
@@ -438,7 +427,6 @@ class Index:
|
||||
if hDepth != "H0":
|
||||
nwItem.setMainHeading(hDepth)
|
||||
break
|
||||
return
|
||||
|
||||
def _splitHeading(self, line: str) -> tuple[str, str]:
|
||||
"""Split a heading into its heading level and text value."""
|
||||
@@ -462,7 +450,6 @@ class Index:
|
||||
"""Count text stats and save the counts to the index."""
|
||||
cC, wC, pC = standardCounter(text)
|
||||
self._itemIndex.setHeadingCounts(tHandle, sTitle, cC, wC, pC)
|
||||
return
|
||||
|
||||
def _indexKeyword(
|
||||
self, tHandle: str, line: str, sTitle: str, itemClass: nwItemClass, tags: dict[str, bool]
|
||||
@@ -498,7 +485,6 @@ class Index:
|
||||
model.setExtraColumn(self._novelExtra)
|
||||
self._appendSubTreeToModel(tHandle, model)
|
||||
self._novelModels[tHandle] = model
|
||||
return
|
||||
|
||||
def _appendSubTreeToModel(self, tHandle: str, model: NovelModel) -> None:
|
||||
"""Append all active novel documents to a novel model."""
|
||||
@@ -509,7 +495,6 @@ class Index:
|
||||
and node.item.isActive
|
||||
):
|
||||
model.append(node)
|
||||
return
|
||||
|
||||
##
|
||||
# Check @ Lines
|
||||
@@ -683,15 +668,13 @@ class Index:
|
||||
"words": hItem.wordCount,
|
||||
}
|
||||
|
||||
result = [(
|
||||
return [(
|
||||
tKey,
|
||||
tData[tKey]["level"],
|
||||
tData[tKey]["title"],
|
||||
tData[tKey]["words"]
|
||||
) for tKey in tOrder]
|
||||
|
||||
return result
|
||||
|
||||
def getCounts(self, tHandle: str, sTitle: str | None = None) -> tuple[int, int, int]:
|
||||
"""Return the counts for a file, or a section of a file,
|
||||
starting at title sTitle if it is provided.
|
||||
@@ -796,7 +779,7 @@ class Index:
|
||||
# =====================
|
||||
|
||||
class TagsIndex:
|
||||
"""Core: Tags Index Wrapper Class
|
||||
"""Core: Tags Index Wrapper Class.
|
||||
|
||||
A wrapper class that holds the reverse lookup tags index. This is
|
||||
just a simple wrapper around a single dictionary to keep tighter
|
||||
@@ -807,14 +790,12 @@ class TagsIndex:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._tags: dict[str, dict[str, str]] = {}
|
||||
return
|
||||
|
||||
def __contains__(self, tagKey: str) -> bool:
|
||||
return tagKey.lower() in self._tags
|
||||
|
||||
def __delitem__(self, tagKey: str) -> None:
|
||||
self._tags.pop(tagKey.lower(), None)
|
||||
return
|
||||
|
||||
def __getitem__(self, tagKey: str) -> dict | None:
|
||||
return self._tags.get(tagKey.lower(), None)
|
||||
@@ -826,7 +807,6 @@ class TagsIndex:
|
||||
def clear(self) -> None:
|
||||
"""Clear the index."""
|
||||
self._tags = {}
|
||||
return
|
||||
|
||||
def items(self) -> ItemsView:
|
||||
"""Return a dictionary view of all tags."""
|
||||
@@ -842,7 +822,6 @@ class TagsIndex:
|
||||
"heading": sTitle,
|
||||
"class": className,
|
||||
}
|
||||
return
|
||||
|
||||
def tagName(self, tagKey: str, default: str = "") -> str:
|
||||
"""Get the name of a given tag."""
|
||||
@@ -882,7 +861,6 @@ class TagsIndex:
|
||||
for entry in self._tags.values():
|
||||
if entry.get("handle") == tHandle:
|
||||
entry["class"] = className
|
||||
return
|
||||
|
||||
##
|
||||
# Pack/Unpack
|
||||
@@ -925,11 +903,9 @@ class TagsIndex:
|
||||
|
||||
self.add(name, display, handle, heading, className)
|
||||
|
||||
return
|
||||
|
||||
|
||||
class IndexCache:
|
||||
"""Core: Item Index Lookup Data Class
|
||||
"""Core: Item Index Lookup Data Class.
|
||||
|
||||
A small data class passed between all objects of the Item Index
|
||||
which provides lookup capabilities and caching for shared data.
|
||||
@@ -941,14 +917,13 @@ class IndexCache:
|
||||
self.tags: TagsIndex = tagsIndex
|
||||
self.story: set[str] = set()
|
||||
self.note: set[str] = set()
|
||||
return
|
||||
|
||||
|
||||
# The Item Index Objects
|
||||
# ======================
|
||||
|
||||
class ItemIndex:
|
||||
"""Core: Item Index Wrapper Class
|
||||
"""Core: Item Index Wrapper Class.
|
||||
|
||||
A wrapper object holding the indexed items. This is a wrapper
|
||||
class around a single storage dictionary with a set of utility
|
||||
@@ -963,14 +938,12 @@ class ItemIndex:
|
||||
self._project = project
|
||||
self._cache = IndexCache(tagsIndex)
|
||||
self._items: dict[str, IndexNode] = {}
|
||||
return
|
||||
|
||||
def __contains__(self, tHandle: str) -> bool:
|
||||
return tHandle in self._items
|
||||
|
||||
def __delitem__(self, tHandle: str) -> None:
|
||||
self._items.pop(tHandle, None)
|
||||
return
|
||||
|
||||
def __getitem__(self, tHandle: str) -> IndexNode | None:
|
||||
return self._items.get(tHandle, None)
|
||||
@@ -982,14 +955,12 @@ class ItemIndex:
|
||||
def clear(self) -> None:
|
||||
"""Clear the index."""
|
||||
self._items = {}
|
||||
return
|
||||
|
||||
def add(self, tHandle: str, nwItem: NWItem) -> None:
|
||||
"""Add a new item to the index. This will overwrite the item if
|
||||
it already exists.
|
||||
"""
|
||||
self._items[tHandle] = IndexNode(self._cache, tHandle, nwItem)
|
||||
return
|
||||
|
||||
def allStoryKeys(self) -> set[str]:
|
||||
"""Return all story structure keys."""
|
||||
@@ -1064,7 +1035,6 @@ class ItemIndex:
|
||||
"""
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].setHeadingCounts(sTitle, cC, wC, pC)
|
||||
return
|
||||
|
||||
def setHeadingComment(
|
||||
self, tHandle: str, sTitle: str,
|
||||
@@ -1073,25 +1043,21 @@ class ItemIndex:
|
||||
"""Set a story comment for a heading on a given item."""
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].setHeadingComment(sTitle, comment, key, text)
|
||||
return
|
||||
|
||||
def setHeadingTag(self, tHandle: str, sTitle: str, tagKey: str) -> None:
|
||||
"""Set the main tag for a heading on a given item."""
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].setHeadingTag(sTitle, tagKey)
|
||||
return
|
||||
|
||||
def addHeadingRef(self, tHandle: str, sTitle: str, tagKeys: list[str], refType: str) -> None:
|
||||
"""Set the reference tags for a heading on a given item."""
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].addHeadingRef(sTitle, tagKeys, refType)
|
||||
return
|
||||
|
||||
def addNoteKey(self, tHandle: str, style: T_NoteTypes, key: str) -> None:
|
||||
"""Set notes key for a given item."""
|
||||
if tHandle in self._items:
|
||||
self._items[tHandle].addNoteKey(style, key)
|
||||
return
|
||||
|
||||
def genNewNoteKey(self, tHandle: str, style: T_NoteTypes) -> str:
|
||||
"""Set notes key for a given item."""
|
||||
@@ -1131,5 +1097,3 @@ class ItemIndex:
|
||||
tItem = IndexNode(self._cache, tHandle, nwItem)
|
||||
tItem.unpackData(tData)
|
||||
self._items[tHandle] = tItem
|
||||
|
||||
return
|
||||
|
||||
@@ -23,7 +23,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -50,7 +50,7 @@ NOTE_TYPES: list[T_NoteTypes] = ["footnotes", "comments"]
|
||||
|
||||
|
||||
class IndexNode:
|
||||
"""Core: Single Index Item Node Class
|
||||
"""Core: Single Index Item Node Class.
|
||||
|
||||
This object represents the index data of a project item (NWItem).
|
||||
It holds a record of all the headings in the text, and the meta data
|
||||
@@ -68,7 +68,6 @@ class IndexNode:
|
||||
self._headings: dict[str, IndexHeading] = {TT_NONE: IndexHeading(self._cache, TT_NONE)}
|
||||
self._notes: dict[str, set[str]] = {}
|
||||
self._count = 0
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<IndexNode handle='{self._handle}'>"
|
||||
@@ -107,39 +106,33 @@ class IndexNode:
|
||||
if TT_NONE in self._headings:
|
||||
self._headings.pop(TT_NONE)
|
||||
self._headings[tHeading.key] = tHeading
|
||||
return
|
||||
|
||||
def setHeadingCounts(self, sTitle: str, cCount: int, wCount: int, pCount: int) -> None:
|
||||
"""Set the character, word and paragraph count of a heading."""
|
||||
if sTitle in self._headings:
|
||||
self._headings[sTitle].setCounts([cCount, wCount, pCount])
|
||||
return
|
||||
|
||||
def setHeadingComment(self, sTitle: str, comment: nwComment, key: str, text: str) -> None:
|
||||
"""Set the comment text of a heading."""
|
||||
if sTitle in self._headings:
|
||||
self._headings[sTitle].setComment(comment.name, key, text)
|
||||
return
|
||||
|
||||
def setHeadingTag(self, sTitle: str, tag: str) -> None:
|
||||
"""Set the tag of a heading."""
|
||||
if sTitle in self._headings:
|
||||
self._headings[sTitle].setTag(tag)
|
||||
return
|
||||
|
||||
def addHeadingRef(self, sTitle: str, tags: list[str], keyword: str) -> None:
|
||||
"""Add a reference key and all its types to a heading."""
|
||||
if sTitle in self._headings:
|
||||
for tag in tags:
|
||||
self._headings[sTitle].addReference(tag, keyword)
|
||||
return
|
||||
|
||||
def addNoteKey(self, style: T_NoteTypes, key: str) -> None:
|
||||
"""Add a note key to the index."""
|
||||
if style not in self._notes:
|
||||
self._notes[style] = set()
|
||||
self._notes[style].add(key)
|
||||
return
|
||||
|
||||
##
|
||||
# Data Methods
|
||||
@@ -195,11 +188,10 @@ class IndexNode:
|
||||
self._notes[style] = set(keys)
|
||||
else:
|
||||
raise KeyError("Index node contains an invalid key")
|
||||
return
|
||||
|
||||
|
||||
class IndexHeading:
|
||||
"""Core: Single Index Heading Class
|
||||
"""Core: Single Index Heading Class.
|
||||
|
||||
This object represents a section of text in a project item
|
||||
associated with a single (valid) heading. It holds a separate record
|
||||
@@ -224,7 +216,6 @@ class IndexHeading:
|
||||
self._tag = ""
|
||||
self._refs: dict[str, set[str]] = {}
|
||||
self._comments: dict[str, str] = {}
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<IndexHeading key='{self._key}'>"
|
||||
@@ -289,12 +280,10 @@ class IndexHeading:
|
||||
"""Set the level of the heading if it's a valid value."""
|
||||
if level in nwStyles.H_VALID:
|
||||
self._level = level
|
||||
return
|
||||
|
||||
def setLine(self, line: int) -> None:
|
||||
"""Set the line number of a heading."""
|
||||
self._line = max(0, checkInt(line, 0))
|
||||
return
|
||||
|
||||
def setCounts(self, counts: Sequence[int]) -> None:
|
||||
"""Set the character, word and paragraph count. Make sure the
|
||||
@@ -306,7 +295,6 @@ class IndexHeading:
|
||||
max(0, checkInt(counts[1], 0)),
|
||||
max(0, checkInt(counts[2], 0)),
|
||||
)
|
||||
return
|
||||
|
||||
def setComment(self, comment: str, key: str, text: str) -> None:
|
||||
"""Set the text for a comment and make sure it is a string."""
|
||||
@@ -319,12 +307,10 @@ class IndexHeading:
|
||||
case "note" if key:
|
||||
self._cache.note.add(key)
|
||||
self._comments[f"note.{key}"] = str(text)
|
||||
return
|
||||
|
||||
def setTag(self, tag: str) -> None:
|
||||
"""Set the tag for references, and make sure it is a string."""
|
||||
self._tag = str(tag).lower()
|
||||
return
|
||||
|
||||
def addReference(self, tag: str, keyword: str) -> None:
|
||||
"""Add a record of a reference tag, and what keyword types it is
|
||||
@@ -335,7 +321,6 @@ class IndexHeading:
|
||||
if tag not in self._refs:
|
||||
self._refs[tag] = set()
|
||||
self._refs[tag].add(keyword)
|
||||
return
|
||||
|
||||
##
|
||||
# Getters
|
||||
@@ -409,4 +394,3 @@ class IndexHeading:
|
||||
self.setComment(comment, compact(kind), str(entry))
|
||||
else:
|
||||
raise KeyError("Unknown key in heading entry")
|
||||
return
|
||||
|
||||
@@ -20,7 +20,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWItem:
|
||||
"""Core: Item Data Class
|
||||
"""Core: Item Data Class.
|
||||
|
||||
This class holds all the project information about a project item.
|
||||
Each item must be associated with a project and have a valid handle.
|
||||
@@ -84,16 +84,14 @@ class NWItem:
|
||||
self._wordInit = 0 # Initial character count
|
||||
self._charInit = 0 # Initial word count
|
||||
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<NWItem handle={self._handle}, parent={self._parent}, name='{self._name}'>"
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
"""The truthiness of the class. The handle used to be initiated
|
||||
to None, but this is no longer the case. It should always
|
||||
evaluate to True since 2.1-beta1, although unpack and the NWTree
|
||||
class can leave it as an empty string.
|
||||
"""Check the truthiness of the class. The handle used to be
|
||||
initiated to None, but this is no longer the case. It should
|
||||
always evaluate to True since 2.1-beta1, although unpack and the
|
||||
NWTree class can leave it as an empty string.
|
||||
"""
|
||||
return bool(self._handle)
|
||||
|
||||
@@ -206,15 +204,13 @@ class NWItem:
|
||||
meta["cursorPos"] = str(self._cursorPos)
|
||||
name["active"] = yesNo(self._active)
|
||||
|
||||
data = {
|
||||
return {
|
||||
"name": str(self._name),
|
||||
"itemAttr": item,
|
||||
"metaAttr": meta,
|
||||
"nameAttr": name,
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
def unpack(self, data: dict) -> bool:
|
||||
"""Set the values from a data dictionary."""
|
||||
item = data.get("itemAttr", {})
|
||||
@@ -298,13 +294,11 @@ class NWItem:
|
||||
def notifyToRefresh(self) -> None:
|
||||
"""Notify GUI that item info needs to be refreshed."""
|
||||
self._project.tree.refreshItems([self._handle])
|
||||
return
|
||||
|
||||
def notifyNovelStructureChange(self) -> None:
|
||||
"""Notify that the structure of a novel has changed."""
|
||||
if self._root and self._class == nwItemClass.NOVEL:
|
||||
self._project.tree.novelStructureChanged(self._root)
|
||||
return
|
||||
|
||||
##
|
||||
# Lookup Methods
|
||||
@@ -457,8 +451,6 @@ class NWItem:
|
||||
if self._import is None:
|
||||
self.setImport("New") # This forces a default value lookup
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Set Item Values
|
||||
##
|
||||
@@ -469,7 +461,6 @@ class NWItem:
|
||||
self._name = simplified(name)
|
||||
else:
|
||||
self._name = ""
|
||||
return
|
||||
|
||||
def setParent(self, handle: Any) -> None:
|
||||
"""Set the parent handle, and ensure it is valid."""
|
||||
@@ -479,7 +470,6 @@ class NWItem:
|
||||
self._parent = handle
|
||||
else:
|
||||
self._parent = None
|
||||
return
|
||||
|
||||
def setRoot(self, handle: Any) -> None:
|
||||
"""Set the root handle, and ensure it is valid."""
|
||||
@@ -489,7 +479,6 @@ class NWItem:
|
||||
self._root = handle
|
||||
else:
|
||||
self._root = None
|
||||
return
|
||||
|
||||
def setOrder(self, order: Any) -> None:
|
||||
"""Set the item order, and ensure that it is valid. This value
|
||||
@@ -497,7 +486,6 @@ class NWItem:
|
||||
the moment.
|
||||
"""
|
||||
self._order = checkInt(order, 0)
|
||||
return
|
||||
|
||||
def setType(self, value: Any) -> None:
|
||||
"""Set the item type from either a proper nwItemType, or set it
|
||||
@@ -510,7 +498,6 @@ class NWItem:
|
||||
else:
|
||||
logger.error("Unrecognised item type '%s'", value)
|
||||
self._type = nwItemType.NO_TYPE
|
||||
return
|
||||
|
||||
def setClass(self, value: Any) -> None:
|
||||
"""Set the item class from either a proper nwItemClass, or set
|
||||
@@ -523,7 +510,6 @@ class NWItem:
|
||||
else:
|
||||
logger.error("Unrecognised item class '%s'", value)
|
||||
self._class = nwItemClass.NO_CLASS
|
||||
return
|
||||
|
||||
def setLayout(self, value: Any) -> None:
|
||||
"""Set the item layout from either a proper nwItemLayout, or set
|
||||
@@ -536,21 +522,18 @@ class NWItem:
|
||||
else:
|
||||
logger.error("Unrecognised item layout '%s'", value)
|
||||
self._layout = nwItemLayout.NO_LAYOUT
|
||||
return
|
||||
|
||||
def setStatus(self, value: Any) -> None:
|
||||
"""Set the item status by looking it up in the valid status
|
||||
items of the current project.
|
||||
"""
|
||||
self._status = self._project.data.itemStatus.check(value)
|
||||
return
|
||||
|
||||
def setImport(self, value: Any) -> None:
|
||||
"""Set the item importance by looking it up in the valid import
|
||||
items of the current project.
|
||||
"""
|
||||
self._import = self._project.data.itemImport.check(value)
|
||||
return
|
||||
|
||||
def setActive(self, state: Any) -> None:
|
||||
"""Set the active flag."""
|
||||
@@ -558,7 +541,6 @@ class NWItem:
|
||||
self._active = state
|
||||
else:
|
||||
self._active = False
|
||||
return
|
||||
|
||||
def setExpanded(self, state: Any) -> None:
|
||||
"""Set the expanded status of an item in the project tree."""
|
||||
@@ -566,7 +548,6 @@ class NWItem:
|
||||
self._expanded = state
|
||||
else:
|
||||
self._expanded = False
|
||||
return
|
||||
|
||||
##
|
||||
# Set Document Meta Data
|
||||
@@ -576,7 +557,6 @@ class NWItem:
|
||||
"""Set the main heading level."""
|
||||
if value in nwStyles.H_LEVEL:
|
||||
self._heading = value
|
||||
return
|
||||
|
||||
def setCharCount(self, count: Any) -> None:
|
||||
"""Set the character count, and ensure that it is an integer."""
|
||||
@@ -584,7 +564,6 @@ class NWItem:
|
||||
self._charCount = max(0, count)
|
||||
else:
|
||||
self._charCount = 0
|
||||
return
|
||||
|
||||
def setWordCount(self, count: Any) -> None:
|
||||
"""Set the word count, and ensure that it is an integer."""
|
||||
@@ -592,7 +571,6 @@ class NWItem:
|
||||
self._wordCount = max(0, count)
|
||||
else:
|
||||
self._wordCount = 0
|
||||
return
|
||||
|
||||
def setParaCount(self, count: Any) -> None:
|
||||
"""Set the paragraph count, and ensure that it is an integer."""
|
||||
@@ -600,7 +578,6 @@ class NWItem:
|
||||
self._paraCount = max(0, count)
|
||||
else:
|
||||
self._paraCount = 0
|
||||
return
|
||||
|
||||
def setCursorPos(self, position: Any) -> None:
|
||||
"""Set the cursor position, and ensure that it is an integer."""
|
||||
@@ -608,4 +585,3 @@ class NWItem:
|
||||
self._cursorPos = max(0, position)
|
||||
else:
|
||||
self._cursorPos = 0
|
||||
return
|
||||
|
||||
@@ -21,7 +21,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -66,7 +66,7 @@ T_NodeData = str | QIcon | QFont | Qt.AlignmentFlag | None
|
||||
|
||||
|
||||
class ProjectNode:
|
||||
"""Core: Project Model Node Class
|
||||
"""Core: Project Model Node Class.
|
||||
|
||||
The project tree structure is saved as nodes in a tree, starting
|
||||
from a root node. This class makes up these nodes.
|
||||
@@ -103,7 +103,6 @@ class ProjectNode:
|
||||
self._count = 0
|
||||
self.refresh()
|
||||
self.updateCount()
|
||||
return
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
@@ -114,7 +113,7 @@ class ProjectNode:
|
||||
)
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
"""A node should always evaluate to True."""
|
||||
# A node should always evaluate to True.
|
||||
return True
|
||||
|
||||
##
|
||||
@@ -162,15 +161,12 @@ class ProjectNode:
|
||||
self._cache[C_STATUS_TIP] = sText
|
||||
self._cache[C_STATUS_ACCESS] = sText
|
||||
|
||||
return
|
||||
|
||||
def updateCount(self, propagate: bool = True) -> None:
|
||||
"""Update counts, and propagate upwards in the tree."""
|
||||
self._count = self._item.mainCount + sum(c._count for c in self._children) # noqa: SLF001
|
||||
self._cache[C_COUNT_TEXT] = f"{self._count:n}"
|
||||
if propagate and (parent := self._parent):
|
||||
parent.updateCount()
|
||||
return
|
||||
|
||||
##
|
||||
# Data Access
|
||||
@@ -223,7 +219,6 @@ class ProjectNode:
|
||||
self._children.append(child)
|
||||
self._refreshChildrenPos()
|
||||
self._item.notifyNovelStructureChange()
|
||||
return
|
||||
|
||||
def takeChild(self, pos: int) -> ProjectNode | None:
|
||||
"""Remove a child item and return it."""
|
||||
@@ -243,7 +238,6 @@ class ProjectNode:
|
||||
self._children.insert(target, node)
|
||||
self._refreshChildrenPos()
|
||||
self._item.notifyNovelStructureChange()
|
||||
return
|
||||
|
||||
def setExpanded(self, state: bool) -> None:
|
||||
"""Set the node's expanded state."""
|
||||
@@ -251,7 +245,6 @@ class ProjectNode:
|
||||
self._item.setExpanded(True)
|
||||
else:
|
||||
self._item.setExpanded(False)
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
@@ -262,14 +255,12 @@ class ProjectNode:
|
||||
for node in self._children:
|
||||
children.append(node)
|
||||
node._recursiveAppendChildren(children) # noqa: SLF001
|
||||
return
|
||||
|
||||
def _refreshChildrenPos(self) -> None:
|
||||
"""Update the row value on all children."""
|
||||
for n, child in enumerate(self._children):
|
||||
child._row = n # noqa: SLF001
|
||||
child.item.setOrder(n)
|
||||
return
|
||||
|
||||
def _updateRelationships(self, child: ProjectNode) -> None:
|
||||
"""Update a child item's relationships."""
|
||||
@@ -282,11 +273,10 @@ class ProjectNode:
|
||||
child.item.setParent(None)
|
||||
child.item.setRoot(child.item.itemHandle)
|
||||
child.item.setClassDefaults(child.item.itemClass)
|
||||
return
|
||||
|
||||
|
||||
class ProjectModel(QAbstractItemModel):
|
||||
"""Core: Project Model Class
|
||||
"""Core: Project Model Class.
|
||||
|
||||
This class provides the interface for the tree widget used on the
|
||||
GUI. It implements the QModelIndex based interface required, adds
|
||||
@@ -302,11 +292,9 @@ class ProjectModel(QAbstractItemModel):
|
||||
self._root = ProjectNode(NWItem(tree.project, INV_ROOT))
|
||||
self._root.item.setName("Invisible Root")
|
||||
logger.debug("Ready: ProjectModel")
|
||||
return
|
||||
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: ProjectModel")
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -363,7 +351,7 @@ class ProjectModel(QAbstractItemModel):
|
||||
##
|
||||
|
||||
def supportedDropActions(self) -> Qt.DropAction:
|
||||
"""Return supported drop actions"""
|
||||
"""Return supported drop actions."""
|
||||
return Qt.DropAction.MoveAction
|
||||
|
||||
def mimeTypes(self) -> list[str]:
|
||||
@@ -445,7 +433,6 @@ class ProjectModel(QAbstractItemModel):
|
||||
self.beginInsertRows(parent, row, row)
|
||||
node.addChild(child, row)
|
||||
self.endInsertRows()
|
||||
return
|
||||
|
||||
def removeChild(self, parent: QModelIndex, pos: int) -> ProjectNode | None:
|
||||
"""Remove a node from the model and return it."""
|
||||
@@ -469,7 +456,6 @@ class ProjectModel(QAbstractItemModel):
|
||||
self.beginMoveRows(index.parent(), pos, pos, index.parent(), end)
|
||||
parent.moveChild(pos, new)
|
||||
self.endMoveRows()
|
||||
return
|
||||
|
||||
def multiMove(self, indices: list[QModelIndex], target: QModelIndex, pos: int = -1) -> None:
|
||||
"""Move multiple items to a new location."""
|
||||
@@ -497,7 +483,6 @@ class ProjectModel(QAbstractItemModel):
|
||||
node._updateRelationships(child) # noqa: SLF001
|
||||
child.item.notifyToRefresh()
|
||||
node.item.notifyToRefresh()
|
||||
return
|
||||
|
||||
##
|
||||
# Other Methods
|
||||
@@ -506,7 +491,6 @@ class ProjectModel(QAbstractItemModel):
|
||||
def clear(self) -> None:
|
||||
"""Clear the project model."""
|
||||
self._root.children.clear()
|
||||
return
|
||||
|
||||
def allExpanded(self) -> list[QModelIndex]:
|
||||
"""Return a list of all expanded items."""
|
||||
|
||||
@@ -20,7 +20,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -54,6 +54,7 @@ T_NodeData = str | QIcon | QPixmap | Qt.AlignmentFlag | None
|
||||
|
||||
|
||||
class NovelModel(QAbstractTableModel):
|
||||
"""Core: Novel Model CLass."""
|
||||
|
||||
__slots__ = ("_columns", "_extraKey", "_extraLabel", "_more", "_rows")
|
||||
|
||||
@@ -64,11 +65,9 @@ class NovelModel(QAbstractTableModel):
|
||||
self._columns = 3
|
||||
self._extraKey = ""
|
||||
self._extraLabel = ""
|
||||
return
|
||||
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: NovelModel")
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -102,7 +101,6 @@ class NovelModel(QAbstractTableModel):
|
||||
self._columns = 4
|
||||
self._extraKey = nwKeyWords.PLOT_KEY
|
||||
self._extraLabel = trConst(nwLabels.KEY_NAME[nwKeyWords.PLOT_KEY])
|
||||
return
|
||||
|
||||
##
|
||||
# Model Interface
|
||||
@@ -147,7 +145,6 @@ class NovelModel(QAbstractTableModel):
|
||||
def clear(self) -> None:
|
||||
"""Clear the model."""
|
||||
self._rows.clear()
|
||||
return
|
||||
|
||||
def append(self, node: IndexNode) -> None:
|
||||
"""Append a node to the model."""
|
||||
@@ -155,7 +152,6 @@ class NovelModel(QAbstractTableModel):
|
||||
for key, head in node.items():
|
||||
if key != "T0000":
|
||||
self._rows.append(self._generateEntry(handle, key, head))
|
||||
return
|
||||
|
||||
def refresh(self, node: IndexNode) -> bool:
|
||||
"""Refresh an index node."""
|
||||
|
||||
@@ -21,7 +21,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
@@ -80,7 +80,7 @@ VALID_MAP: dict[str, set[str]] = {
|
||||
|
||||
|
||||
class OptionState:
|
||||
"""Core: GUI Options Storage
|
||||
"""Core: GUI Options Storage.
|
||||
|
||||
A class for storing the state of the GUI. The data is stored per
|
||||
project. Settings that should be project-independent are stored in
|
||||
@@ -90,7 +90,6 @@ class OptionState:
|
||||
def __init__(self, project: NWProject) -> None:
|
||||
self._project = project
|
||||
self._state = {}
|
||||
return
|
||||
|
||||
##
|
||||
# Load and Save Cache
|
||||
|
||||
@@ -20,7 +20,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
@@ -57,6 +57,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWProjectState(Enum):
|
||||
"""The state of the loaded project."""
|
||||
|
||||
UNKNOWN = 0
|
||||
LOCKED = 1
|
||||
@@ -65,6 +66,11 @@ class NWProjectState(Enum):
|
||||
|
||||
|
||||
class NWProject:
|
||||
"""Core: novelWriter Project Class.
|
||||
|
||||
This class is the parent class of the project, and holds instances
|
||||
of project data, the project tree, and the project index.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"_changed", "_data", "_index", "_langData", "_options", "_session",
|
||||
@@ -92,17 +98,13 @@ class NWProject:
|
||||
|
||||
logger.debug("Ready: NWProject")
|
||||
|
||||
return
|
||||
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: NWProject")
|
||||
return
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clear the project."""
|
||||
self._tree.clear()
|
||||
self._index.clear()
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -263,7 +265,6 @@ class NWProject:
|
||||
if rHandle and (tHandle := SHARED.project.newFile(tag.title(), rHandle)):
|
||||
self.writeNewFile(tHandle, 1, False, f"@tag: {tag}\n\n")
|
||||
self._tree.refreshItems([tHandle])
|
||||
return
|
||||
|
||||
##
|
||||
# Project Methods
|
||||
@@ -441,7 +442,6 @@ class NWProject:
|
||||
self._tree.writeToCFile()
|
||||
self._session.appendSession(idleTime)
|
||||
self._storage.closeSession()
|
||||
return
|
||||
|
||||
def backupProject(self, doNotify: bool) -> bool:
|
||||
"""Create a zip file of the entire project."""
|
||||
@@ -499,7 +499,6 @@ class NWProject:
|
||||
self._data.itemImport.add(None, self.tr("Minor"), "purple", "BLOCK_2", 0)
|
||||
self._data.itemImport.add(None, self.tr("Major"), "purple", "BLOCK_3", 0)
|
||||
self._data.itemImport.add(None, self.tr("Main"), "purple", "BLOCK_4", 0)
|
||||
return
|
||||
|
||||
def setProjectLang(self, language: str | None) -> None:
|
||||
"""Set the project-specific language."""
|
||||
@@ -508,7 +507,6 @@ class NWProject:
|
||||
self._data.setLanguage(language)
|
||||
self._loadProjectLocalisation()
|
||||
self.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setProjectChanged(self, status: bool) -> bool:
|
||||
"""Toggle the project changed flag, and propagate the
|
||||
@@ -527,7 +525,6 @@ class NWProject:
|
||||
"""Update the total word and character count values."""
|
||||
wNovel, wNotes, cNovel, cNotes = self._tree.sumCounts()
|
||||
self._data.setCurrCounts(wNovel=wNovel, wNotes=wNotes, cNovel=cNovel, cNotes=cNotes)
|
||||
return
|
||||
|
||||
def countStatus(self) -> None:
|
||||
"""Count how many times the various status flags are used in the
|
||||
@@ -541,7 +538,6 @@ class NWProject:
|
||||
self._data.itemStatus.increment(nwItem.itemStatus)
|
||||
else:
|
||||
self._data.itemImport.increment(nwItem.itemImport)
|
||||
return
|
||||
|
||||
def updateStatus(self, kind: T_StatusKind, update: T_UpdateEntry) -> None:
|
||||
"""Update status or import entries."""
|
||||
@@ -553,13 +549,11 @@ class NWProject:
|
||||
self._data.itemImport.update(update)
|
||||
SHARED.emitStatusLabelsChanged(self, kind)
|
||||
self._tree.refreshAllItems()
|
||||
return
|
||||
|
||||
def updateTheme(self) -> None:
|
||||
"""Update theme elements."""
|
||||
self._data.itemStatus.refreshIcons()
|
||||
self._data.itemImport.refreshIcons()
|
||||
return
|
||||
|
||||
def localLookup(self, word: str | int) -> str:
|
||||
"""Look up a word or number in the translation map for the
|
||||
|
||||
@@ -20,7 +20,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -41,7 +41,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWProjectData:
|
||||
"""Core: Project Data Class
|
||||
"""Core: Project Data Class.
|
||||
|
||||
The class holds all project data from the main XML file, aside from
|
||||
the list of project items.
|
||||
@@ -86,8 +86,6 @@ class NWProjectData:
|
||||
self._status = NWStatus(NWStatus.STATUS)
|
||||
self._import = NWStatus(NWStatus.IMPORT)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
@@ -191,13 +189,11 @@ class NWProjectData:
|
||||
"""Increment the save count by one."""
|
||||
self._saveCount += 1
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def incAutoCount(self) -> None:
|
||||
"""Increment the auto save count by one."""
|
||||
self._autoCount += 1
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
##
|
||||
# Getters
|
||||
@@ -219,67 +215,57 @@ class NWProjectData:
|
||||
elif value != self._uuid:
|
||||
self._uuid = value
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setName(self, value: str | None) -> None:
|
||||
"""Set a new project name."""
|
||||
if value != self._name:
|
||||
self._name = simplified(str(value or ""))
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setAuthor(self, value: str | None) -> None:
|
||||
"""Set the author value."""
|
||||
if value != self._author:
|
||||
self._author = simplified(str(value or ""))
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setSaveCount(self, value: Any) -> None:
|
||||
"""Set the save count from last session."""
|
||||
self._saveCount = checkInt(value, 0)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setAutoCount(self, value: Any) -> None:
|
||||
"""Set the auto save count from last session."""
|
||||
self._autoCount = checkInt(value, 0)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setEditTime(self, value: Any) -> None:
|
||||
"""Set the edit time from last session."""
|
||||
self._editTime = checkInt(value, 0)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setDoBackup(self, value: Any) -> None:
|
||||
"""Set the do write backup flag."""
|
||||
if value != self._doBackup:
|
||||
self._doBackup = checkBool(value, False)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setLanguage(self, value: str | None) -> None:
|
||||
"""Set the project language."""
|
||||
if value != self._language:
|
||||
self._language = checkStringNone(value, None)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setSpellCheck(self, value: Any) -> None:
|
||||
"""Set the spell check flag."""
|
||||
if value != self._spellCheck:
|
||||
self._spellCheck = checkBool(value, False)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setSpellLang(self, value: str | None) -> None:
|
||||
"""Set the spell check language."""
|
||||
if value != self._spellLang:
|
||||
self._spellLang = checkStringNone(value, None)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setLastHandle(self, value: str | None, component: str) -> None:
|
||||
"""Set a last used handle into the handle registry for a given
|
||||
@@ -288,7 +274,6 @@ class NWProjectData:
|
||||
if isinstance(component, str):
|
||||
self._lastHandle[component] = checkStringNone(value, None)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setLastHandles(self, value: dict) -> None:
|
||||
"""Set the full last handles dictionary to a new set of values.
|
||||
@@ -299,7 +284,6 @@ class NWProjectData:
|
||||
if key in self._lastHandle:
|
||||
self._lastHandle[key] = str(entry) if isHandle(entry) else None
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setInitCounts(
|
||||
self, wNovel: Any = None, wNotes: Any = None, cNovel: Any = None, cNotes: Any = None
|
||||
@@ -321,7 +305,6 @@ class NWProjectData:
|
||||
count = checkInt(cNotes, 0)
|
||||
self._initCounts[3] = count
|
||||
self._currCounts[3] = count
|
||||
return
|
||||
|
||||
def setCurrCounts(
|
||||
self, wNovel: Any = None, wNotes: Any = None, cNovel: Any = None, cNotes: Any = None
|
||||
@@ -335,7 +318,6 @@ class NWProjectData:
|
||||
self._currCounts[2] = checkInt(cNovel, 0)
|
||||
if cNotes is not None:
|
||||
self._currCounts[3] = checkInt(cNotes, 0)
|
||||
return
|
||||
|
||||
def setAutoReplace(self, value: dict) -> None:
|
||||
"""Set the auto-replace dictionary."""
|
||||
@@ -345,4 +327,3 @@ class NWProjectData:
|
||||
if isinstance(entry, str):
|
||||
self._autoReplace[key] = simplified(entry)
|
||||
self._project.setProjectChanged(True)
|
||||
return
|
||||
|
||||
@@ -22,7 +22,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -72,7 +72,7 @@ class XMLReadState(Enum):
|
||||
|
||||
|
||||
class ProjectXMLReader:
|
||||
"""Core: Project XML Reader
|
||||
"""Core: Project XML Reader.
|
||||
|
||||
All data is read into a NWProjectData instance, which must be
|
||||
provided.
|
||||
@@ -124,7 +124,6 @@ class ProjectXMLReader:
|
||||
self._appVersion = ""
|
||||
self._hexVersion = 0x0
|
||||
self._timeStamp = ""
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -254,8 +253,6 @@ class ProjectXMLReader:
|
||||
elif xItem.tag == "editTime": # Moved to attribute in 1.5
|
||||
data.setEditTime(xItem.text)
|
||||
|
||||
return
|
||||
|
||||
def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData) -> None:
|
||||
"""Parse the settings section of the XML file."""
|
||||
logger.debug("Parsing <settings> section")
|
||||
@@ -294,8 +291,6 @@ class ProjectXMLReader:
|
||||
elif xItem.tag == "notesWordCount": # Moved to content attribute in 1.5
|
||||
data.setInitCounts(wNotes=xItem.text)
|
||||
|
||||
return
|
||||
|
||||
def _parseProjectContent(
|
||||
self, xSection: ET.Element, data: NWProjectData, content: list
|
||||
) -> None:
|
||||
@@ -356,8 +351,6 @@ class ProjectXMLReader:
|
||||
"nameAttr": name,
|
||||
})
|
||||
|
||||
return
|
||||
|
||||
def _parseProjectContentLegacy(
|
||||
self, xSection: ET.Element, data: NWProjectData, content: list
|
||||
) -> None:
|
||||
@@ -434,8 +427,6 @@ class ProjectXMLReader:
|
||||
"nameAttr": name,
|
||||
})
|
||||
|
||||
return
|
||||
|
||||
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus) -> None:
|
||||
"""Parse a status or importance entry."""
|
||||
for xEntry in xItem:
|
||||
@@ -450,7 +441,6 @@ class ProjectXMLReader:
|
||||
if color is None:
|
||||
color = f"{red}, {green}, {blue}"
|
||||
sObject.add(key, xEntry.text or "", color, shape, count)
|
||||
return
|
||||
|
||||
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
|
||||
"""Parse a dictionary stored with key as an attribute and the
|
||||
@@ -470,7 +460,7 @@ class ProjectXMLReader:
|
||||
|
||||
|
||||
class ProjectXMLWriter:
|
||||
"""Core: Project XML Writer
|
||||
"""Core: Project XML Writer.
|
||||
|
||||
The project writer class will only write a file according to the
|
||||
very latest spec.
|
||||
@@ -479,7 +469,6 @@ class ProjectXMLWriter:
|
||||
def __init__(self, path: str | Path) -> None:
|
||||
self._path = Path(path)
|
||||
self._error = None
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -580,7 +569,6 @@ class ProjectXMLWriter:
|
||||
"""Pack a single value into an XML element."""
|
||||
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
||||
xItem.text = str(value) or ""
|
||||
return
|
||||
|
||||
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict) -> None:
|
||||
"""Pack the entries of a dictionary into an XML element."""
|
||||
@@ -589,4 +577,3 @@ class ProjectXMLWriter:
|
||||
if len(key) > 0:
|
||||
xEntry = ET.SubElement(xItem, "entry", attrib={"key": key})
|
||||
xEntry.text = str(value) or ""
|
||||
return
|
||||
|
||||
@@ -20,7 +20,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
@@ -43,7 +43,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWSessionLog:
|
||||
"""Core: Session JSON Lines Log File
|
||||
"""Core: Session JSON Lines Log File.
|
||||
|
||||
The class that wraps the session log file, which is in JSON Lines
|
||||
format. That is, one JSON object per line.
|
||||
@@ -52,7 +52,6 @@ class NWSessionLog:
|
||||
def __init__(self, project: NWProject) -> None:
|
||||
self._project = project
|
||||
self._start = 0.0
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -70,7 +69,6 @@ class NWSessionLog:
|
||||
def startSession(self) -> None:
|
||||
"""Start the writing session."""
|
||||
self._start = time()
|
||||
return
|
||||
|
||||
def appendSession(self, idleTime: float) -> bool:
|
||||
"""Append session statistics to the sessions log file."""
|
||||
|
||||
@@ -21,7 +21,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
@@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWSpellEnchant:
|
||||
"""Core: Enchant Spell Checking Wrapper
|
||||
"""Core: Enchant Spell Checking Wrapper.
|
||||
|
||||
This is a rapper class for Enchant to keep the API consistent
|
||||
between spell check tools.
|
||||
@@ -57,11 +57,9 @@ class NWSpellEnchant:
|
||||
self._language = None
|
||||
self._broker = None
|
||||
logger.debug("Ready: NWSpellEnchant")
|
||||
return
|
||||
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: NWSpellEnchant")
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -106,21 +104,19 @@ class NWSpellEnchant:
|
||||
for word in self._userDict:
|
||||
self._enchant.add_to_session(word)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def checkWord(self, word: str) -> bool:
|
||||
"""Wrapper function for pyenchant."""
|
||||
"""Forward check to pyenchant."""
|
||||
try:
|
||||
return bool(self._enchant.check(word))
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
def suggestWords(self, word: str) -> list[str]:
|
||||
"""Wrapper function for pyenchant."""
|
||||
"""Ask pyenchant for suggestions."""
|
||||
try:
|
||||
return self._enchant.suggest(word)
|
||||
except Exception:
|
||||
@@ -172,24 +168,29 @@ class FakeEnchant:
|
||||
self.tag = ""
|
||||
self.provider = FakeProvider()
|
||||
|
||||
return
|
||||
|
||||
def check(self, word: str) -> bool:
|
||||
"""Return True for all words."""
|
||||
return True
|
||||
|
||||
def suggest(self, word: str) -> list[str]:
|
||||
"""Return an empty suggestion list."""
|
||||
return []
|
||||
|
||||
def add_to_session(self, word: str) -> None:
|
||||
"""Do nothing."""
|
||||
return
|
||||
|
||||
|
||||
class UserDictionary:
|
||||
"""Core: User Word Dictionary.
|
||||
|
||||
This class holds all the user's own words for spell checking
|
||||
purposes. The dictionary is per-project.
|
||||
"""
|
||||
|
||||
def __init__(self, project: NWProject) -> None:
|
||||
self._project = project
|
||||
self._words = set()
|
||||
return
|
||||
|
||||
def __contains__(self, word: str) -> bool:
|
||||
return word in self._words
|
||||
@@ -219,7 +220,6 @@ class UserDictionary:
|
||||
except Exception:
|
||||
logger.error("Failed to load user dictionary")
|
||||
logException()
|
||||
return
|
||||
|
||||
def save(self) -> None:
|
||||
"""Save the user's dictionary."""
|
||||
@@ -232,4 +232,3 @@ class UserDictionary:
|
||||
except Exception:
|
||||
logger.error("Failed to save user dictionary")
|
||||
logException()
|
||||
return
|
||||
|
||||
@@ -21,7 +21,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
@@ -49,6 +49,7 @@ CUSTOM_COL = "custom"
|
||||
|
||||
@dataclasses.dataclass
|
||||
class StatusEntry:
|
||||
"""DataClass: Status Label Values."""
|
||||
|
||||
name: str
|
||||
color: QColor
|
||||
@@ -73,6 +74,7 @@ T_StatusKind = Literal["s", "i"]
|
||||
|
||||
|
||||
class NWStatus:
|
||||
"""Core: Status/Importance Label Class."""
|
||||
|
||||
STATUS = "s"
|
||||
IMPORT = "i"
|
||||
@@ -84,7 +86,6 @@ class NWStatus:
|
||||
self._default = None
|
||||
self._prefix = prefix[:1]
|
||||
self._height = SHARED.theme.baseIconHeight
|
||||
return
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._store)
|
||||
@@ -133,8 +134,6 @@ class NWStatus:
|
||||
if self._default not in self._store:
|
||||
self._default = next(iter(self._store)) if self._store else None
|
||||
|
||||
return
|
||||
|
||||
def check(self, value: str) -> str:
|
||||
"""Check the key against the stored status names."""
|
||||
if self._isKey(value) and value in self._store:
|
||||
@@ -147,13 +146,11 @@ class NWStatus:
|
||||
"""Clear the counts of references to the status entries."""
|
||||
for entry in self._store.values():
|
||||
entry.count = 0
|
||||
return
|
||||
|
||||
def increment(self, key: str | None) -> None:
|
||||
"""Increment the counter for a given entry."""
|
||||
if key and key in self._store:
|
||||
self._store[key].count += 1
|
||||
return
|
||||
|
||||
def pack(self) -> Iterable[tuple[str, dict]]:
|
||||
"""Pack the status entries into a dictionary."""
|
||||
@@ -195,7 +192,6 @@ class NWStatus:
|
||||
if entry.theme != CUSTOM_COL:
|
||||
entry.color = SHARED.theme.parseColor(entry.theme)
|
||||
entry.icon = NWStatus.createIcon(self._height, entry.color, entry.shape)
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
|
||||
@@ -252,7 +248,6 @@ class _ShapeCache:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._cache: dict[nwStatusShape, QPainterPath] = {}
|
||||
return
|
||||
|
||||
def getShape(self, shape: nwStatusShape) -> QPainterPath:
|
||||
"""Return a painter shape for an icon."""
|
||||
|
||||
@@ -20,7 +20,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
@@ -47,6 +47,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWStorageOpen(Enum):
|
||||
"""The status of a storage location."""
|
||||
|
||||
UNKOWN = 0
|
||||
NOT_FOUND = 1
|
||||
@@ -56,6 +57,7 @@ class NWStorageOpen(Enum):
|
||||
|
||||
|
||||
class NWStorageCreate(Enum):
|
||||
"""The status of a new storage location."""
|
||||
|
||||
NOT_EMPTY = 0
|
||||
OS_ERROR = 1
|
||||
@@ -63,7 +65,7 @@ class NWStorageCreate(Enum):
|
||||
|
||||
|
||||
class NWStorage:
|
||||
"""Core: Project Storage Class
|
||||
"""Core: Project Storage Class.
|
||||
|
||||
The class that handles all paths related to the project storage.
|
||||
"""
|
||||
@@ -81,7 +83,6 @@ class NWStorage:
|
||||
self._openMode = self.MODE_INACTIVE
|
||||
self._ready = False
|
||||
self._exception = None
|
||||
return
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Reset internal variables."""
|
||||
@@ -90,7 +91,6 @@ class NWStorage:
|
||||
self._lockFilePath = None
|
||||
self._openMode = self.MODE_INACTIVE
|
||||
self._ready = False
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
@@ -252,13 +252,11 @@ class NWStorage:
|
||||
"""Lock the session when the project is successfully opened."""
|
||||
if self._ready:
|
||||
self._writeLockFile()
|
||||
return
|
||||
|
||||
def closeSession(self) -> None:
|
||||
"""Run tasks related to closing the session."""
|
||||
self._clearLockFile()
|
||||
self.clear()
|
||||
return
|
||||
|
||||
##
|
||||
# Content Access Methods
|
||||
@@ -394,7 +392,7 @@ class NWStorage:
|
||||
|
||||
|
||||
class _LegacyStorage:
|
||||
"""Core: Legacy Storage Converter Utils
|
||||
"""Core: Legacy Storage Converter Utils.
|
||||
|
||||
A class with various functions to convert old file formats and
|
||||
file/folder layouts to the current project format.
|
||||
@@ -402,7 +400,6 @@ class _LegacyStorage:
|
||||
|
||||
def __init__(self, project: NWProject) -> None:
|
||||
self._project = project
|
||||
return
|
||||
|
||||
def legacyDataFolder(self, path: Path, child: Path) -> None:
|
||||
"""Handle the content of a legacy data folder from a version 1.0
|
||||
@@ -484,8 +481,6 @@ class _LegacyStorage:
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to delete: %s", item, exc_info=exc)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
@@ -21,7 +21,7 @@ General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
""" # noqa
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -50,7 +50,7 @@ MAX_DEPTH = 999 # Cap of tree traversing for loops (recursion limit)
|
||||
|
||||
|
||||
class NWTree:
|
||||
"""Core: Project Tree Data Class
|
||||
"""Core: Project Tree Data Class.
|
||||
|
||||
Only one instance of this class should exist in the project class.
|
||||
This class holds all the project items of the project as instances
|
||||
@@ -71,18 +71,16 @@ class NWTree:
|
||||
self._trash = None
|
||||
self._ready = False
|
||||
logger.debug("Ready: NWTree")
|
||||
return
|
||||
|
||||
def __del__(self) -> None: # pragma: no cover
|
||||
logger.debug("Delete: NWTree")
|
||||
return
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""The number of items in the project."""
|
||||
"""Return the number of items in the project."""
|
||||
return len(self._items)
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
"""True if there are any items in the project."""
|
||||
"""Return True if there are any items in the project."""
|
||||
return bool(self._items)
|
||||
|
||||
def __getitem__(self, tHandle: str | None) -> NWItem | None:
|
||||
@@ -95,7 +93,7 @@ class NWTree:
|
||||
return None
|
||||
|
||||
def __contains__(self, tHandle: str) -> bool:
|
||||
"""Checks if a handle exists in the tree."""
|
||||
"""Check if a handle exists in the tree."""
|
||||
return tHandle in self._items
|
||||
|
||||
def __iter__(self) -> Iterator[NWItem]:
|
||||
@@ -142,7 +140,6 @@ class NWTree:
|
||||
self._trash = None
|
||||
oldModel.deleteLater()
|
||||
del oldModel
|
||||
return
|
||||
|
||||
def add(self, item: NWItem, pos: int = -1) -> bool:
|
||||
"""Add a project item into the project tree."""
|
||||
@@ -260,8 +257,6 @@ class NWTree:
|
||||
self._model.endInsertRows()
|
||||
self._model.layoutChanged.emit()
|
||||
|
||||
return
|
||||
|
||||
def pickParent(self, sNode: ProjectNode, hLevel: int, isNote: bool) -> tuple[str | None, int]:
|
||||
"""Pick an appropriate parent handle for adding a new item."""
|
||||
if sNode.item.isFolderType() or sNode.item.isRootType():
|
||||
@@ -299,7 +294,6 @@ class NWTree:
|
||||
indexE = self._model.indexFromNode(node, 3)
|
||||
self._model.dataChanged.emit(indexS, indexE)
|
||||
self._itemChange(node.item, nwChange.UPDATE)
|
||||
return
|
||||
|
||||
def refreshAllItems(self) -> None:
|
||||
"""Refresh all items in the tree."""
|
||||
@@ -309,13 +303,11 @@ class NWTree:
|
||||
self._model.root.refresh()
|
||||
self._model.root.updateCount(propagate=False)
|
||||
self._model.layoutChanged.emit()
|
||||
return
|
||||
|
||||
def novelStructureChanged(self, tHandle: str) -> None:
|
||||
"""Emit a novel structure change signal."""
|
||||
if self._ready:
|
||||
SHARED.novelStructureChanged.emit(tHandle)
|
||||
return
|
||||
|
||||
def checkConsistency(self, prefix: str) -> tuple[int, int]:
|
||||
"""Check the project tree consistency. Also check the content
|
||||
@@ -496,7 +488,6 @@ class NWTree:
|
||||
SHARED.emitProjectItemChanged(self._project, tHandle, change)
|
||||
if item.isRootType():
|
||||
SHARED.emitRootFolderChanged(self._project, tHandle, change)
|
||||
return
|
||||
|
||||
def _getTrashNode(self) -> ProjectNode | None:
|
||||
"""Get the trash node. If it doesn't exist, create it."""
|
||||
|
||||
Reference in New Issue
Block a user