Add shape setting to status and importance icons (#1810)
This commit is contained in:
@@ -25,7 +25,7 @@ from __future__ import annotations
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
|
||||
|
||||
from novelwriter.enum import nwBuildFmt, nwItemClass, nwItemLayout, nwOutline
|
||||
from novelwriter.enum import nwBuildFmt, nwItemClass, nwItemLayout, nwOutline, nwStatusShape
|
||||
|
||||
|
||||
def trConst(text: str) -> str:
|
||||
@@ -268,6 +268,34 @@ class nwLabels:
|
||||
nwBuildFmt.J_HTML: ".json",
|
||||
nwBuildFmt.J_NWD: ".json",
|
||||
}
|
||||
SHAPES_PLAIN = {
|
||||
nwStatusShape.SQUARE: QT_TRANSLATE_NOOP("Constant", "Square"),
|
||||
nwStatusShape.TRIANGLE: QT_TRANSLATE_NOOP("Constant", "Triangle"),
|
||||
nwStatusShape.NABLA: QT_TRANSLATE_NOOP("Constant", "Nabla"),
|
||||
nwStatusShape.DIAMOND: QT_TRANSLATE_NOOP("Constant", "Diamond"),
|
||||
nwStatusShape.PENTAGON: QT_TRANSLATE_NOOP("Constant", "Pentagon"),
|
||||
nwStatusShape.HEXAGON: QT_TRANSLATE_NOOP("Constant", "Hexagon"),
|
||||
nwStatusShape.STAR: QT_TRANSLATE_NOOP("Constant", "Star"),
|
||||
nwStatusShape.PACMAN: QT_TRANSLATE_NOOP("Constant", "Pacman"),
|
||||
}
|
||||
SHAPES_CIRCLE = {
|
||||
nwStatusShape.CIRCLE_Q: QT_TRANSLATE_NOOP("Constant", "1/4 Circle"),
|
||||
nwStatusShape.CIRCLE_H: QT_TRANSLATE_NOOP("Constant", "Half Circle"),
|
||||
nwStatusShape.CIRCLE_T: QT_TRANSLATE_NOOP("Constant", "3/4 Circle"),
|
||||
nwStatusShape.CIRCLE: QT_TRANSLATE_NOOP("Constant", "Full Circle"),
|
||||
}
|
||||
SHAPES_BARS = {
|
||||
nwStatusShape.BARS_1: QT_TRANSLATE_NOOP("Constant", "1 Bar"),
|
||||
nwStatusShape.BARS_2: QT_TRANSLATE_NOOP("Constant", "2 Bars"),
|
||||
nwStatusShape.BARS_3: QT_TRANSLATE_NOOP("Constant", "3 Bars"),
|
||||
nwStatusShape.BARS_4: QT_TRANSLATE_NOOP("Constant", "4 Bars"),
|
||||
}
|
||||
SHAPES_BLOCKS = {
|
||||
nwStatusShape.BLOCK_1: QT_TRANSLATE_NOOP("Constant", "1 Block"),
|
||||
nwStatusShape.BLOCK_2: QT_TRANSLATE_NOOP("Constant", "2 Blocks"),
|
||||
nwStatusShape.BLOCK_3: QT_TRANSLATE_NOOP("Constant", "3 Blocks"),
|
||||
nwStatusShape.BLOCK_4: QT_TRANSLATE_NOOP("Constant", "4 Blocks"),
|
||||
}
|
||||
FILE_FILTERS = {
|
||||
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
|
||||
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
|
||||
|
||||
@@ -104,7 +104,7 @@ class DocMerger:
|
||||
docText = self._project.storage.getDocumentText(srcHandle).rstrip("\n")
|
||||
if addComment:
|
||||
docInfo = srcItem.describeMe()
|
||||
docSt, _ = srcItem.getImportStatus(incIcon=False)
|
||||
docSt, _ = srcItem.getImportStatus()
|
||||
cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n"
|
||||
docText = cmtLine + docText
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Literal, overload
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from PyQt5.QtGui import QIcon
|
||||
|
||||
@@ -308,25 +308,15 @@ class NWItem:
|
||||
|
||||
return trConst(nwLabels.ITEM_DESCRIPTION.get(descKey, ""))
|
||||
|
||||
@overload # pragma: no cover
|
||||
def getImportStatus(self, incIcon: Literal[True] = True) -> tuple[str, QIcon]:
|
||||
pass
|
||||
|
||||
@overload # pragma: no cover
|
||||
def getImportStatus(self, incIcon: Literal[False]) -> tuple[str, None]:
|
||||
pass
|
||||
|
||||
def getImportStatus(self, incIcon=True):
|
||||
def getImportStatus(self) -> tuple[str, QIcon]:
|
||||
"""Return the relevant importance or status label and icon for
|
||||
the current item based on its class.
|
||||
"""
|
||||
if self.isNovelLike():
|
||||
stName = self._project.data.itemStatus.name(self._status)
|
||||
stIcon = self._project.data.itemStatus.icon(self._status) if incIcon else None
|
||||
entry = self._project.data.itemStatus[self._status]
|
||||
else:
|
||||
stName = self._project.data.itemImport.name(self._import)
|
||||
stIcon = self._project.data.itemImport.icon(self._import) if incIcon else None
|
||||
return stName, stIcon
|
||||
entry = self._project.data.itemImport[self._import]
|
||||
return entry.name, entry.icon
|
||||
|
||||
##
|
||||
# Checker Methods
|
||||
|
||||
+21
-52
@@ -26,33 +26,32 @@ from __future__ import annotations
|
||||
import json
|
||||
import logging
|
||||
|
||||
from collections.abc import Iterable
|
||||
from enum import Enum
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
from functools import partial
|
||||
from collections.abc import Iterable
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from novelwriter import CONFIG, SHARED, __version__, __hexversion__
|
||||
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.constants import trConst, nwLabels
|
||||
from novelwriter.core.tree import NWTree
|
||||
from novelwriter.core.index import NWIndex
|
||||
from novelwriter.core.options import OptionState
|
||||
from novelwriter.core.storage import NWStorage, NWStorageOpen
|
||||
from novelwriter.core.sessions import NWSessionLog
|
||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
||||
from novelwriter.core.projectdata import NWProjectData
|
||||
from novelwriter.common import (
|
||||
checkStringNone, formatInt, formatTimeStamp, getFileSize, hexToInt, makeFileNameSafe, minmax
|
||||
)
|
||||
from novelwriter.constants import trConst, nwLabels
|
||||
from novelwriter.core.index import NWIndex
|
||||
from novelwriter.core.options import OptionState
|
||||
from novelwriter.core.projectdata import NWProjectData
|
||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
||||
from novelwriter.core.sessions import NWSessionLog
|
||||
from novelwriter.core.storage import NWStorage, NWStorageOpen
|
||||
from novelwriter.core.tree import NWTree
|
||||
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.core.item import NWItem
|
||||
from novelwriter.core.status import NWStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -461,14 +460,14 @@ class NWProject:
|
||||
|
||||
def setDefaultStatusImport(self) -> None:
|
||||
"""Set the default status and importance values."""
|
||||
self._data.itemStatus.write(None, self.tr("New"), (100, 100, 100))
|
||||
self._data.itemStatus.write(None, self.tr("Note"), (200, 50, 0))
|
||||
self._data.itemStatus.write(None, self.tr("Draft"), (200, 150, 0))
|
||||
self._data.itemStatus.write(None, self.tr("Finished"), (50, 200, 0))
|
||||
self._data.itemImport.write(None, self.tr("New"), (100, 100, 100))
|
||||
self._data.itemImport.write(None, self.tr("Minor"), (200, 50, 0))
|
||||
self._data.itemImport.write(None, self.tr("Major"), (200, 150, 0))
|
||||
self._data.itemImport.write(None, self.tr("Main"), (50, 200, 0))
|
||||
self._data.itemStatus.add(None, self.tr("New"), (100, 100, 100), "SQUARE", 0)
|
||||
self._data.itemStatus.add(None, self.tr("Note"), (200, 50, 0), "SQUARE", 0)
|
||||
self._data.itemStatus.add(None, self.tr("Draft"), (200, 150, 0), "SQUARE", 0)
|
||||
self._data.itemStatus.add(None, self.tr("Finished"), (50, 200, 0), "SQUARE", 0)
|
||||
self._data.itemImport.add(None, self.tr("New"), (100, 100, 100), "SQUARE", 0)
|
||||
self._data.itemImport.add(None, self.tr("Minor"), (200, 50, 0), "SQUARE", 0)
|
||||
self._data.itemImport.add(None, self.tr("Major"), (200, 150, 0), "SQUARE", 0)
|
||||
self._data.itemImport.add(None, self.tr("Main"), (50, 200, 0), "SQUARE", 0)
|
||||
return
|
||||
|
||||
def setProjectLang(self, language: str | None) -> None:
|
||||
@@ -491,14 +490,6 @@ class NWProject:
|
||||
self.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setStatusColours(self, new: list[dict], deleted: list[str]) -> bool:
|
||||
"""Update the list of novel file status flags."""
|
||||
return self._setStatusImport(new, deleted, self._data.itemStatus)
|
||||
|
||||
def setImportColours(self, new: list[dict], deleted: list[str]) -> bool:
|
||||
"""Update the list of note file importance flags."""
|
||||
return self._setStatusImport(new, deleted, self._data.itemImport)
|
||||
|
||||
def setProjectChanged(self, status: bool) -> bool:
|
||||
"""Toggle the project changed flag, and propagate the
|
||||
information to the GUI statusbar.
|
||||
@@ -584,28 +575,6 @@ class NWProject:
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _setStatusImport(self, new: list[dict], delete: list[str], target: NWStatus) -> bool:
|
||||
"""Update the list of novel file status or importance flags, and
|
||||
delete those that have been requested deleted.
|
||||
"""
|
||||
if not (new or delete):
|
||||
return False
|
||||
|
||||
order = []
|
||||
for entry in new:
|
||||
key = entry.get("key", None)
|
||||
name = entry.get("name", "")
|
||||
cols = entry.get("cols", (100, 100, 100))
|
||||
if name:
|
||||
order.append(target.write(key, name, cols))
|
||||
|
||||
for key in delete:
|
||||
target.remove(key)
|
||||
|
||||
target.reorder(order)
|
||||
|
||||
return True
|
||||
|
||||
def _loadProjectLocalisation(self) -> bool:
|
||||
"""Load the language data for the current project language."""
|
||||
if self._data.language is None or CONFIG._nwLangPath is None:
|
||||
|
||||
@@ -46,7 +46,7 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
FILE_VERSION = "1.5" # The current project file format version
|
||||
FILE_REVISION = "3" # The current project file format revision
|
||||
FILE_REVISION = "4" # The current project file format revision
|
||||
HEX_VERSION = 0x0105
|
||||
|
||||
NUM_VERSION = {
|
||||
@@ -109,6 +109,8 @@ class ProjectXMLReader:
|
||||
Rev 2: Drops the title node from project and adds the TEMPLATE
|
||||
class for items. 2.3 Beta 1.
|
||||
Rev 3: Added TEMPLATE class. 2.3.
|
||||
Rev 4: Added shape attribute to status and importance entry
|
||||
nodes. 2.5.
|
||||
"""
|
||||
|
||||
def __init__(self, path: str | Path) -> None:
|
||||
@@ -356,8 +358,8 @@ class ProjectXMLReader:
|
||||
logger.debug("Parsing <content> section (legacy format)")
|
||||
|
||||
# Create maps to look up name -> key for status and importance
|
||||
statusMap = {entry.get("name"): key for key, entry in data.itemStatus.items()}
|
||||
importMap = {entry.get("name"): key for key, entry in data.itemImport.items()}
|
||||
sMap: dict[str | None, str] = {e.name: k for k, e in data.itemStatus.iterItems()}
|
||||
iMap: dict[str | None, str] = {e.name: k for k, e in data.itemImport.iterItems()}
|
||||
|
||||
for xItem in xSection:
|
||||
if xItem.tag != "item":
|
||||
@@ -404,9 +406,9 @@ class ProjectXMLReader:
|
||||
|
||||
# Status was split into separate status/import with a key in 1.4
|
||||
if item.get("class", "") in ("NOVEL", "ARCHIVE"):
|
||||
name["status"] = statusMap.get(tmpStatus, None)
|
||||
name["status"] = sMap.get(tmpStatus, None)
|
||||
else:
|
||||
name["import"] = importMap.get(tmpStatus, None)
|
||||
name["import"] = iMap.get(tmpStatus, None)
|
||||
|
||||
# A number of layouts were removed in 1.3
|
||||
if item.get("layout", "") in (
|
||||
@@ -436,7 +438,8 @@ class ProjectXMLReader:
|
||||
green = checkInt(xEntry.attrib.get("green", 0), 0)
|
||||
blue = checkInt(xEntry.attrib.get("blue", 0), 0)
|
||||
count = checkInt(xEntry.attrib.get("count", 0), 0)
|
||||
sObject.write(key, xEntry.text or "", (red, green, blue), count)
|
||||
shape = xEntry.attrib.get("shape", "")
|
||||
sObject.add(key, xEntry.text or "", (red, green, blue), shape, count)
|
||||
return
|
||||
|
||||
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
|
||||
|
||||
+222
-169
@@ -24,17 +24,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
import dataclasses
|
||||
import logging
|
||||
import random
|
||||
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
from collections.abc import ItemsView, Iterable, Iterator, KeysView, ValuesView
|
||||
from collections.abc import Iterable
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
|
||||
from PyQt5.QtCore import QRectF
|
||||
from PyQt5.QtCore import QPointF, Qt
|
||||
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor, QPolygonF
|
||||
|
||||
from novelwriter import CONFIG
|
||||
from novelwriter.common import minmax, simplified
|
||||
from novelwriter import SHARED
|
||||
from novelwriter.common import simplified
|
||||
from novelwriter.enum import nwStatusShape
|
||||
from novelwriter.types import QtPaintAnitAlias, QtTransparent
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
@@ -43,83 +45,94 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class StatusEntry:
|
||||
|
||||
name: str
|
||||
color: QColor
|
||||
shape: nwStatusShape
|
||||
icon: QIcon
|
||||
count: int = 0
|
||||
|
||||
@classmethod
|
||||
def duplicate(cls, source: StatusEntry) -> StatusEntry:
|
||||
"""Create a deep copy of the source object."""
|
||||
cls = dataclasses.replace(source)
|
||||
cls.color = QColor(source.color)
|
||||
cls.icon = QIcon(source.icon)
|
||||
return cls
|
||||
|
||||
# END Class StatusEntry
|
||||
|
||||
|
||||
NO_ENTRY = StatusEntry("", QColor(0, 0, 0), nwStatusShape.SQUARE, QIcon(), 0)
|
||||
|
||||
|
||||
class NWStatus:
|
||||
|
||||
STATUS = 1
|
||||
IMPORT = 2
|
||||
STATUS = "s"
|
||||
IMPORT = "i"
|
||||
|
||||
def __init__(self, kind: Literal[1, 2]) -> None:
|
||||
__slots__ = ("_store", "_default", "_prefix", "_height")
|
||||
|
||||
self._type = kind
|
||||
self._store = {}
|
||||
def __init__(self, prefix: str) -> None:
|
||||
self._store: dict[str, StatusEntry] = {}
|
||||
self._default = None
|
||||
|
||||
self._iPX = CONFIG.pxInt(24)
|
||||
|
||||
pA = CONFIG.pxInt(2)
|
||||
pB = CONFIG.pxInt(20)
|
||||
pR = float(CONFIG.pxInt(4))
|
||||
self._iconPath = QPainterPath()
|
||||
self._iconPath.addRoundedRect(QRectF(pA, pA, pB, pB), pR, pR)
|
||||
|
||||
self._defaultIcon = self._createIcon(100, 100, 100)
|
||||
|
||||
if self._type == self.STATUS:
|
||||
self._prefix = "s"
|
||||
elif self._type == self.IMPORT:
|
||||
self._prefix = "i"
|
||||
else:
|
||||
raise Exception("This is a bug!")
|
||||
|
||||
self._prefix = prefix[:1]
|
||||
self._height = SHARED.theme.baseIconHeight
|
||||
return
|
||||
|
||||
def write(self, key: str | None, name: str, col: tuple, count: int | None = None) -> str:
|
||||
def __len__(self) -> int:
|
||||
return len(self._store)
|
||||
|
||||
def __getitem__(self, key: str | None) -> StatusEntry:
|
||||
"""Return the entry associated with a given key."""
|
||||
if key and key in self._store:
|
||||
return self._store[key]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]
|
||||
return NO_ENTRY
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def add(self, key: str | None, name: str, color: tuple[int, int, int],
|
||||
shape: str, count: int) -> str:
|
||||
"""Add or update a status entry. If the key is invalid, a new
|
||||
key is generated.
|
||||
"""
|
||||
if not self._isKey(key):
|
||||
key = self._newKey()
|
||||
if not isinstance(col, tuple):
|
||||
col = (100, 100, 100)
|
||||
if len(col) != 3:
|
||||
col = (100, 100, 100)
|
||||
if isinstance(color, tuple) and len(color) == 3:
|
||||
qColor = QColor(*color)
|
||||
else:
|
||||
qColor = QColor(100, 100, 100)
|
||||
|
||||
cR = minmax(col[0], 0, 255)
|
||||
cG = minmax(col[1], 0, 255)
|
||||
cB = minmax(col[2], 0, 255)
|
||||
try:
|
||||
iShape = nwStatusShape[shape]
|
||||
except KeyError:
|
||||
iShape = nwStatusShape.SQUARE
|
||||
|
||||
key = self._checkKey(key)
|
||||
name = simplified(name)
|
||||
if count is None:
|
||||
count = self._store.get(key, {}).get("count", 0)
|
||||
|
||||
self._store[key] = {
|
||||
"name": name,
|
||||
"icon": self._createIcon(cR, cG, cB),
|
||||
"cols": (cR, cG, cB),
|
||||
"count": count,
|
||||
}
|
||||
icon = self.createIcon(self._height, qColor, iShape)
|
||||
self._store[key] = StatusEntry(name, qColor, iShape, icon, count)
|
||||
|
||||
if self._default is None:
|
||||
self._default = key
|
||||
|
||||
return key
|
||||
|
||||
def remove(self, key: str) -> bool:
|
||||
"""Remove an entry in the list, except if the count > 0."""
|
||||
if key not in self._store:
|
||||
return False
|
||||
if self._store[key]["count"] > 0:
|
||||
return False
|
||||
def update(self, update: list[tuple[str | None, StatusEntry]]) -> None:
|
||||
"""Update the list of statuses, and from removed list."""
|
||||
self._store.clear()
|
||||
for key, entry in update:
|
||||
self._store[self._checkKey(key)] = entry
|
||||
|
||||
del self._store[key]
|
||||
# Check if we need a new default
|
||||
if self._default not in self._store:
|
||||
self._default = next(iter(self._store)) if self._store else None
|
||||
|
||||
keys = list(self._store.keys())
|
||||
if key == self._default:
|
||||
if len(keys) > 0:
|
||||
self._default = keys[0]
|
||||
else:
|
||||
self._default = None
|
||||
|
||||
return True
|
||||
return
|
||||
|
||||
def check(self, value: str) -> str:
|
||||
"""Check the key against the stored status names."""
|
||||
@@ -129,93 +142,51 @@ class NWStatus:
|
||||
return self._default
|
||||
return ""
|
||||
|
||||
def name(self, key: str | None) -> str:
|
||||
"""Return the name associated with a given key."""
|
||||
if key and key in self._store:
|
||||
return self._store[key]["name"]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]["name"]
|
||||
return ""
|
||||
|
||||
def cols(self, key: str | None) -> tuple[int, int, int]:
|
||||
"""Return the colours associated with a given key."""
|
||||
if key and key in self._store:
|
||||
return self._store[key]["cols"]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]["cols"]
|
||||
return 100, 100, 100
|
||||
|
||||
def count(self, key: str | None) -> int:
|
||||
"""Return the count associated with a given key."""
|
||||
if key and key in self._store:
|
||||
return self._store[key]["count"]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]["count"]
|
||||
return 0
|
||||
|
||||
def icon(self, key: str | None) -> QIcon:
|
||||
"""Return the icon associated with a given key."""
|
||||
if key and key in self._store:
|
||||
return self._store[key]["icon"]
|
||||
elif self._default is not None:
|
||||
return self._store[self._default]["icon"]
|
||||
return self._defaultIcon
|
||||
|
||||
def reorder(self, order: list[str]) -> bool:
|
||||
"""Reorder the items according to list."""
|
||||
if len(order) != len(self._store):
|
||||
logger.error("Length mismatch between new and old order")
|
||||
return False
|
||||
|
||||
if order == list(self._store.keys()):
|
||||
return False
|
||||
|
||||
store = {}
|
||||
for key in order:
|
||||
if key in self._store:
|
||||
store[key] = self._store[key]
|
||||
else:
|
||||
logger.error("Unknown key '%s' in order", key)
|
||||
return False
|
||||
|
||||
self._store = store
|
||||
|
||||
return True
|
||||
|
||||
def resetCounts(self) -> None:
|
||||
"""Clear the counts of references to the status entries."""
|
||||
for key in self._store:
|
||||
self._store[key]["count"] = 0
|
||||
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
|
||||
self._store[key].count += 1
|
||||
return
|
||||
|
||||
def pack(self) -> Iterable[tuple[str, dict]]:
|
||||
"""Pack the status entries into a dictionary."""
|
||||
for key, data in self._store.items():
|
||||
yield (data["name"], {
|
||||
for key, entry in self._store.items():
|
||||
yield (entry.name, {
|
||||
"key": key,
|
||||
"count": str(data["count"]),
|
||||
"red": str(data["cols"][0]),
|
||||
"green": str(data["cols"][1]),
|
||||
"blue": str(data["cols"][2]),
|
||||
"count": str(entry.count),
|
||||
"red": str(entry.color.red()),
|
||||
"green": str(entry.color.green()),
|
||||
"blue": str(entry.color.blue()),
|
||||
"shape": entry.shape.name,
|
||||
})
|
||||
return
|
||||
|
||||
def unpack(self, data: dict) -> None:
|
||||
"""Unpack a data dictionary and set the class values."""
|
||||
self._store = {}
|
||||
self._default = None
|
||||
for key, entry in data.items():
|
||||
label = entry.get("label", "")
|
||||
colour = entry.get("colour", (100, 100, 100))
|
||||
count = entry.get("count", 0)
|
||||
self.write(key, label, colour, count)
|
||||
return
|
||||
def iterItems(self) -> Iterable[tuple[str, StatusEntry]]:
|
||||
"""Yield entries from the status icons."""
|
||||
yield from self._store.items()
|
||||
|
||||
@staticmethod
|
||||
def createIcon(height: int, color: QColor, shape: nwStatusShape) -> QIcon:
|
||||
"""Generate an icon for a status label."""
|
||||
pixmap = QPixmap(48, 48)
|
||||
pixmap.fill(QtTransparent)
|
||||
|
||||
painter = QPainter(pixmap)
|
||||
painter.setRenderHint(QtPaintAnitAlias)
|
||||
painter.fillPath(_SHAPES.getShape(shape), color)
|
||||
painter.end()
|
||||
|
||||
return QIcon(pixmap.scaled(
|
||||
height, height,
|
||||
Qt.AspectRatioMode.IgnoreAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation
|
||||
))
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
@@ -246,38 +217,120 @@ class NWStatus:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _createIcon(self, red: int, green: int, blue: int) -> QIcon:
|
||||
"""Generate an icon for a status label."""
|
||||
pixmap = QPixmap(self._iPX, self._iPX)
|
||||
pixmap.fill(QtTransparent)
|
||||
|
||||
painter = QPainter(pixmap)
|
||||
painter.setRenderHint(QtPaintAnitAlias)
|
||||
painter.fillPath(self._iconPath, QColor(red, green, blue))
|
||||
painter.end()
|
||||
|
||||
return QIcon(pixmap)
|
||||
|
||||
##
|
||||
# Iterator Bits
|
||||
##
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._store)
|
||||
|
||||
def __getitem__(self, key: str) -> dict:
|
||||
return self._store[key]
|
||||
|
||||
def __iter__(self) -> Iterator[dict]:
|
||||
return iter(self._store)
|
||||
|
||||
def keys(self) -> KeysView[str]:
|
||||
return self._store.keys()
|
||||
|
||||
def items(self) -> ItemsView[str, dict]:
|
||||
return self._store.items()
|
||||
|
||||
def values(self) -> ValuesView[dict]:
|
||||
return self._store.values()
|
||||
def _checkKey(self, key: str | None) -> str:
|
||||
"""Check key is valid, and if not, generate one."""
|
||||
return key if self._isKey(key) else self._newKey()
|
||||
|
||||
# END Class NWStatus
|
||||
|
||||
|
||||
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."""
|
||||
if shape in self._cache:
|
||||
return self._cache[shape]
|
||||
|
||||
path = QPainterPath()
|
||||
if shape == nwStatusShape.SQUARE:
|
||||
path.addRoundedRect(2.0, 2.0, 44.0, 44.0, 4.0, 4.0)
|
||||
elif shape == nwStatusShape.TRIANGLE:
|
||||
path.addPolygon(QPolygonF([
|
||||
QPointF(24.00, 3.00),
|
||||
QPointF(43.92, 37.50),
|
||||
QPointF(4.08, 37.50),
|
||||
]))
|
||||
elif shape == nwStatusShape.NABLA:
|
||||
path.addPolygon(QPolygonF([
|
||||
QPointF(24.00, 48.00),
|
||||
QPointF(4.08, 14.50),
|
||||
QPointF(43.92, 14.50),
|
||||
]))
|
||||
elif shape == nwStatusShape.DIAMOND:
|
||||
path.addPolygon(QPolygonF([
|
||||
QPointF(24.00, 2.00),
|
||||
QPointF(44.00, 24.00),
|
||||
QPointF(24.00, 46.00),
|
||||
QPointF(4.00, 24.00),
|
||||
]))
|
||||
elif shape == nwStatusShape.PENTAGON:
|
||||
path.addPolygon(QPolygonF([
|
||||
QPointF(24.00, 1.50),
|
||||
QPointF(45.87, 17.39),
|
||||
QPointF(37.52, 43.11),
|
||||
QPointF(10.48, 43.11),
|
||||
QPointF(2.13, 17.39),
|
||||
]))
|
||||
elif shape == nwStatusShape.HEXAGON:
|
||||
path.addPolygon(QPolygonF([
|
||||
QPointF(24.00, 1.50),
|
||||
QPointF(43.92, 13.00),
|
||||
QPointF(43.92, 36.00),
|
||||
QPointF(24.00, 47.50),
|
||||
QPointF(4.08, 36.00),
|
||||
QPointF(4.08, 13.00),
|
||||
]))
|
||||
elif shape == nwStatusShape.STAR:
|
||||
path.addPolygon(QPolygonF([
|
||||
QPointF(24.00, 0.50), QPointF(31.05, 14.79),
|
||||
QPointF(46.83, 17.08), QPointF(35.41, 28.21),
|
||||
QPointF(38.11, 43.92), QPointF(24.00, 36.50),
|
||||
QPointF(9.89, 43.92), QPointF(12.59, 28.21),
|
||||
QPointF(1.17, 17.08), QPointF(15.37, 16.16),
|
||||
]))
|
||||
elif shape == nwStatusShape.PACMAN:
|
||||
path.moveTo(24.0, 24.0)
|
||||
path.arcTo(2.0, 2.0, 44.0, 44.0, 40.0, 280.0)
|
||||
elif shape == nwStatusShape.CIRCLE_Q:
|
||||
path.moveTo(24.0, 24.0)
|
||||
path.arcTo(2.0, 2.0, 44.0, 44.0, 0.0, 90.0)
|
||||
elif shape == nwStatusShape.CIRCLE_H:
|
||||
path.moveTo(24.0, 24.0)
|
||||
path.arcTo(2.0, 2.0, 44.0, 44.0, -90.0, 180.0)
|
||||
elif shape == nwStatusShape.CIRCLE_T:
|
||||
path.moveTo(24.0, 24.0)
|
||||
path.arcTo(2.0, 2.0, 44.0, 44.0, -180.0, 270.0)
|
||||
elif shape == nwStatusShape.CIRCLE:
|
||||
path.addEllipse(2.0, 2.0, 44.0, 44.0)
|
||||
elif shape == nwStatusShape.BARS_1:
|
||||
path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
elif shape == nwStatusShape.BARS_2:
|
||||
path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
elif shape == nwStatusShape.BARS_3:
|
||||
path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
path.addRoundedRect(26.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
elif shape == nwStatusShape.BARS_4:
|
||||
path.addRoundedRect(2.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
path.addRoundedRect(14.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
path.addRoundedRect(26.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
path.addRoundedRect(38.0, 2.0, 8.0, 44.0, 4.0, 4.0)
|
||||
elif shape == nwStatusShape.BLOCK_1:
|
||||
path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
|
||||
elif shape == nwStatusShape.BLOCK_2:
|
||||
path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
|
||||
path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
|
||||
elif shape == nwStatusShape.BLOCK_3:
|
||||
path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
|
||||
path.addRoundedRect(2.0, 24.0, 20.0, 20.0, 4.0, 4.0)
|
||||
path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
|
||||
elif shape == nwStatusShape.BLOCK_4:
|
||||
path.addRoundedRect(2.0, 2.0, 20.0, 20.0, 4.0, 4.0)
|
||||
path.addRoundedRect(2.0, 24.0, 20.0, 20.0, 4.0, 4.0)
|
||||
path.addRoundedRect(24.0, 2.0, 20.0, 20.0, 4.0, 4.0)
|
||||
path.addRoundedRect(24.0, 24.0, 20.0, 20.0, 4.0, 4.0)
|
||||
|
||||
self._cache[shape] = path
|
||||
|
||||
return path
|
||||
|
||||
# END Class _ShapeCache
|
||||
|
||||
|
||||
# Create Singleton
|
||||
_SHAPES = _ShapeCache()
|
||||
|
||||
@@ -27,20 +27,26 @@ from __future__ import annotations
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtGui import QCloseEvent, QColor, QIcon, QPixmap
|
||||
from PyQt5.QtGui import QCloseEvent, QColor
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QColorDialog, QDialog, QDialogButtonBox, QHBoxLayout,
|
||||
QLineEdit, QPushButton, QStackedWidget, QTreeWidget, QTreeWidgetItem,
|
||||
QVBoxLayout, QWidget
|
||||
QAbstractItemView, QApplication, QColorDialog, QDialog, QDialogButtonBox, QHBoxLayout,
|
||||
QLineEdit, QMenu, QStackedWidget, QToolButton, QTreeWidget,
|
||||
QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import simplified
|
||||
from novelwriter.constants import nwLabels
|
||||
from novelwriter.core.status import NWStatus, StatusEntry
|
||||
from novelwriter.enum import nwStatusShape
|
||||
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm
|
||||
from novelwriter.extensions.modified import NComboBox, NIconToolButton
|
||||
from novelwriter.extensions.pagedsidebar import NPagedSideBar
|
||||
from novelwriter.extensions.switch import NSwitch
|
||||
from novelwriter.types import QtDialogCancel, QtDialogSave, QtUserRole
|
||||
from novelwriter.types import (
|
||||
QtDialogCancel, QtDialogSave, QtSizeMinimum, QtSizeMinimumExpanding,
|
||||
QtUserRole
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -179,19 +185,19 @@ class GuiProjectSettings(QDialog):
|
||||
|
||||
rebuildTrees = False
|
||||
|
||||
if self.statusPage.wasChanged:
|
||||
newList, delList = self.statusPage.getNewList()
|
||||
project.setStatusColours(newList, delList)
|
||||
if self.statusPage.changed:
|
||||
logger.debug("Updating status labels")
|
||||
project.data.itemStatus.update(self.statusPage.getNewList())
|
||||
rebuildTrees = True
|
||||
|
||||
if self.importPage.wasChanged:
|
||||
newList, delList = self.importPage.getNewList()
|
||||
project.setImportColours(newList, delList)
|
||||
if self.importPage.changed:
|
||||
logger.debug("Updating importance labels")
|
||||
project.data.itemImport.update(self.importPage.getNewList())
|
||||
rebuildTrees = True
|
||||
|
||||
if self.replacePage.wasChanged:
|
||||
newList = self.replacePage.getNewList()
|
||||
project.data.setAutoReplace(newList)
|
||||
if self.replacePage.changed:
|
||||
logger.debug("Updating auto-replace settings")
|
||||
project.data.setAutoReplace(self.replacePage.getNewList())
|
||||
|
||||
self.newProjectSettingsReady.emit(rebuildTrees)
|
||||
QApplication.processEvents()
|
||||
@@ -301,12 +307,12 @@ class _SettingsPage(NScrollableForm):
|
||||
|
||||
class _StatusPage(NFixedPage):
|
||||
|
||||
COL_LABEL = 0
|
||||
COL_USAGE = 1
|
||||
C_DATA = 0
|
||||
C_LABEL = 0
|
||||
C_USAGE = 1
|
||||
|
||||
KEY_ROLE = QtUserRole
|
||||
COL_ROLE = QtUserRole + 1
|
||||
NUM_ROLE = QtUserRole + 2
|
||||
D_KEY = QtUserRole
|
||||
D_ENTRY = QtUserRole + 1
|
||||
|
||||
def __init__(self, parent: QWidget, isStatus: bool) -> None:
|
||||
super().__init__(parent=parent)
|
||||
@@ -325,12 +331,21 @@ class _StatusPage(NFixedPage):
|
||||
)
|
||||
|
||||
self._changed = False
|
||||
self._colDeleted = []
|
||||
self._selColour = QColor(100, 100, 100)
|
||||
self._color = QColor(100, 100, 100)
|
||||
self._shape = nwStatusShape.SQUARE
|
||||
self._icons = {}
|
||||
|
||||
self.iPx = SHARED.theme.baseIconHeight
|
||||
self._iPx = SHARED.theme.baseIconHeight
|
||||
iSz = SHARED.theme.baseIconSize
|
||||
bSz = SHARED.theme.buttonIconSize
|
||||
bPd = CONFIG.pxInt(4)
|
||||
|
||||
iColor = self.palette().text().color()
|
||||
|
||||
# Labels
|
||||
self.trCountNone = self.tr("Not in use")
|
||||
self.trCountOne = self.tr("Used once")
|
||||
self.trCountMore = self.tr("Used by {0} items")
|
||||
self.trSelColor = self.tr("Select Colour")
|
||||
|
||||
# Title
|
||||
self.pageTitle = NColourLabel(
|
||||
@@ -341,12 +356,14 @@ class _StatusPage(NFixedPage):
|
||||
# List Box
|
||||
self.listBox = QTreeWidget(self)
|
||||
self.listBox.setHeaderLabels([self.tr("Label"), self.tr("Usage")])
|
||||
self.listBox.itemSelectionChanged.connect(self._selectedItem)
|
||||
self.listBox.setColumnWidth(self.COL_LABEL, wCol0)
|
||||
self.listBox.setColumnWidth(self.C_LABEL, wCol0)
|
||||
self.listBox.setIndentation(0)
|
||||
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||
self.listBox.itemSelectionChanged.connect(self._selectionChanged)
|
||||
|
||||
for key, entry in status.items():
|
||||
self._addItem(key, entry["name"], entry["cols"], entry["count"])
|
||||
for key, entry in status.iterItems():
|
||||
self._addItem(key, StatusEntry.duplicate(entry))
|
||||
|
||||
# List Controls
|
||||
self.addButton = NIconToolButton(self, iSz, "add")
|
||||
@@ -367,16 +384,43 @@ class _StatusPage(NFixedPage):
|
||||
self.editName.setPlaceholderText(self.tr("Select item to edit"))
|
||||
self.editName.setEnabled(False)
|
||||
|
||||
self.colPixmap = QPixmap(self.iPx, self.iPx)
|
||||
self.colPixmap.fill(QColor(100, 100, 100))
|
||||
self.colButton = QPushButton(QIcon(self.colPixmap), self.tr("Colour"), self)
|
||||
self.colButton.setIconSize(bSz)
|
||||
self.colButton.setEnabled(False)
|
||||
self.colButton.clicked.connect(self._selectColour)
|
||||
buttonStyle = (
|
||||
f"QToolButton {{padding: 0 {bPd}px;}} "
|
||||
"QToolButton::menu-indicator {image: none;}"
|
||||
)
|
||||
|
||||
self.saveButton = QPushButton(self.tr("Save"), self)
|
||||
self.saveButton.setEnabled(False)
|
||||
self.saveButton.clicked.connect(self._saveItem)
|
||||
self.colorButton = NIconToolButton(self, iSz)
|
||||
self.colorButton.setToolTip(self.tr("Colour"))
|
||||
self.colorButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
|
||||
self.colorButton.setStyleSheet(buttonStyle)
|
||||
self.colorButton.setEnabled(False)
|
||||
self.colorButton.clicked.connect(self._selectColour)
|
||||
|
||||
def buildMenu(menu: QMenu, items: dict[nwStatusShape, str]) -> None:
|
||||
for shape, label in items.items():
|
||||
icon = NWStatus.createIcon(self._iPx, iColor, shape)
|
||||
action = menu.addAction(icon, label)
|
||||
action.triggered.connect(lambda _, shape=shape: self._selectShape(shape))
|
||||
self._icons[shape] = icon
|
||||
|
||||
self.shapeMenu = QMenu(self)
|
||||
buildMenu(self.shapeMenu, nwLabels.SHAPES_PLAIN)
|
||||
buildMenu(self.shapeMenu.addMenu(self.tr("Circles ...")), nwLabels.SHAPES_CIRCLE)
|
||||
buildMenu(self.shapeMenu.addMenu(self.tr("Bars ...")), nwLabels.SHAPES_BARS)
|
||||
buildMenu(self.shapeMenu.addMenu(self.tr("Blocks ...")), nwLabels.SHAPES_BLOCKS)
|
||||
|
||||
self.shapeButton = NIconToolButton(self, iSz)
|
||||
self.shapeButton.setMenu(self.shapeMenu)
|
||||
self.shapeButton.setToolTip(self.tr("Shape"))
|
||||
self.shapeButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
|
||||
self.shapeButton.setStyleSheet(buttonStyle)
|
||||
self.shapeButton.setEnabled(False)
|
||||
|
||||
self.applyButton = QToolButton(self)
|
||||
self.applyButton.setText(self.tr("Apply"))
|
||||
self.applyButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
|
||||
self.applyButton.setEnabled(False)
|
||||
self.applyButton.clicked.connect(self._applyChanges)
|
||||
|
||||
# Assemble
|
||||
self.listControls = QVBoxLayout()
|
||||
@@ -387,28 +431,30 @@ class _StatusPage(NFixedPage):
|
||||
self.listControls.addStretch(1)
|
||||
|
||||
self.editBox = QHBoxLayout()
|
||||
self.editBox.addWidget(self.editName)
|
||||
self.editBox.addWidget(self.colButton)
|
||||
self.editBox.addWidget(self.saveButton)
|
||||
self.editBox.addWidget(self.editName, 1)
|
||||
self.editBox.addWidget(self.colorButton, 0)
|
||||
self.editBox.addWidget(self.shapeButton, 0)
|
||||
self.editBox.addWidget(self.applyButton, 0)
|
||||
|
||||
self.mainBox = QVBoxLayout()
|
||||
self.mainBox.addWidget(self.listBox)
|
||||
self.mainBox.addLayout(self.editBox)
|
||||
self.mainBox.addWidget(self.listBox, 1)
|
||||
self.mainBox.addLayout(self.editBox, 0)
|
||||
|
||||
self.innerBox = QHBoxLayout()
|
||||
self.innerBox.addLayout(self.mainBox)
|
||||
self.innerBox.addLayout(self.listControls)
|
||||
self.innerBox.addLayout(self.mainBox, 1)
|
||||
self.innerBox.addLayout(self.listControls, 0)
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.outerBox.addWidget(self.pageTitle)
|
||||
self.outerBox.addLayout(self.innerBox)
|
||||
self.outerBox.addWidget(self.pageTitle, 0)
|
||||
self.outerBox.addLayout(self.innerBox, 1)
|
||||
|
||||
self.setCentralLayout(self.outerBox)
|
||||
self._setButtonIcons()
|
||||
|
||||
return
|
||||
|
||||
@property
|
||||
def wasChanged(self) -> bool:
|
||||
def changed(self) -> bool:
|
||||
"""The user changed these settings."""
|
||||
return self._changed
|
||||
|
||||
@@ -416,20 +462,17 @@ class _StatusPage(NFixedPage):
|
||||
# Methods
|
||||
##
|
||||
|
||||
def getNewList(self) -> tuple[list, list]:
|
||||
def getNewList(self) -> list[tuple[str | None, StatusEntry]]:
|
||||
"""Return list of entries."""
|
||||
if self._changed:
|
||||
newList = []
|
||||
update = []
|
||||
for n in range(self.listBox.topLevelItemCount()):
|
||||
item = self.listBox.topLevelItem(n)
|
||||
if item is not None:
|
||||
newList.append({
|
||||
"key": item.data(self.COL_LABEL, self.KEY_ROLE),
|
||||
"name": item.text(self.COL_LABEL),
|
||||
"cols": item.data(self.COL_LABEL, self.COL_ROLE),
|
||||
})
|
||||
return newList, self._colDeleted
|
||||
return [], []
|
||||
if item := self.listBox.topLevelItem(n):
|
||||
key = item.data(self.C_DATA, self.D_KEY)
|
||||
entry = item.data(self.C_DATA, self.D_ENTRY)
|
||||
update.append((key, entry))
|
||||
return update
|
||||
return []
|
||||
|
||||
def columnWidth(self) -> int:
|
||||
"""Return the size of the header column."""
|
||||
@@ -442,124 +485,119 @@ class _StatusPage(NFixedPage):
|
||||
@pyqtSlot()
|
||||
def _selectColour(self) -> None:
|
||||
"""Open a dialog to select the status icon colour."""
|
||||
if self._selColour is not None:
|
||||
newCol = QColorDialog.getColor(
|
||||
self._selColour, self, self.tr("Select Colour")
|
||||
)
|
||||
if newCol.isValid():
|
||||
self._selColour = newCol
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(newCol)
|
||||
self.colButton.setIcon(QIcon(pixmap))
|
||||
self.colButton.setIconSize(pixmap.rect().size())
|
||||
if (color := QColorDialog.getColor(self._color, self, self.trSelColor)).isValid():
|
||||
self._color = color
|
||||
self._setButtonIcons()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _newItem(self) -> None:
|
||||
"""Create a new status item."""
|
||||
self._addItem(None, self.tr("New Item"), (100, 100, 100), 0)
|
||||
color = QColor(100, 100, 100)
|
||||
shape = nwStatusShape.SQUARE
|
||||
icon = NWStatus.createIcon(self._iPx, color, shape)
|
||||
self._addItem(None, StatusEntry(self.tr("New Item"), color, shape, icon, 0))
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _delItem(self) -> None:
|
||||
"""Delete a status item."""
|
||||
selItem = self._getSelectedItem()
|
||||
if isinstance(selItem, QTreeWidgetItem):
|
||||
iRow = self.listBox.indexOfTopLevelItem(selItem)
|
||||
if selItem.data(self.COL_LABEL, self.NUM_ROLE) > 0:
|
||||
if item := self._getSelectedItem():
|
||||
iRow = self.listBox.indexOfTopLevelItem(item)
|
||||
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
||||
if entry.count > 0:
|
||||
SHARED.error(self.tr("Cannot delete a status item that is in use."))
|
||||
else:
|
||||
self.listBox.takeTopLevelItem(iRow)
|
||||
self._colDeleted.append(selItem.data(self.COL_LABEL, self.KEY_ROLE))
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _saveItem(self) -> None:
|
||||
def _applyChanges(self) -> None:
|
||||
"""Save changes made to a status item."""
|
||||
selItem = self._getSelectedItem()
|
||||
if isinstance(selItem, QTreeWidgetItem):
|
||||
selItem.setText(self.COL_LABEL, simplified(self.editName.text()))
|
||||
selItem.setIcon(self.COL_LABEL, self.colButton.icon())
|
||||
selItem.setData(self.COL_LABEL, self.COL_ROLE, (
|
||||
self._selColour.red(), self._selColour.green(), self._selColour.blue()
|
||||
))
|
||||
if item := self._getSelectedItem():
|
||||
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
||||
|
||||
name = simplified(self.editName.text())
|
||||
icon = NWStatus.createIcon(self._iPx, self._color, self._shape)
|
||||
|
||||
entry.name = name
|
||||
entry.color = self._color
|
||||
entry.shape = self._shape
|
||||
entry.icon = icon
|
||||
|
||||
item.setText(self.C_LABEL, name)
|
||||
item.setIcon(self.C_LABEL, icon)
|
||||
|
||||
self._changed = True
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _selectedItem(self) -> None:
|
||||
def _selectionChanged(self) -> None:
|
||||
"""Extract the info of a selected item and populate the settings
|
||||
boxes and button. If no item is selected, clear the form.
|
||||
"""
|
||||
selItem = self._getSelectedItem()
|
||||
if isinstance(selItem, QTreeWidgetItem):
|
||||
cols = selItem.data(self.COL_LABEL, self.COL_ROLE)
|
||||
name = selItem.text(self.COL_LABEL)
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(*cols))
|
||||
self._selColour = QColor(*cols)
|
||||
self.editName.setText(name)
|
||||
self.colButton.setIcon(QIcon(pixmap))
|
||||
if item := self._getSelectedItem():
|
||||
entry: StatusEntry = item.data(self.C_DATA, self.D_ENTRY)
|
||||
self._color = entry.color
|
||||
self._shape = entry.shape
|
||||
self._setButtonIcons()
|
||||
|
||||
self.editName.setText(entry.name)
|
||||
self.editName.selectAll()
|
||||
self.editName.setFocus()
|
||||
|
||||
self.editName.setEnabled(True)
|
||||
self.colButton.setEnabled(True)
|
||||
self.saveButton.setEnabled(True)
|
||||
self.colorButton.setEnabled(True)
|
||||
self.shapeButton.setEnabled(True)
|
||||
self.applyButton.setEnabled(True)
|
||||
|
||||
else:
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(100, 100, 100))
|
||||
self._selColour = QColor(100, 100, 100)
|
||||
self._color = QColor(100, 100, 100)
|
||||
self._shape = nwStatusShape.SQUARE
|
||||
self._setButtonIcons()
|
||||
self.editName.setText("")
|
||||
self.colButton.setIcon(QIcon(pixmap))
|
||||
|
||||
self.editName.setEnabled(False)
|
||||
self.colButton.setEnabled(False)
|
||||
self.saveButton.setEnabled(False)
|
||||
self.colorButton.setEnabled(False)
|
||||
self.shapeButton.setEnabled(False)
|
||||
self.applyButton.setEnabled(False)
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _addItem(self, key: str | None, name: str,
|
||||
colour: tuple[int, int, int], count: int) -> None:
|
||||
def _selectShape(self, shape: nwStatusShape) -> None:
|
||||
"""Set the current shape."""
|
||||
self._shape = shape
|
||||
self._setButtonIcons()
|
||||
return
|
||||
|
||||
def _addItem(self, key: str | None, entry: StatusEntry) -> None:
|
||||
"""Add a status item to the list."""
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(*colour))
|
||||
|
||||
item = QTreeWidgetItem()
|
||||
item.setText(self.COL_LABEL, name)
|
||||
item.setIcon(self.COL_LABEL, QIcon(pixmap))
|
||||
item.setData(self.COL_LABEL, self.KEY_ROLE, key)
|
||||
item.setData(self.COL_LABEL, self.COL_ROLE, colour)
|
||||
item.setData(self.COL_LABEL, self.NUM_ROLE, count)
|
||||
item.setText(self.COL_USAGE, self._usageString(count))
|
||||
|
||||
item.setText(self.C_LABEL, entry.name)
|
||||
item.setIcon(self.C_LABEL, entry.icon)
|
||||
item.setText(self.C_USAGE, self._usageString(entry.count))
|
||||
item.setData(self.C_DATA, self.D_KEY, key)
|
||||
item.setData(self.C_DATA, self.D_ENTRY, entry)
|
||||
self.listBox.addTopLevelItem(item)
|
||||
|
||||
return
|
||||
|
||||
def _moveItem(self, step: int) -> None:
|
||||
"""Move and item up or down step."""
|
||||
selItem = self._getSelectedItem()
|
||||
if selItem is None:
|
||||
return
|
||||
|
||||
tIndex = self.listBox.indexOfTopLevelItem(selItem)
|
||||
nChild = self.listBox.topLevelItemCount()
|
||||
nIndex = tIndex + step
|
||||
if nIndex < 0 or nIndex >= nChild:
|
||||
return
|
||||
|
||||
cItem = self.listBox.takeTopLevelItem(tIndex)
|
||||
self.listBox.insertTopLevelItem(nIndex, cItem)
|
||||
self.listBox.clearSelection()
|
||||
|
||||
if cItem is not None:
|
||||
cItem.setSelected(True)
|
||||
self._changed = True
|
||||
|
||||
if item := self._getSelectedItem():
|
||||
tIdx = self.listBox.indexOfTopLevelItem(item)
|
||||
nItm = self.listBox.topLevelItemCount()
|
||||
nIdx = tIdx + step
|
||||
if (0 <= nIdx < nItm) and (cItem := self.listBox.takeTopLevelItem(tIdx)):
|
||||
self.listBox.insertTopLevelItem(nIdx, cItem)
|
||||
self.listBox.clearSelection()
|
||||
cItem.setSelected(True)
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
def _getSelectedItem(self) -> QTreeWidgetItem | None:
|
||||
@@ -571,19 +609,26 @@ class _StatusPage(NFixedPage):
|
||||
def _usageString(self, count: int) -> str:
|
||||
"""Generate usage string."""
|
||||
if count == 0:
|
||||
return self.tr("Not in use")
|
||||
return self.trCountNone
|
||||
elif count == 1:
|
||||
return self.tr("Used once")
|
||||
return self.trCountOne
|
||||
else:
|
||||
return self.tr("Used by {0} items").format(count)
|
||||
return self.trCountMore.format(count)
|
||||
|
||||
def _setButtonIcons(self) -> None:
|
||||
"""Set the colour of the colour button."""
|
||||
icon = NWStatus.createIcon(self._iPx, self._color, nwStatusShape.SQUARE)
|
||||
self.colorButton.setIcon(icon)
|
||||
self.shapeButton.setIcon(self._icons[self._shape])
|
||||
return
|
||||
|
||||
# END Class _StatusPage
|
||||
|
||||
|
||||
class _ReplacePage(NFixedPage):
|
||||
|
||||
COL_KEY = 0
|
||||
COL_REPL = 1
|
||||
C_KEY = 0
|
||||
C_REPL = 1
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
@@ -605,15 +650,17 @@ class _ReplacePage(NFixedPage):
|
||||
# List Box
|
||||
self.listBox = QTreeWidget(self)
|
||||
self.listBox.setHeaderLabels([self.tr("Keyword"), self.tr("Replace With")])
|
||||
self.listBox.setColumnWidth(self.COL_KEY, wCol0)
|
||||
self.listBox.setColumnWidth(self.C_KEY, wCol0)
|
||||
self.listBox.setIndentation(0)
|
||||
self.listBox.itemSelectionChanged.connect(self._selectedItem)
|
||||
self.listBox.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
self.listBox.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
|
||||
self.listBox.itemSelectionChanged.connect(self._selectionChanged)
|
||||
|
||||
for aKey, aVal in SHARED.project.data.autoReplace.items():
|
||||
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
|
||||
self.listBox.addTopLevelItem(newItem)
|
||||
|
||||
self.listBox.sortByColumn(self.COL_KEY, Qt.SortOrder.AscendingOrder)
|
||||
self.listBox.sortByColumn(self.C_KEY, Qt.SortOrder.AscendingOrder)
|
||||
self.listBox.setSortingEnabled(True)
|
||||
|
||||
# List Controls
|
||||
@@ -633,8 +680,10 @@ class _ReplacePage(NFixedPage):
|
||||
self.editValue.setEnabled(False)
|
||||
self.editValue.setMaxLength(80)
|
||||
|
||||
self.saveButton = QPushButton(self.tr("Save"), self)
|
||||
self.saveButton.clicked.connect(self._saveEntry)
|
||||
self.applyButton = QToolButton(self)
|
||||
self.applyButton.setText(self.tr("Apply"))
|
||||
self.applyButton.setSizePolicy(QtSizeMinimum, QtSizeMinimumExpanding)
|
||||
self.applyButton.clicked.connect(self._applyChanges)
|
||||
|
||||
# Assemble
|
||||
self.listControls = QVBoxLayout()
|
||||
@@ -645,11 +694,11 @@ class _ReplacePage(NFixedPage):
|
||||
self.editBox = QHBoxLayout()
|
||||
self.editBox.addWidget(self.editKey, 4)
|
||||
self.editBox.addWidget(self.editValue, 5)
|
||||
self.editBox.addWidget(self.saveButton, 0)
|
||||
self.editBox.addWidget(self.applyButton, 0)
|
||||
|
||||
self.mainBox = QVBoxLayout()
|
||||
self.mainBox.addWidget(self.listBox)
|
||||
self.mainBox.addLayout(self.editBox)
|
||||
self.mainBox.addWidget(self.listBox, 1)
|
||||
self.mainBox.addLayout(self.editBox, 0)
|
||||
|
||||
self.innerBox = QHBoxLayout()
|
||||
self.innerBox.addLayout(self.mainBox)
|
||||
@@ -664,7 +713,7 @@ class _ReplacePage(NFixedPage):
|
||||
return
|
||||
|
||||
@property
|
||||
def wasChanged(self) -> bool:
|
||||
def changed(self) -> bool:
|
||||
"""The user changed these settings."""
|
||||
return self._changed
|
||||
|
||||
@@ -672,15 +721,13 @@ class _ReplacePage(NFixedPage):
|
||||
# Methods
|
||||
##
|
||||
|
||||
def getNewList(self) -> dict:
|
||||
def getNewList(self) -> dict[str, str]:
|
||||
"""Extract the list from the widget."""
|
||||
new = {}
|
||||
for n in range(self.listBox.topLevelItemCount()):
|
||||
if tItem := self.listBox.topLevelItem(n):
|
||||
aKey = self._stripNotAllowed(tItem.text(0))
|
||||
aVal = tItem.text(1)
|
||||
if len(aKey) > 0:
|
||||
new[aKey] = aVal
|
||||
if item := self.listBox.topLevelItem(n):
|
||||
if key := self._stripNotAllowed(item.text(self.C_KEY)):
|
||||
new[key] = item.text(self.C_REPL)
|
||||
return new
|
||||
|
||||
def columnWidth(self) -> int:
|
||||
@@ -692,51 +739,48 @@ class _ReplacePage(NFixedPage):
|
||||
##
|
||||
|
||||
@pyqtSlot()
|
||||
def _selectedItem(self) -> None:
|
||||
def _selectionChanged(self) -> None:
|
||||
"""Extract the details from the selected item and populate the
|
||||
edit form.
|
||||
"""
|
||||
if selItem := self._getSelectedItem():
|
||||
editKey = self._stripNotAllowed(selItem.text(0))
|
||||
editVal = selItem.text(1)
|
||||
self.editKey.setText(editKey)
|
||||
self.editValue.setText(editVal)
|
||||
if item := self._getSelectedItem():
|
||||
self.editKey.setText(self._stripNotAllowed(item.text(self.C_KEY)))
|
||||
self.editValue.setText(item.text(self.C_REPL))
|
||||
self.editKey.setEnabled(True)
|
||||
self.editValue.setEnabled(True)
|
||||
self.editKey.selectAll()
|
||||
self.editKey.setFocus()
|
||||
else:
|
||||
self.editKey.setText("")
|
||||
self.editValue.setText("")
|
||||
self.editKey.setEnabled(False)
|
||||
self.editValue.setEnabled(False)
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _saveEntry(self) -> None:
|
||||
def _applyChanges(self) -> None:
|
||||
"""Save the form data into the list widget."""
|
||||
if selItem := self._getSelectedItem():
|
||||
newKey = self.editKey.text()
|
||||
newVal = self.editValue.text()
|
||||
saveKey = self._stripNotAllowed(newKey)
|
||||
if len(saveKey) > 0 and len(newVal) > 0:
|
||||
selItem.setText(self.COL_KEY, "<%s>" % saveKey)
|
||||
selItem.setText(self.COL_REPL, newVal)
|
||||
self.editKey.clear()
|
||||
self.editValue.clear()
|
||||
self.editKey.setEnabled(False)
|
||||
self.editValue.setEnabled(False)
|
||||
self.listBox.clearSelection()
|
||||
if item := self._getSelectedItem():
|
||||
key = self._stripNotAllowed(self.editKey.text())
|
||||
value = self.editValue.text()
|
||||
if key and value:
|
||||
item.setText(self.C_KEY, f"<{key}>")
|
||||
item.setText(self.C_REPL, value)
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _addEntry(self) -> None:
|
||||
"""Add a new list entry."""
|
||||
saveKey = "<keyword%d>" % (self.listBox.topLevelItemCount() + 1)
|
||||
self.listBox.addTopLevelItem(QTreeWidgetItem([saveKey, ""]))
|
||||
key = f"<keyword{self.listBox.topLevelItemCount() + 1:d}>"
|
||||
self.listBox.addTopLevelItem(QTreeWidgetItem([key, ""]))
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _delEntry(self) -> None:
|
||||
"""Delete the selected entry."""
|
||||
if selItem := self._getSelectedItem():
|
||||
self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem))
|
||||
if item := self._getSelectedItem():
|
||||
self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(item))
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
|
||||
@@ -205,3 +205,29 @@ class nwBuildFmt(Enum):
|
||||
J_NWD = 7
|
||||
|
||||
# END Enum nwBuildFormat
|
||||
|
||||
|
||||
class nwStatusShape(Enum):
|
||||
|
||||
SQUARE = 0
|
||||
TRIANGLE = 1
|
||||
NABLA = 2
|
||||
DIAMOND = 3
|
||||
PENTAGON = 4
|
||||
HEXAGON = 5
|
||||
STAR = 6
|
||||
PACMAN = 7
|
||||
CIRCLE_Q = 8
|
||||
CIRCLE_H = 9
|
||||
CIRCLE_T = 10
|
||||
CIRCLE = 11
|
||||
BARS_1 = 12
|
||||
BARS_2 = 13
|
||||
BARS_3 = 14
|
||||
BARS_4 = 15
|
||||
BLOCK_1 = 16
|
||||
BLOCK_2 = 17
|
||||
BLOCK_3 = 18
|
||||
BLOCK_4 = 19
|
||||
|
||||
# END Enum nwStatusShape
|
||||
|
||||
@@ -27,10 +27,11 @@ from math import ceil
|
||||
|
||||
from PyQt5.QtCore import QRect
|
||||
from PyQt5.QtGui import QBrush, QColor, QPaintEvent, QPainter, QPen
|
||||
from PyQt5.QtWidgets import QProgressBar, QSizePolicy, QWidget
|
||||
from PyQt5.QtWidgets import QProgressBar, QWidget
|
||||
|
||||
from novelwriter.types import (
|
||||
QtPaintAnitAlias, QtAlignCenter, QtRoundCap, QtSolidLine, QtTransparent
|
||||
QtPaintAnitAlias, QtAlignCenter, QtRoundCap, QtSizeFixed, QtSolidLine,
|
||||
QtTransparent
|
||||
)
|
||||
|
||||
|
||||
@@ -59,7 +60,7 @@ class NProgressCircle(QProgressBar):
|
||||
bar=self.palette().highlight().color(),
|
||||
text=self.palette().text().color()
|
||||
)
|
||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
self.setSizePolicy(QtSizeFixed, QtSizeFixed)
|
||||
self.setFixedWidth(size)
|
||||
self.setFixedHeight(size)
|
||||
return
|
||||
|
||||
@@ -25,6 +25,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from PyQt5.QtCore import QSize, Qt
|
||||
from PyQt5.QtGui import QWheelEvent
|
||||
from PyQt5.QtWidgets import QComboBox, QDoubleSpinBox, QSpinBox, QToolButton, QWidget
|
||||
@@ -46,7 +48,7 @@ class NComboBox(QComboBox):
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
def setCurrentData(self, data: str, default: str) -> None:
|
||||
def setCurrentData(self, data: str | Enum, default: str | Enum) -> None:
|
||||
"""Set the current index from data, with a fallback."""
|
||||
idx = self.findData(data)
|
||||
self.setCurrentIndex(self.findData(default) if idx < 0 else idx)
|
||||
|
||||
@@ -28,11 +28,14 @@ from __future__ import annotations
|
||||
from PyQt5.QtGui import QColor, QPaintEvent, QPainter, QPolygon
|
||||
from PyQt5.QtCore import QPoint, QRectF, QSize, Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractButton, QAction, QButtonGroup, QLabel, QSizePolicy, QStyle,
|
||||
QAbstractButton, QAction, QButtonGroup, QLabel, QStyle,
|
||||
QStyleOptionToolButton, QToolBar, QToolButton, QWidget
|
||||
)
|
||||
|
||||
from novelwriter.types import QtPaintAnitAlias, QtAlignLeft, QtMouseOver, QtNoBrush, QtNoPen
|
||||
from novelwriter.types import (
|
||||
QtPaintAnitAlias, QtAlignLeft, QtMouseOver, QtNoBrush, QtNoPen,
|
||||
QtSizeExpanding, QtSizeFixed
|
||||
)
|
||||
|
||||
|
||||
class NPagedSideBar(QToolBar):
|
||||
@@ -59,7 +62,7 @@ class NPagedSideBar(QToolBar):
|
||||
self.setOrientation(Qt.Orientation.Vertical)
|
||||
|
||||
stretch = QWidget(self)
|
||||
stretch.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
stretch.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
||||
self._stretchAction = self.addWidget(stretch)
|
||||
|
||||
return
|
||||
@@ -119,7 +122,7 @@ class _NPagedToolButton(QToolButton):
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
self.setSizePolicy(QtSizeExpanding, QtSizeFixed)
|
||||
self.setCheckable(True)
|
||||
|
||||
fH = self.fontMetrics().height()
|
||||
@@ -197,7 +200,7 @@ class _NPagedToolLabel(QLabel):
|
||||
def __init__(self, parent: QWidget, textColor: QColor | None = None) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
self.setSizePolicy(QtSizeExpanding, QtSizeFixed)
|
||||
|
||||
fH = self.fontMetrics().height()
|
||||
self._bH = round(fH * 1.7)
|
||||
|
||||
@@ -25,10 +25,10 @@ from __future__ import annotations
|
||||
|
||||
from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent
|
||||
from PyQt5.QtCore import QEvent, QPropertyAnimation, Qt, pyqtProperty
|
||||
from PyQt5.QtWidgets import QAbstractButton, QSizePolicy, QWidget
|
||||
from PyQt5.QtWidgets import QAbstractButton, QWidget
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.types import QtPaintAnitAlias, QtMouseLeft, QtNoPen
|
||||
from novelwriter.types import QtPaintAnitAlias, QtMouseLeft, QtNoPen, QtSizeFixed
|
||||
|
||||
|
||||
class NSwitch(QAbstractButton):
|
||||
@@ -46,7 +46,7 @@ class NSwitch(QAbstractButton):
|
||||
self._rR = self._xR - self._rB
|
||||
|
||||
self.setCheckable(True)
|
||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
self.setSizePolicy(QtSizeFixed, QtSizeFixed)
|
||||
self.setFixedWidth(self._xW)
|
||||
self.setFixedHeight(self._xH)
|
||||
self._offset = self._xR
|
||||
|
||||
@@ -25,10 +25,13 @@ from __future__ import annotations
|
||||
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget
|
||||
from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QWidget
|
||||
|
||||
from novelwriter.extensions.switch import NSwitch
|
||||
from novelwriter.types import QtAlignLeft, QtAlignRight, QtAlignRightMiddle
|
||||
from novelwriter.types import (
|
||||
QtAlignLeft, QtAlignRight, QtAlignRightMiddle, QtSizeMinimum,
|
||||
QtSizeMinimumExpanding
|
||||
)
|
||||
|
||||
|
||||
class NSwitchBox(QScrollArea):
|
||||
@@ -59,7 +62,7 @@ class NSwitchBox(QScrollArea):
|
||||
self._content.setColumnStretch(1, 1)
|
||||
|
||||
self._widget = QWidget(self)
|
||||
self._widget.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Minimum)
|
||||
self._widget.setSizePolicy(QtSizeMinimumExpanding, QtSizeMinimum)
|
||||
self._widget.setLayout(self._content)
|
||||
|
||||
self.setWidgetResizable(True)
|
||||
|
||||
@@ -3136,7 +3136,7 @@ class GuiDocEditFooter(QWidget):
|
||||
sText = ""
|
||||
else:
|
||||
iPx = round(0.9*SHARED.theme.baseIconHeight)
|
||||
status, icon = self._tItem.getImportStatus(incIcon=True)
|
||||
status, icon = self._tItem.getImportStatus()
|
||||
sIcon = icon.pixmap(iPx, iPx)
|
||||
sText = f"{status} / {self._tItem.describeMe()}"
|
||||
|
||||
|
||||
@@ -450,7 +450,7 @@ class _ViewPanelKeyWords(QTreeWidget):
|
||||
nwItem.itemType, nwItem.itemClass,
|
||||
nwItem.itemLayout, nwItem.mainHeading
|
||||
)
|
||||
impLabel, impIcon = nwItem.getImportStatus(incIcon=True)
|
||||
impLabel, impIcon = nwItem.getImportStatus()
|
||||
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
|
||||
hDec = SHARED.theme.getHeaderDecorationNarrow(iLevel)
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ class GuiItemDetails(QWidget):
|
||||
# Status
|
||||
# ======
|
||||
|
||||
status, icon = nwItem.getImportStatus(incIcon=True)
|
||||
status, icon = nwItem.getImportStatus()
|
||||
self.statusIcon.setPixmap(icon.pixmap(iPx, iPx))
|
||||
self.statusData.setText(status)
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ from PyQt5.QtCore import QModelIndex, QPoint, Qt, pyqtSlot, pyqtSignal
|
||||
from PyQt5.QtGui import QFocusEvent, QFont, QMouseEvent, QPalette, QResizeEvent
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QActionGroup, QFrame, QHBoxLayout, QHeaderView,
|
||||
QInputDialog, QMenu, QSizePolicy, QToolTip, QTreeWidget, QTreeWidgetItem,
|
||||
QVBoxLayout, QWidget
|
||||
QInputDialog, QMenu, QToolTip, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
|
||||
QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -47,7 +47,10 @@ from novelwriter.enum import nwDocMode, nwItemClass, nwOutline
|
||||
from novelwriter.extensions.modified import NIconToolButton
|
||||
from novelwriter.extensions.novelselector import NovelSelector
|
||||
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
||||
from novelwriter.types import QtAlignRight, QtDecoration, QtMouseLeft, QtMouseMiddle, QtUserRole
|
||||
from novelwriter.types import (
|
||||
QtAlignRight, QtDecoration, QtMouseLeft, QtMouseMiddle, QtSizeExpanding,
|
||||
QtUserRole
|
||||
)
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.guimain import GuiMain
|
||||
@@ -215,7 +218,7 @@ class GuiNovelToolBar(QWidget):
|
||||
self.novelValue.setFont(selFont)
|
||||
self.novelValue.setListFormat(self.tr("Outline of {0}"))
|
||||
self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
|
||||
self.novelValue.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
self.novelValue.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
||||
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
|
||||
|
||||
self.tbNovel = NIconToolButton(self, iSz)
|
||||
|
||||
@@ -36,20 +36,19 @@ from enum import Enum
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, QT_TRANSLATE_NOOP
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QAction, QFileDialog, QFrame, QGridLayout, QGroupBox,
|
||||
QHBoxLayout, QLabel, QMenu, QScrollArea, QSizePolicy, QSplitter, QToolBar,
|
||||
QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
QHBoxLayout, QLabel, QMenu, QScrollArea, QSplitter, QToolBar, QToolButton,
|
||||
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.enum import (
|
||||
nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
|
||||
)
|
||||
from novelwriter.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType, nwOutline
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import checkInt, formatFileFilter, makeFileNameSafe
|
||||
from novelwriter.constants import nwHeaders, trConst, nwKeyWords, nwLabels
|
||||
from novelwriter.extensions.novelselector import NovelSelector
|
||||
from novelwriter.types import (
|
||||
QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration, QtUserRole
|
||||
QtAlignLeftTop, QtAlignRight, QtAlignRightTop, QtDecoration,
|
||||
QtSizeExpanding, QtUserRole
|
||||
)
|
||||
|
||||
|
||||
@@ -217,7 +216,7 @@ class GuiOutlineToolBar(QToolBar):
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
stretch = QWidget(self)
|
||||
stretch.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
stretch.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
||||
|
||||
# Novel Selector
|
||||
self.novelLabel = QLabel(self.tr("Outline of"), self)
|
||||
@@ -1048,7 +1047,7 @@ class GuiOutlineDetails(QScrollArea):
|
||||
self.titleLabel.setText(self.tr(self.LVL_MAP.get(novIdx.level, "H1")))
|
||||
self.titleValue.setText(novIdx.title)
|
||||
|
||||
itemStatus, _ = nwItem.getImportStatus(incIcon=False)
|
||||
itemStatus, _ = nwItem.getImportStatus()
|
||||
|
||||
self.fileValue.setText(nwItem.itemName)
|
||||
self.itemValue.setText(itemStatus)
|
||||
|
||||
+14
-11
@@ -38,8 +38,8 @@ from PyQt5.QtGui import (
|
||||
)
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QAction, QDialog, QFrame, QHBoxLayout, QHeaderView,
|
||||
QLabel, QMenu, QShortcut, QSizePolicy, QTreeWidget, QTreeWidgetItem,
|
||||
QVBoxLayout, QWidget
|
||||
QLabel, QMenu, QShortcut, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
|
||||
QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -54,7 +54,10 @@ from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
||||
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout
|
||||
from novelwriter.extensions.modified import NIconToolButton
|
||||
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
||||
from novelwriter.types import QtAlignLeft, QtAlignRight, QtMouseLeft, QtMouseMiddle, QtUserRole
|
||||
from novelwriter.types import (
|
||||
QtAlignLeft, QtAlignRight, QtMouseLeft, QtMouseMiddle, QtSizeExpanding,
|
||||
QtUserRole,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.guimain import GuiMain
|
||||
@@ -274,7 +277,7 @@ class GuiProjectToolBar(QWidget):
|
||||
self.viewLabel = QLabel(self.tr("Project Content"), self)
|
||||
self.viewLabel.setFont(SHARED.theme.guiFontB)
|
||||
self.viewLabel.setContentsMargins(0, 0, 0, 0)
|
||||
self.viewLabel.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
self.viewLabel.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
||||
|
||||
# Quick Links
|
||||
self.mQuick = QMenu(self)
|
||||
@@ -1033,7 +1036,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
if trItem is None or nwItem is None:
|
||||
return
|
||||
|
||||
itemStatus, statusIcon = nwItem.getImportStatus(incIcon=True)
|
||||
itemStatus, statusIcon = nwItem.getImportStatus()
|
||||
hLevel = nwItem.mainHeading
|
||||
itemIcon = SHARED.theme.getItemIcon(
|
||||
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, hLevel
|
||||
@@ -1855,11 +1858,11 @@ class _TreeContextMenu(QMenu):
|
||||
if self._item.isNovelLike():
|
||||
menu = self.addMenu(self.tr("Set Status to ..."))
|
||||
current = self._item.itemStatus
|
||||
for n, (key, entry) in enumerate(SHARED.project.data.itemStatus.items()):
|
||||
name = entry["name"]
|
||||
for n, (key, entry) in enumerate(SHARED.project.data.itemStatus.iterItems()):
|
||||
name = entry.name
|
||||
if not multi and current == key:
|
||||
name += f" ({nwUnicode.U_CHECK})"
|
||||
action = menu.addAction(entry["icon"], name)
|
||||
action = menu.addAction(entry.icon, name)
|
||||
if multi:
|
||||
action.triggered.connect(lambda n, key=key: self._iterSetItemStatus(key))
|
||||
else:
|
||||
@@ -1872,11 +1875,11 @@ class _TreeContextMenu(QMenu):
|
||||
else:
|
||||
menu = self.addMenu(self.tr("Set Importance to ..."))
|
||||
current = self._item.itemImport
|
||||
for n, (key, entry) in enumerate(SHARED.project.data.itemImport.items()):
|
||||
name = entry["name"]
|
||||
for n, (key, entry) in enumerate(SHARED.project.data.itemImport.iterItems()):
|
||||
name = entry.name
|
||||
if not multi and current == key:
|
||||
name += f" ({nwUnicode.U_CHECK})"
|
||||
action = menu.addAction(entry["icon"], name)
|
||||
action = menu.addAction(entry.icon, name)
|
||||
if multi:
|
||||
action.triggered.connect(lambda n, key=key: self._iterSetItemImport(key))
|
||||
else:
|
||||
|
||||
@@ -36,8 +36,8 @@ from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractItemView, QApplication, QDialog, QFormLayout, QGridLayout,
|
||||
QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton,
|
||||
QSizePolicy, QSplitter, QStackedWidget, QTabWidget, QTextBrowser,
|
||||
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
QSplitter, QStackedWidget, QTabWidget, QTextBrowser, QTreeWidget,
|
||||
QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
@@ -54,7 +54,7 @@ from novelwriter.tools.manusbuild import GuiManuscriptBuild
|
||||
from novelwriter.tools.manussettings import GuiBuildSettings
|
||||
from novelwriter.types import (
|
||||
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignRight, QtAlignTop,
|
||||
QtUserRole
|
||||
QtSizeExpanding, QtSizeIgnored, QtUserRole
|
||||
)
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
@@ -1023,16 +1023,14 @@ class _StatsWidget(QWidget):
|
||||
@pyqtSlot(bool)
|
||||
def _toggleView(self, state: bool) -> None:
|
||||
"""Toggle minimal or maximal view."""
|
||||
ignored = QSizePolicy.Policy.Ignored
|
||||
expanded = QSizePolicy.Policy.Expanding
|
||||
if state:
|
||||
self.mainStack.setCurrentWidget(self.maxWidget)
|
||||
self.maxWidget.setSizePolicy(expanded, expanded)
|
||||
self.minWidget.setSizePolicy(ignored, ignored)
|
||||
self.maxWidget.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
||||
self.minWidget.setSizePolicy(QtSizeIgnored, QtSizeIgnored)
|
||||
else:
|
||||
self.mainStack.setCurrentWidget(self.minWidget)
|
||||
self.maxWidget.setSizePolicy(ignored, ignored)
|
||||
self.minWidget.setSizePolicy(expanded, expanded)
|
||||
self.maxWidget.setSizePolicy(QtSizeIgnored, QtSizeIgnored)
|
||||
self.minWidget.setSizePolicy(QtSizeExpanding, QtSizeExpanding)
|
||||
self.maxWidget.adjustSize()
|
||||
self.minWidget.adjustSize()
|
||||
self.mainStack.adjustSize()
|
||||
|
||||
@@ -25,7 +25,7 @@ from __future__ import annotations
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QColor, QPainter, QTextCursor
|
||||
from PyQt5.QtWidgets import QDialogButtonBox, QStyle
|
||||
from PyQt5.QtWidgets import QDialogButtonBox, QSizePolicy, QStyle
|
||||
|
||||
# Qt Alignment Flags
|
||||
|
||||
@@ -88,3 +88,11 @@ QtKeepAnchor = QTextCursor.MoveMode.KeepAnchor
|
||||
QtMoveAnchor = QTextCursor.MoveMode.MoveAnchor
|
||||
QtMoveLeft = QTextCursor.MoveOperation.Left
|
||||
QtMoveRight = QTextCursor.MoveOperation.Right
|
||||
|
||||
# Size Policy
|
||||
|
||||
QtSizeExpanding = QSizePolicy.Policy.Expanding
|
||||
QtSizeFixed = QSizePolicy.Policy.Fixed
|
||||
QtSizeIgnored = QSizePolicy.Policy.Ignored
|
||||
QtSizeMinimum = QSizePolicy.Policy.Minimum
|
||||
QtSizeMinimumExpanding = QSizePolicy.Policy.MinimumExpanding
|
||||
|
||||
+22
-21
@@ -1,6 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="3" timeStamp="2024-04-06 15:52:29">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1699" autoCount="264" editTime="83349">
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-11 14:19:47">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1850" autoCount="272" editTime="86438">
|
||||
<name>Sample Project</name>
|
||||
<author>Jane Smith</author>
|
||||
</project>
|
||||
@@ -20,19 +20,20 @@
|
||||
<entry key="C">D</entry>
|
||||
</autoReplace>
|
||||
<status>
|
||||
<entry key="sf12341" count="8" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="sc24b8f" count="3" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s90e6c9" count="7" red="193" green="129" blue="0">1st Draft</entry>
|
||||
<entry key="sd51c5b" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||
<entry key="s78ea90" count="1" red="58" green="180" blue="58">Finished</entry>
|
||||
<entry key="sf12341" count="8" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
||||
<entry key="sc24b8f" count="3" red="182" green="60" blue="0" shape="BARS_1">Started</entry>
|
||||
<entry key="s90e6c9" count="5" red="193" green="129" blue="0" shape="BARS_2">1st Draft</entry>
|
||||
<entry key="sd51c5b" count="1" red="193" green="129" blue="0" shape="BARS_3">2nd Draft</entry>
|
||||
<entry key="s8ae72a" count="1" red="193" green="129" blue="0" shape="BARS_4">3rd Draft</entry>
|
||||
<entry key="s78ea90" count="1" red="58" green="180" blue="58" shape="STAR">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry>
|
||||
<entry key="icfb3a5" count="2" red="0" green="122" blue="188">Minor</entry>
|
||||
<entry key="i2d7a54" count="2" red="21" green="0" blue="180">Major</entry>
|
||||
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
|
||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
||||
<entry key="i4a1d39" count="1" red="220" green="138" blue="221" shape="BLOCK_1">Background</entry>
|
||||
<entry key="icfb3a5" count="1" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
|
||||
<entry key="i2d7a54" count="2" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
|
||||
<entry key="i56be10" count="1" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="31" novelWords="998" notesWords="416">
|
||||
@@ -57,7 +58,7 @@
|
||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="2937" wordCount="520" paraCount="15" cursorPos="1465" />
|
||||
<meta expanded="no" heading="H3" charCount="2937" wordCount="520" paraCount="15" cursorPos="0" />
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
@@ -78,7 +79,7 @@
|
||||
</item>
|
||||
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="237" />
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">We Found John!</name>
|
||||
<name status="sd51c5b" import="ia857f0" active="yes">We Found John!</name>
|
||||
</item>
|
||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||
<meta expanded="yes" />
|
||||
@@ -90,7 +91,7 @@
|
||||
</item>
|
||||
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="387" />
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Chapter One</name>
|
||||
<name status="s8ae72a" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="yes" />
|
||||
@@ -102,11 +103,11 @@
|
||||
</item>
|
||||
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="15" />
|
||||
<name status="sf12341" import="icfb3a5" active="yes">John Smith</name>
|
||||
<name status="sf12341" import="i2d7a54" active="yes">John Smith</name>
|
||||
</item>
|
||||
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="31" />
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Jane Smith</name>
|
||||
<name status="sf12341" import="i56be10" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||
<meta expanded="yes" />
|
||||
@@ -114,15 +115,15 @@
|
||||
</item>
|
||||
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="111" />
|
||||
<name status="sf12341" import="i56be10" active="yes">Earth</name>
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Earth</name>
|
||||
</item>
|
||||
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="135" />
|
||||
<name status="sf12341" import="icfb3a5" active="yes">Space</name>
|
||||
<name status="sf12341" import="i4a1d39" active="yes">Space</name>
|
||||
</item>
|
||||
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="62" />
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Mars</name>
|
||||
<name status="sf12341" import="icfb3a5" active="yes">Mars</name>
|
||||
</item>
|
||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="yes" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="3" timeStamp="2022-11-07 13:00:48">
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2022-11-07 13:00:48">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="5" autoCount="10" editTime="1000">
|
||||
<name>Sample Project</name>
|
||||
<author>Jane Smith</author>
|
||||
@@ -20,19 +20,19 @@
|
||||
<entry key="C">D</entry>
|
||||
</autoReplace>
|
||||
<status>
|
||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="sc24b8f" count="3" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s90e6c9" count="7" red="193" green="129" blue="0">1st Draft</entry>
|
||||
<entry key="sd51c5b" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||
<entry key="s78ea90" count="1" red="58" green="180" blue="58">Finished</entry>
|
||||
<entry key="sf12341" count="4" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
||||
<entry key="sc24b8f" count="3" red="182" green="60" blue="0" shape="SQUARE">Started</entry>
|
||||
<entry key="s90e6c9" count="7" red="193" green="129" blue="0" shape="SQUARE">1st Draft</entry>
|
||||
<entry key="sd51c5b" count="0" red="193" green="129" blue="0" shape="SQUARE">2nd Draft</entry>
|
||||
<entry key="s8ae72a" count="0" red="193" green="129" blue="0" shape="SQUARE">3rd Draft</entry>
|
||||
<entry key="s78ea90" count="1" red="58" green="180" blue="58" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry>
|
||||
<entry key="icfb3a5" count="2" red="0" green="122" blue="188">Minor</entry>
|
||||
<entry key="i2d7a54" count="2" red="21" green="0" blue="180">Major</entry>
|
||||
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
|
||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
||||
<entry key="icfb3a5" count="2" red="0" green="122" blue="188" shape="SQUARE">Minor</entry>
|
||||
<entry key="i2d7a54" count="2" red="21" green="0" blue="180" shape="SQUARE">Major</entry>
|
||||
<entry key="i56be10" count="1" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="27" novelWords="954" notesWords="409">
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ class MockStatusBar:
|
||||
class MockTheme:
|
||||
|
||||
def __init__(self):
|
||||
self.baseIconHeight = 10
|
||||
self.baseIconHeight = 20
|
||||
return
|
||||
|
||||
def getPixmap(self, *a):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.3a4" hexVersion="0x020300a4" fileVersion="1.5" fileRevision="2" timeStamp="2024-02-08 21:34:07">
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-09 22:34:16">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||
<name>New Project</name>
|
||||
<author>Jane Doe</author>
|
||||
@@ -16,16 +16,16 @@
|
||||
</lastHandle>
|
||||
<autoReplace />
|
||||
<status>
|
||||
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="7" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000004" count="5" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="5" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="12" novelWords="10" notesWords="6">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.3a1" hexVersion="0x020300a1" fileVersion="1.5" fileRevision="2" timeStamp="2024-01-26 22:50:57">
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-09 22:34:16">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||
<name>New Project</name>
|
||||
<author>Jane Doe</author>
|
||||
@@ -16,16 +16,16 @@
|
||||
</lastHandle>
|
||||
<autoReplace />
|
||||
<status>
|
||||
<entry key="s000000" count="6" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="6" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000004" count="10" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="10" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="16" novelWords="9" notesWords="0">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.3a1" hexVersion="0x020300a1" fileVersion="1.5" fileRevision="2" timeStamp="2024-01-26 22:53:40">
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-09 22:34:16">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||
<name>New Project</name>
|
||||
<author>Jane Doe</author>
|
||||
@@ -16,16 +16,16 @@
|
||||
</lastHandle>
|
||||
<autoReplace />
|
||||
<status>
|
||||
<entry key="s000000" count="15" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="15" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000004" count="3" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="3" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="18" novelWords="26" notesWords="0">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.3b1" hexVersion="0x020300b1" fileVersion="1.5" fileRevision="2" timeStamp="2024-02-20 12:52:16">
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-09 22:34:16">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||
<name>Test Project A</name>
|
||||
<author>Jane Doe</author>
|
||||
@@ -16,16 +16,16 @@
|
||||
</lastHandle>
|
||||
<autoReplace />
|
||||
<status>
|
||||
<entry key="s000000" count="15" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="15" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000004" count="7" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="7" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="22" novelWords="0" notesWords="0">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.3b1" hexVersion="0x020300b1" fileVersion="1.5" fileRevision="2" timeStamp="2024-02-20 12:52:16">
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-09 22:34:16">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||
<name>Test Project B</name>
|
||||
<author>Jane Doe</author>
|
||||
@@ -16,16 +16,16 @@
|
||||
</lastHandle>
|
||||
<autoReplace />
|
||||
<status>
|
||||
<entry key="s000000" count="9" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="9" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000004" count="7" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="7" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="16" novelWords="0" notesWords="0">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.3a1" hexVersion="0x020300a1" fileVersion="1.5" fileRevision="2" timeStamp="2024-01-26 22:48:13">
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-09 22:31:18">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="3">
|
||||
<name>New Project</name>
|
||||
<author>Jane Doe</author>
|
||||
@@ -16,16 +16,16 @@
|
||||
</lastHandle>
|
||||
<autoReplace />
|
||||
<status>
|
||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="5" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000004" count="6" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="6" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="11" novelWords="142" notesWords="27">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.3a1" hexVersion="0x020300a1" fileVersion="1.5" fileRevision="2" timeStamp="2024-01-26 22:45:36">
|
||||
<novelWriterXML appVersion="2.4rc1" hexVersion="0x020400c1" fileVersion="1.5" fileRevision="4" timeStamp="2024-04-09 22:31:14">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="2" autoCount="1" editTime="0">
|
||||
<name>New Project</name>
|
||||
<author>Jane Doe</author>
|
||||
@@ -16,16 +16,16 @@
|
||||
</lastHandle>
|
||||
<autoReplace />
|
||||
<status>
|
||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="5" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0" shape="SQUARE">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000004" count="3" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="3" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0" shape="SQUARE">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="8" novelWords="9" notesWords="0">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="3" timeStamp="2020-05-28 09:59:15">
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2020-05-28 09:59:15">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="0" autoCount="0" editTime="1000">
|
||||
<name>Sample Project</name>
|
||||
<author>Jay Doh</author>
|
||||
@@ -20,19 +20,19 @@
|
||||
<entry key="C">D</entry>
|
||||
</autoReplace>
|
||||
<status>
|
||||
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="s000002" count="0" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s000003" count="0" red="193" green="129" blue="0">1st Draft</entry>
|
||||
<entry key="s000004" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||
<entry key="s000005" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||
<entry key="s000006" count="0" red="58" green="180" blue="58">Finished</entry>
|
||||
<entry key="s000000" count="0" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
||||
<entry key="s000002" count="0" red="182" green="60" blue="0" shape="SQUARE">Started</entry>
|
||||
<entry key="s000003" count="0" red="193" green="129" blue="0" shape="SQUARE">1st Draft</entry>
|
||||
<entry key="s000004" count="0" red="193" green="129" blue="0" shape="SQUARE">2nd Draft</entry>
|
||||
<entry key="s000005" count="0" red="193" green="129" blue="0" shape="SQUARE">3rd Draft</entry>
|
||||
<entry key="s000006" count="0" red="58" green="180" blue="58" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000007" count="0" red="100" green="100" blue="100">None</entry>
|
||||
<entry key="i000008" count="0" red="0" green="122" blue="188">Minor</entry>
|
||||
<entry key="i000009" count="0" red="21" green="0" blue="180">Major</entry>
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||
<entry key="i000007" count="0" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
||||
<entry key="i000008" count="0" red="0" green="122" blue="188" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000009" count="0" red="21" green="0" blue="180" shape="SQUARE">Major</entry>
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="22" novelWords="0" notesWords="0">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="3" timeStamp="2020-06-26 21:20:24">
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2020-06-26 21:20:24">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||
<name>Sample Project</name>
|
||||
<author>Jay Doh</author>
|
||||
@@ -20,19 +20,19 @@
|
||||
<entry key="C">D</entry>
|
||||
</autoReplace>
|
||||
<status>
|
||||
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="s000002" count="0" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s000003" count="0" red="193" green="129" blue="0">1st Draft</entry>
|
||||
<entry key="s000004" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||
<entry key="s000005" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||
<entry key="s000006" count="0" red="58" green="180" blue="58">Finished</entry>
|
||||
<entry key="s000000" count="0" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
||||
<entry key="s000002" count="0" red="182" green="60" blue="0" shape="SQUARE">Started</entry>
|
||||
<entry key="s000003" count="0" red="193" green="129" blue="0" shape="SQUARE">1st Draft</entry>
|
||||
<entry key="s000004" count="0" red="193" green="129" blue="0" shape="SQUARE">2nd Draft</entry>
|
||||
<entry key="s000005" count="0" red="193" green="129" blue="0" shape="SQUARE">3rd Draft</entry>
|
||||
<entry key="s000006" count="0" red="58" green="180" blue="58" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000007" count="0" red="100" green="100" blue="100">None</entry>
|
||||
<entry key="i000008" count="0" red="0" green="122" blue="188">Minor</entry>
|
||||
<entry key="i000009" count="0" red="21" green="0" blue="180">Major</entry>
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||
<entry key="i000007" count="0" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
||||
<entry key="i000008" count="0" red="0" green="122" blue="188" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000009" count="0" red="21" green="0" blue="180" shape="SQUARE">Major</entry>
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="22" novelWords="0" notesWords="0">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="3" timeStamp="2021-08-30 23:33:44">
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2021-08-30 23:33:44">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||
<name>Sample Project</name>
|
||||
<author>Jay Doh</author>
|
||||
@@ -20,19 +20,19 @@
|
||||
<entry key="C">D</entry>
|
||||
</autoReplace>
|
||||
<status>
|
||||
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="s000002" count="0" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s000003" count="0" red="193" green="129" blue="0">1st Draft</entry>
|
||||
<entry key="s000004" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||
<entry key="s000005" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||
<entry key="s000006" count="0" red="58" green="180" blue="58">Finished</entry>
|
||||
<entry key="s000000" count="0" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
||||
<entry key="s000002" count="0" red="182" green="60" blue="0" shape="SQUARE">Started</entry>
|
||||
<entry key="s000003" count="0" red="193" green="129" blue="0" shape="SQUARE">1st Draft</entry>
|
||||
<entry key="s000004" count="0" red="193" green="129" blue="0" shape="SQUARE">2nd Draft</entry>
|
||||
<entry key="s000005" count="0" red="193" green="129" blue="0" shape="SQUARE">3rd Draft</entry>
|
||||
<entry key="s000006" count="0" red="58" green="180" blue="58" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000007" count="0" red="100" green="100" blue="100">None</entry>
|
||||
<entry key="i000008" count="0" red="0" green="122" blue="188">Minor</entry>
|
||||
<entry key="i000009" count="0" red="21" green="0" blue="180">Major</entry>
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||
<entry key="i000007" count="0" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
||||
<entry key="i000008" count="0" red="0" green="122" blue="188" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000009" count="0" red="21" green="0" blue="180" shape="SQUARE">Major</entry>
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="25" novelWords="840" notesWords="376">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="3" timeStamp="2022-10-25 18:26:15">
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2022-10-25 18:26:15">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||
<name>Sample Project</name>
|
||||
<author>Jay Doh</author>
|
||||
@@ -20,19 +20,19 @@
|
||||
<entry key="C">D</entry>
|
||||
</autoReplace>
|
||||
<status>
|
||||
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="s000002" count="0" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s000003" count="0" red="193" green="129" blue="0">1st Draft</entry>
|
||||
<entry key="s000004" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||
<entry key="s000005" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||
<entry key="s000006" count="0" red="58" green="180" blue="58">Finished</entry>
|
||||
<entry key="s000000" count="0" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
||||
<entry key="s000002" count="0" red="182" green="60" blue="0" shape="SQUARE">Started</entry>
|
||||
<entry key="s000003" count="0" red="193" green="129" blue="0" shape="SQUARE">1st Draft</entry>
|
||||
<entry key="s000004" count="0" red="193" green="129" blue="0" shape="SQUARE">2nd Draft</entry>
|
||||
<entry key="s000005" count="0" red="193" green="129" blue="0" shape="SQUARE">3rd Draft</entry>
|
||||
<entry key="s000006" count="0" red="58" green="180" blue="58" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i000007" count="0" red="100" green="100" blue="100">None</entry>
|
||||
<entry key="i000008" count="0" red="0" green="122" blue="188">Minor</entry>
|
||||
<entry key="i000009" count="0" red="21" green="0" blue="180">Major</entry>
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||
<entry key="i000007" count="0" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
||||
<entry key="i000008" count="0" red="0" green="122" blue="188" shape="SQUARE">Minor</entry>
|
||||
<entry key="i000009" count="0" red="21" green="0" blue="180" shape="SQUARE">Major</entry>
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="25" novelWords="830" notesWords="376">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="3" timeStamp="2022-10-15 12:12:59">
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2022-10-15 12:12:59">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||
<name>Sample Project</name>
|
||||
<author>Jay Doh</author>
|
||||
@@ -20,19 +20,19 @@
|
||||
<entry key="C">D</entry>
|
||||
</autoReplace>
|
||||
<status>
|
||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
||||
<entry key="sc24b8f" count="3" red="182" green="60" blue="0">Started</entry>
|
||||
<entry key="s90e6c9" count="7" red="193" green="129" blue="0">1st Draft</entry>
|
||||
<entry key="sd51c5b" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||
<entry key="s78ea90" count="1" red="58" green="180" blue="58">Finished</entry>
|
||||
<entry key="sf12341" count="4" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
||||
<entry key="sc24b8f" count="3" red="182" green="60" blue="0" shape="SQUARE">Started</entry>
|
||||
<entry key="s90e6c9" count="7" red="193" green="129" blue="0" shape="SQUARE">1st Draft</entry>
|
||||
<entry key="sd51c5b" count="0" red="193" green="129" blue="0" shape="SQUARE">2nd Draft</entry>
|
||||
<entry key="s8ae72a" count="0" red="193" green="129" blue="0" shape="SQUARE">3rd Draft</entry>
|
||||
<entry key="s78ea90" count="1" red="58" green="180" blue="58" shape="SQUARE">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry>
|
||||
<entry key="icfb3a5" count="2" red="0" green="122" blue="188">Minor</entry>
|
||||
<entry key="i2d7a54" count="2" red="21" green="0" blue="180">Major</entry>
|
||||
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
|
||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
||||
<entry key="icfb3a5" count="2" red="0" green="122" blue="188" shape="SQUARE">Minor</entry>
|
||||
<entry key="i2d7a54" count="2" red="21" green="0" blue="180" shape="SQUARE">Major</entry>
|
||||
<entry key="i56be10" count="1" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="27" novelWords="954" notesWords="409">
|
||||
|
||||
@@ -553,8 +553,8 @@ def testCoreItem_ClassDefaults(mockGUI):
|
||||
def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||
"""Test packing and unpacking entries for the NWItem class."""
|
||||
project = NWProject()
|
||||
project.data.itemStatus.write(None, "New", (100, 100, 100))
|
||||
project.data.itemImport.write(None, "New", (100, 100, 100))
|
||||
project.data.itemStatus.add(None, "New", (100, 100, 100), "SQUARE", 0)
|
||||
project.data.itemImport.add(None, "New", (100, 100, 100), "SQUARE", 0)
|
||||
|
||||
# Invalid
|
||||
item = NWItem(project, "0000000000000")
|
||||
|
||||
@@ -400,115 +400,6 @@ def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
|
||||
# END Test testCoreProject_AccessItems
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
|
||||
"""Test the status and importance flag handling."""
|
||||
project = NWProject()
|
||||
mockRnd.reset()
|
||||
buildTestProject(project, fncPath)
|
||||
|
||||
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
|
||||
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
|
||||
|
||||
# Change Status
|
||||
# =============
|
||||
|
||||
project.tree[C.hNovelRoot].setStatus(statusKeys[3]) # type: ignore
|
||||
project.tree[C.hPlotRoot].setStatus(statusKeys[2]) # type: ignore
|
||||
project.tree[C.hCharRoot].setStatus(statusKeys[1]) # type: ignore
|
||||
project.tree[C.hWorldRoot].setStatus(statusKeys[3]) # type: ignore
|
||||
|
||||
assert project.tree[C.hNovelRoot].itemStatus == statusKeys[3] # type: ignore
|
||||
assert project.tree[C.hPlotRoot].itemStatus == statusKeys[2] # type: ignore
|
||||
assert project.tree[C.hCharRoot].itemStatus == statusKeys[1] # type: ignore
|
||||
assert project.tree[C.hWorldRoot].itemStatus == statusKeys[3] # type: ignore
|
||||
|
||||
newList = [
|
||||
{"key": statusKeys[0], "name": "New", "cols": (1, 1, 1)},
|
||||
{"key": statusKeys[1], "name": "Draft", "cols": (2, 2, 2)}, # These are swapped
|
||||
{"key": statusKeys[2], "name": "Note", "cols": (3, 3, 3)}, # These are swapped
|
||||
{"key": statusKeys[3], "name": "Edited", "cols": (4, 4, 4)}, # Renamed
|
||||
{"key": None, "name": "Finished", "cols": (5, 5, 5)}, # New, reused name
|
||||
]
|
||||
assert project.setStatusColours(None, None) is False # type: ignore
|
||||
assert project.setStatusColours([], []) is False
|
||||
assert project.setStatusColours(newList, []) is True
|
||||
|
||||
assert project.data.itemStatus.name(statusKeys[0]) == "New"
|
||||
assert project.data.itemStatus.name(statusKeys[1]) == "Draft"
|
||||
assert project.data.itemStatus.name(statusKeys[2]) == "Note"
|
||||
assert project.data.itemStatus.name(statusKeys[3]) == "Edited"
|
||||
assert project.data.itemStatus.cols(statusKeys[0]) == (1, 1, 1)
|
||||
assert project.data.itemStatus.cols(statusKeys[1]) == (2, 2, 2)
|
||||
assert project.data.itemStatus.cols(statusKeys[2]) == (3, 3, 3)
|
||||
assert project.data.itemStatus.cols(statusKeys[3]) == (4, 4, 4)
|
||||
|
||||
# Check the new entry
|
||||
lastKey = project.data.itemStatus.check("s000010")
|
||||
assert lastKey == "s000010"
|
||||
assert project.data.itemStatus.name(lastKey) == "Finished"
|
||||
assert project.data.itemStatus.cols(lastKey) == (5, 5, 5)
|
||||
|
||||
# Delete last entry
|
||||
assert project.setStatusColours([], [lastKey]) is True
|
||||
assert project.data.itemStatus.name(lastKey) == "New"
|
||||
|
||||
# Change Importance
|
||||
# =================
|
||||
|
||||
fHandle = project.newFile("Jane Doe", C.hCharRoot)
|
||||
project.tree[fHandle].setImport(importKeys[3]) # type: ignore
|
||||
|
||||
assert project.tree[fHandle].itemImport == importKeys[3] # type: ignore
|
||||
newList = [
|
||||
{"key": importKeys[0], "name": "New", "cols": (1, 1, 1)},
|
||||
{"key": importKeys[1], "name": "Minor", "cols": (2, 2, 2)},
|
||||
{"key": importKeys[2], "name": "Major", "cols": (3, 3, 3)},
|
||||
{"key": importKeys[3], "name": "Min", "cols": (4, 4, 4)},
|
||||
{"key": None, "name": "Max", "cols": (5, 5, 5)},
|
||||
]
|
||||
assert project.setImportColours(None, None) is False # type: ignore
|
||||
assert project.setImportColours([], []) is False
|
||||
assert project.setImportColours(newList, []) is True
|
||||
|
||||
assert project.data.itemImport.name(importKeys[0]) == "New"
|
||||
assert project.data.itemImport.name(importKeys[1]) == "Minor"
|
||||
assert project.data.itemImport.name(importKeys[2]) == "Major"
|
||||
assert project.data.itemImport.name(importKeys[3]) == "Min"
|
||||
assert project.data.itemImport.cols(importKeys[0]) == (1, 1, 1)
|
||||
assert project.data.itemImport.cols(importKeys[1]) == (2, 2, 2)
|
||||
assert project.data.itemImport.cols(importKeys[2]) == (3, 3, 3)
|
||||
assert project.data.itemImport.cols(importKeys[3]) == (4, 4, 4)
|
||||
|
||||
# Check the new entry
|
||||
lastKey = project.data.itemImport.check("i000012")
|
||||
assert lastKey == "i000012"
|
||||
assert project.data.itemImport.name(lastKey) == "Max"
|
||||
assert project.data.itemImport.cols(lastKey) == (5, 5, 5)
|
||||
|
||||
# Delete last entry
|
||||
assert project.setImportColours([], [lastKey]) is True
|
||||
assert project.data.itemImport.name(lastKey) == "New"
|
||||
|
||||
# Delete Status/Import
|
||||
# ====================
|
||||
|
||||
project.data.itemStatus.resetCounts()
|
||||
for key in list(project.data.itemStatus.keys()):
|
||||
assert project.data.itemStatus.remove(key) is True
|
||||
|
||||
project.data.itemImport.resetCounts()
|
||||
for key in list(project.data.itemImport.keys()):
|
||||
assert project.data.itemImport.remove(key) is True
|
||||
|
||||
assert len(project.data.itemStatus) == 0
|
||||
assert len(project.data.itemImport) == 0
|
||||
assert project.saveProject() is True
|
||||
project.closeProject()
|
||||
|
||||
# END Test testCoreProject_StatusImport
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
|
||||
"""Test other project class methods and functions."""
|
||||
|
||||
@@ -27,9 +27,12 @@ from shutil import copyfile
|
||||
from datetime import datetime
|
||||
from novelwriter.constants import nwFiles
|
||||
|
||||
from novelwriter.enum import nwStatusShape
|
||||
from tools import cmpFiles, writeFile
|
||||
from mocked import causeOSError
|
||||
|
||||
from PyQt5.QtGui import QColor
|
||||
|
||||
from novelwriter.core.item import NWItem
|
||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
||||
from novelwriter.core.projectdata import NWProjectData
|
||||
@@ -131,7 +134,7 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, tstPaths, fncPath):
|
||||
assert xmlReader.state == XMLReadState.PARSED_OK
|
||||
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||
assert xmlReader.xmlVersion == 0x0105
|
||||
assert xmlReader.xmlRevision == 3
|
||||
assert xmlReader.xmlRevision == 4
|
||||
assert xmlReader.appVersion == "2.0-rc1"
|
||||
assert xmlReader.hexVersion == 0x020000c1
|
||||
|
||||
@@ -154,44 +157,57 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, tstPaths, fncPath):
|
||||
assert data.getLastHandle("novelTree") == "7031beac91f75"
|
||||
assert data.getLastHandle("outline") == "7031beac91f75"
|
||||
|
||||
assert data.itemStatus.name("sf12341") == "New"
|
||||
assert data.itemStatus.name("sf24ce6") == "Notes"
|
||||
assert data.itemStatus.name("sc24b8f") == "Started"
|
||||
assert data.itemStatus.name("s90e6c9") == "1st Draft"
|
||||
assert data.itemStatus.name("sd51c5b") == "2nd Draft"
|
||||
assert data.itemStatus.name("s8ae72a") == "3rd Draft"
|
||||
assert data.itemStatus.name("s78ea90") == "Finished"
|
||||
assert data.itemStatus["sf12341"].name == "New"
|
||||
assert data.itemStatus["sf24ce6"].name == "Notes"
|
||||
assert data.itemStatus["sc24b8f"].name == "Started"
|
||||
assert data.itemStatus["s90e6c9"].name == "1st Draft"
|
||||
assert data.itemStatus["sd51c5b"].name == "2nd Draft"
|
||||
assert data.itemStatus["s8ae72a"].name == "3rd Draft"
|
||||
assert data.itemStatus["s78ea90"].name == "Finished"
|
||||
|
||||
assert data.itemImport.name("ia857f0") == "None"
|
||||
assert data.itemImport.name("icfb3a5") == "Minor"
|
||||
assert data.itemImport.name("i2d7a54") == "Major"
|
||||
assert data.itemImport.name("i56be10") == "Main"
|
||||
assert data.itemImport["ia857f0"].name == "None"
|
||||
assert data.itemImport["icfb3a5"].name == "Minor"
|
||||
assert data.itemImport["i2d7a54"].name == "Major"
|
||||
assert data.itemImport["i56be10"].name == "Main"
|
||||
|
||||
assert data.itemStatus.cols("sf12341") == (100, 100, 100)
|
||||
assert data.itemStatus.cols("sf24ce6") == (200, 50, 0)
|
||||
assert data.itemStatus.cols("sc24b8f") == (182, 60, 0)
|
||||
assert data.itemStatus.cols("s90e6c9") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("sd51c5b") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s8ae72a") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s78ea90") == (58, 180, 58)
|
||||
assert data.itemStatus["sf12341"].color == QColor(100, 100, 100)
|
||||
assert data.itemStatus["sf24ce6"].color == QColor(200, 50, 0)
|
||||
assert data.itemStatus["sc24b8f"].color == QColor(182, 60, 0)
|
||||
assert data.itemStatus["s90e6c9"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["sd51c5b"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s8ae72a"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s78ea90"].color == QColor(58, 180, 58)
|
||||
|
||||
assert data.itemImport.cols("ia857f0") == (100, 100, 100)
|
||||
assert data.itemImport.cols("icfb3a5") == (0, 122, 188)
|
||||
assert data.itemImport.cols("i2d7a54") == (21, 0, 180)
|
||||
assert data.itemImport.cols("i56be10") == (117, 0, 175)
|
||||
assert data.itemImport["ia857f0"].color == QColor(100, 100, 100)
|
||||
assert data.itemImport["icfb3a5"].color == QColor(0, 122, 188)
|
||||
assert data.itemImport["i2d7a54"].color == QColor(21, 0, 180)
|
||||
assert data.itemImport["i56be10"].color == QColor(117, 0, 175)
|
||||
|
||||
assert data.itemStatus.count("sf12341") == 4
|
||||
assert data.itemStatus.count("sf24ce6") == 2
|
||||
assert data.itemStatus.count("sc24b8f") == 3
|
||||
assert data.itemStatus.count("s90e6c9") == 7
|
||||
assert data.itemStatus.count("sd51c5b") == 0
|
||||
assert data.itemStatus.count("s8ae72a") == 0
|
||||
assert data.itemStatus.count("s78ea90") == 1
|
||||
assert data.itemStatus["sf12341"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["sf24ce6"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["sc24b8f"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s90e6c9"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["sd51c5b"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s8ae72a"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s78ea90"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemImport.count("ia857f0") == 5
|
||||
assert data.itemImport.count("icfb3a5") == 2
|
||||
assert data.itemImport.count("i2d7a54") == 2
|
||||
assert data.itemImport.count("i56be10") == 1
|
||||
assert data.itemImport["ia857f0"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["icfb3a5"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i2d7a54"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i56be10"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemStatus["sf12341"].count == 4
|
||||
assert data.itemStatus["sf24ce6"].count == 2
|
||||
assert data.itemStatus["sc24b8f"].count == 3
|
||||
assert data.itemStatus["s90e6c9"].count == 7
|
||||
assert data.itemStatus["sd51c5b"].count == 0
|
||||
assert data.itemStatus["s8ae72a"].count == 0
|
||||
assert data.itemStatus["s78ea90"].count == 1
|
||||
|
||||
assert data.itemImport["ia857f0"].count == 5
|
||||
assert data.itemImport["icfb3a5"].count == 2
|
||||
assert data.itemImport["i2d7a54"].count == 2
|
||||
assert data.itemImport["i56be10"].count == 1
|
||||
|
||||
# Compare content
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadCurrent.json"
|
||||
@@ -272,44 +288,57 @@ def testCoreProjectXML_ReadLegacy10(tstPaths, fncPath, mockRnd):
|
||||
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.0
|
||||
assert data.getLastHandle("outline") is None # Doesn't exist in 1.0
|
||||
|
||||
assert data.itemStatus.name("s000000") == "New"
|
||||
assert data.itemStatus.name("s000001") == "Notes"
|
||||
assert data.itemStatus.name("s000002") == "Started"
|
||||
assert data.itemStatus.name("s000003") == "1st Draft"
|
||||
assert data.itemStatus.name("s000004") == "2nd Draft"
|
||||
assert data.itemStatus.name("s000005") == "3rd Draft"
|
||||
assert data.itemStatus.name("s000006") == "Finished"
|
||||
assert data.itemStatus["s000000"].name == "New"
|
||||
assert data.itemStatus["s000001"].name == "Notes"
|
||||
assert data.itemStatus["s000002"].name == "Started"
|
||||
assert data.itemStatus["s000003"].name == "1st Draft"
|
||||
assert data.itemStatus["s000004"].name == "2nd Draft"
|
||||
assert data.itemStatus["s000005"].name == "3rd Draft"
|
||||
assert data.itemStatus["s000006"].name == "Finished"
|
||||
|
||||
assert data.itemImport.name("i000007") == "None"
|
||||
assert data.itemImport.name("i000008") == "Minor"
|
||||
assert data.itemImport.name("i000009") == "Major"
|
||||
assert data.itemImport.name("i00000a") == "Main"
|
||||
assert data.itemImport["i000007"].name == "None"
|
||||
assert data.itemImport["i000008"].name == "Minor"
|
||||
assert data.itemImport["i000009"].name == "Major"
|
||||
assert data.itemImport["i00000a"].name == "Main"
|
||||
|
||||
assert data.itemStatus.cols("s000000") == (100, 100, 100)
|
||||
assert data.itemStatus.cols("s000001") == (200, 50, 0)
|
||||
assert data.itemStatus.cols("s000002") == (182, 60, 0)
|
||||
assert data.itemStatus.cols("s000003") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000004") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000005") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000006") == (58, 180, 58)
|
||||
assert data.itemStatus["s000000"].color == QColor(100, 100, 100)
|
||||
assert data.itemStatus["s000001"].color == QColor(200, 50, 0)
|
||||
assert data.itemStatus["s000002"].color == QColor(182, 60, 0)
|
||||
assert data.itemStatus["s000003"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000004"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000005"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000006"].color == QColor(58, 180, 58)
|
||||
|
||||
assert data.itemImport.cols("i000007") == (100, 100, 100)
|
||||
assert data.itemImport.cols("i000008") == (0, 122, 188)
|
||||
assert data.itemImport.cols("i000009") == (21, 0, 180)
|
||||
assert data.itemImport.cols("i00000a") == (117, 0, 175)
|
||||
assert data.itemImport["i000007"].color == QColor(100, 100, 100)
|
||||
assert data.itemImport["i000008"].color == QColor(0, 122, 188)
|
||||
assert data.itemImport["i000009"].color == QColor(21, 0, 180)
|
||||
assert data.itemImport["i00000a"].color == QColor(117, 0, 175)
|
||||
|
||||
assert data.itemStatus.count("s000000") == 0
|
||||
assert data.itemStatus.count("s000001") == 0
|
||||
assert data.itemStatus.count("s000002") == 0
|
||||
assert data.itemStatus.count("s000003") == 0
|
||||
assert data.itemStatus.count("s000004") == 0
|
||||
assert data.itemStatus.count("s000005") == 0
|
||||
assert data.itemStatus.count("s000006") == 0
|
||||
assert data.itemStatus["s000000"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000001"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000002"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000003"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000004"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000005"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000006"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemImport.count("i000007") == 0
|
||||
assert data.itemImport.count("i000008") == 0
|
||||
assert data.itemImport.count("i000009") == 0
|
||||
assert data.itemImport.count("i00000a") == 0
|
||||
assert data.itemImport["i000007"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i000008"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i000009"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i00000a"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemStatus["s000000"].count == 0
|
||||
assert data.itemStatus["s000001"].count == 0
|
||||
assert data.itemStatus["s000002"].count == 0
|
||||
assert data.itemStatus["s000003"].count == 0
|
||||
assert data.itemStatus["s000004"].count == 0
|
||||
assert data.itemStatus["s000005"].count == 0
|
||||
assert data.itemStatus["s000006"].count == 0
|
||||
|
||||
assert data.itemImport["i000007"].count == 0
|
||||
assert data.itemImport["i000008"].count == 0
|
||||
assert data.itemImport["i000009"].count == 0
|
||||
assert data.itemImport["i00000a"].count == 0
|
||||
|
||||
# Compare content
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy10.json"
|
||||
@@ -325,7 +354,7 @@ def testCoreProjectXML_ReadLegacy10(tstPaths, fncPath, mockRnd):
|
||||
for entry in content:
|
||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||
item.unpack(entry)
|
||||
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||
status[item.itemHandle] = item.getImportStatus()[0]
|
||||
packedContent.append(item.pack())
|
||||
|
||||
assert status == {
|
||||
@@ -406,44 +435,57 @@ def testCoreProjectXML_ReadLegacy11(tstPaths, fncPath, mockRnd):
|
||||
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.1
|
||||
assert data.getLastHandle("outline") is None # Doesn't exist in 1.1
|
||||
|
||||
assert data.itemStatus.name("s000000") == "New"
|
||||
assert data.itemStatus.name("s000001") == "Notes"
|
||||
assert data.itemStatus.name("s000002") == "Started"
|
||||
assert data.itemStatus.name("s000003") == "1st Draft"
|
||||
assert data.itemStatus.name("s000004") == "2nd Draft"
|
||||
assert data.itemStatus.name("s000005") == "3rd Draft"
|
||||
assert data.itemStatus.name("s000006") == "Finished"
|
||||
assert data.itemStatus["s000000"].name == "New"
|
||||
assert data.itemStatus["s000001"].name == "Notes"
|
||||
assert data.itemStatus["s000002"].name == "Started"
|
||||
assert data.itemStatus["s000003"].name == "1st Draft"
|
||||
assert data.itemStatus["s000004"].name == "2nd Draft"
|
||||
assert data.itemStatus["s000005"].name == "3rd Draft"
|
||||
assert data.itemStatus["s000006"].name == "Finished"
|
||||
|
||||
assert data.itemImport.name("i000007") == "None"
|
||||
assert data.itemImport.name("i000008") == "Minor"
|
||||
assert data.itemImport.name("i000009") == "Major"
|
||||
assert data.itemImport.name("i00000a") == "Main"
|
||||
assert data.itemImport["i000007"].name == "None"
|
||||
assert data.itemImport["i000008"].name == "Minor"
|
||||
assert data.itemImport["i000009"].name == "Major"
|
||||
assert data.itemImport["i00000a"].name == "Main"
|
||||
|
||||
assert data.itemStatus.cols("s000000") == (100, 100, 100)
|
||||
assert data.itemStatus.cols("s000001") == (200, 50, 0)
|
||||
assert data.itemStatus.cols("s000002") == (182, 60, 0)
|
||||
assert data.itemStatus.cols("s000003") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000004") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000005") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000006") == (58, 180, 58)
|
||||
assert data.itemStatus["s000000"].color == QColor(100, 100, 100)
|
||||
assert data.itemStatus["s000001"].color == QColor(200, 50, 0)
|
||||
assert data.itemStatus["s000002"].color == QColor(182, 60, 0)
|
||||
assert data.itemStatus["s000003"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000004"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000005"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000006"].color == QColor(58, 180, 58)
|
||||
|
||||
assert data.itemImport.cols("i000007") == (100, 100, 100)
|
||||
assert data.itemImport.cols("i000008") == (0, 122, 188)
|
||||
assert data.itemImport.cols("i000009") == (21, 0, 180)
|
||||
assert data.itemImport.cols("i00000a") == (117, 0, 175)
|
||||
assert data.itemImport["i000007"].color == QColor(100, 100, 100)
|
||||
assert data.itemImport["i000008"].color == QColor(0, 122, 188)
|
||||
assert data.itemImport["i000009"].color == QColor(21, 0, 180)
|
||||
assert data.itemImport["i00000a"].color == QColor(117, 0, 175)
|
||||
|
||||
assert data.itemStatus.count("s000000") == 0
|
||||
assert data.itemStatus.count("s000001") == 0
|
||||
assert data.itemStatus.count("s000002") == 0
|
||||
assert data.itemStatus.count("s000003") == 0
|
||||
assert data.itemStatus.count("s000004") == 0
|
||||
assert data.itemStatus.count("s000005") == 0
|
||||
assert data.itemStatus.count("s000006") == 0
|
||||
assert data.itemStatus["s000000"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000001"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000002"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000003"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000004"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000005"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000006"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemImport.count("i000007") == 0
|
||||
assert data.itemImport.count("i000008") == 0
|
||||
assert data.itemImport.count("i000009") == 0
|
||||
assert data.itemImport.count("i00000a") == 0
|
||||
assert data.itemImport["i000007"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i000008"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i000009"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i00000a"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemStatus["s000000"].count == 0
|
||||
assert data.itemStatus["s000001"].count == 0
|
||||
assert data.itemStatus["s000002"].count == 0
|
||||
assert data.itemStatus["s000003"].count == 0
|
||||
assert data.itemStatus["s000004"].count == 0
|
||||
assert data.itemStatus["s000005"].count == 0
|
||||
assert data.itemStatus["s000006"].count == 0
|
||||
|
||||
assert data.itemImport["i000007"].count == 0
|
||||
assert data.itemImport["i000008"].count == 0
|
||||
assert data.itemImport["i000009"].count == 0
|
||||
assert data.itemImport["i00000a"].count == 0
|
||||
|
||||
# Compare content
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy11.json"
|
||||
@@ -459,7 +501,7 @@ def testCoreProjectXML_ReadLegacy11(tstPaths, fncPath, mockRnd):
|
||||
for entry in content:
|
||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||
item.unpack(entry)
|
||||
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||
status[item.itemHandle] = item.getImportStatus()[0]
|
||||
packedContent.append(item.pack())
|
||||
|
||||
assert status == {
|
||||
@@ -540,44 +582,57 @@ def testCoreProjectXML_ReadLegacy12(tstPaths, fncPath, mockRnd):
|
||||
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.2
|
||||
assert data.getLastHandle("outline") is None # Doesn't exist in 1.2
|
||||
|
||||
assert data.itemStatus.name("s000000") == "New"
|
||||
assert data.itemStatus.name("s000001") == "Notes"
|
||||
assert data.itemStatus.name("s000002") == "Started"
|
||||
assert data.itemStatus.name("s000003") == "1st Draft"
|
||||
assert data.itemStatus.name("s000004") == "2nd Draft"
|
||||
assert data.itemStatus.name("s000005") == "3rd Draft"
|
||||
assert data.itemStatus.name("s000006") == "Finished"
|
||||
assert data.itemStatus["s000000"].name == "New"
|
||||
assert data.itemStatus["s000001"].name == "Notes"
|
||||
assert data.itemStatus["s000002"].name == "Started"
|
||||
assert data.itemStatus["s000003"].name == "1st Draft"
|
||||
assert data.itemStatus["s000004"].name == "2nd Draft"
|
||||
assert data.itemStatus["s000005"].name == "3rd Draft"
|
||||
assert data.itemStatus["s000006"].name == "Finished"
|
||||
|
||||
assert data.itemImport.name("i000007") == "None"
|
||||
assert data.itemImport.name("i000008") == "Minor"
|
||||
assert data.itemImport.name("i000009") == "Major"
|
||||
assert data.itemImport.name("i00000a") == "Main"
|
||||
assert data.itemImport["i000007"].name == "None"
|
||||
assert data.itemImport["i000008"].name == "Minor"
|
||||
assert data.itemImport["i000009"].name == "Major"
|
||||
assert data.itemImport["i00000a"].name == "Main"
|
||||
|
||||
assert data.itemStatus.cols("s000000") == (100, 100, 100)
|
||||
assert data.itemStatus.cols("s000001") == (200, 50, 0)
|
||||
assert data.itemStatus.cols("s000002") == (182, 60, 0)
|
||||
assert data.itemStatus.cols("s000003") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000004") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000005") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000006") == (58, 180, 58)
|
||||
assert data.itemStatus["s000000"].color == QColor(100, 100, 100)
|
||||
assert data.itemStatus["s000001"].color == QColor(200, 50, 0)
|
||||
assert data.itemStatus["s000002"].color == QColor(182, 60, 0)
|
||||
assert data.itemStatus["s000003"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000004"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000005"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000006"].color == QColor(58, 180, 58)
|
||||
|
||||
assert data.itemImport.cols("i000007") == (100, 100, 100)
|
||||
assert data.itemImport.cols("i000008") == (0, 122, 188)
|
||||
assert data.itemImport.cols("i000009") == (21, 0, 180)
|
||||
assert data.itemImport.cols("i00000a") == (117, 0, 175)
|
||||
assert data.itemImport["i000007"].color == QColor(100, 100, 100)
|
||||
assert data.itemImport["i000008"].color == QColor(0, 122, 188)
|
||||
assert data.itemImport["i000009"].color == QColor(21, 0, 180)
|
||||
assert data.itemImport["i00000a"].color == QColor(117, 0, 175)
|
||||
|
||||
assert data.itemStatus.count("s000000") == 0
|
||||
assert data.itemStatus.count("s000001") == 0
|
||||
assert data.itemStatus.count("s000002") == 0
|
||||
assert data.itemStatus.count("s000003") == 0
|
||||
assert data.itemStatus.count("s000004") == 0
|
||||
assert data.itemStatus.count("s000005") == 0
|
||||
assert data.itemStatus.count("s000006") == 0
|
||||
assert data.itemStatus["s000000"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000001"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000002"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000003"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000004"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000005"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000006"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemImport.count("i000007") == 0
|
||||
assert data.itemImport.count("i000008") == 0
|
||||
assert data.itemImport.count("i000009") == 0
|
||||
assert data.itemImport.count("i00000a") == 0
|
||||
assert data.itemImport["i000007"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i000008"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i000009"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i00000a"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemStatus["s000000"].count == 0
|
||||
assert data.itemStatus["s000001"].count == 0
|
||||
assert data.itemStatus["s000002"].count == 0
|
||||
assert data.itemStatus["s000003"].count == 0
|
||||
assert data.itemStatus["s000004"].count == 0
|
||||
assert data.itemStatus["s000005"].count == 0
|
||||
assert data.itemStatus["s000006"].count == 0
|
||||
|
||||
assert data.itemImport["i000007"].count == 0
|
||||
assert data.itemImport["i000008"].count == 0
|
||||
assert data.itemImport["i000009"].count == 0
|
||||
assert data.itemImport["i00000a"].count == 0
|
||||
|
||||
# Compare content
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy12.json"
|
||||
@@ -593,7 +648,7 @@ def testCoreProjectXML_ReadLegacy12(tstPaths, fncPath, mockRnd):
|
||||
for entry in content:
|
||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||
item.unpack(entry)
|
||||
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||
status[item.itemHandle] = item.getImportStatus()[0]
|
||||
packedContent.append(item.pack())
|
||||
|
||||
assert status == {
|
||||
@@ -677,44 +732,57 @@ def testCoreProjectXML_ReadLegacy13(tstPaths, fncPath, mockRnd):
|
||||
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.3
|
||||
assert data.getLastHandle("outline") is None # Doesn't exist in 1.3
|
||||
|
||||
assert data.itemStatus.name("s000000") == "New"
|
||||
assert data.itemStatus.name("s000001") == "Notes"
|
||||
assert data.itemStatus.name("s000002") == "Started"
|
||||
assert data.itemStatus.name("s000003") == "1st Draft"
|
||||
assert data.itemStatus.name("s000004") == "2nd Draft"
|
||||
assert data.itemStatus.name("s000005") == "3rd Draft"
|
||||
assert data.itemStatus.name("s000006") == "Finished"
|
||||
assert data.itemStatus["s000000"].name == "New"
|
||||
assert data.itemStatus["s000001"].name == "Notes"
|
||||
assert data.itemStatus["s000002"].name == "Started"
|
||||
assert data.itemStatus["s000003"].name == "1st Draft"
|
||||
assert data.itemStatus["s000004"].name == "2nd Draft"
|
||||
assert data.itemStatus["s000005"].name == "3rd Draft"
|
||||
assert data.itemStatus["s000006"].name == "Finished"
|
||||
|
||||
assert data.itemImport.name("i000007") == "None"
|
||||
assert data.itemImport.name("i000008") == "Minor"
|
||||
assert data.itemImport.name("i000009") == "Major"
|
||||
assert data.itemImport.name("i00000a") == "Main"
|
||||
assert data.itemImport["i000007"].name == "None"
|
||||
assert data.itemImport["i000008"].name == "Minor"
|
||||
assert data.itemImport["i000009"].name == "Major"
|
||||
assert data.itemImport["i00000a"].name == "Main"
|
||||
|
||||
assert data.itemStatus.cols("s000000") == (100, 100, 100)
|
||||
assert data.itemStatus.cols("s000001") == (200, 50, 0)
|
||||
assert data.itemStatus.cols("s000002") == (182, 60, 0)
|
||||
assert data.itemStatus.cols("s000003") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000004") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000005") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s000006") == (58, 180, 58)
|
||||
assert data.itemStatus["s000000"].color == QColor(100, 100, 100)
|
||||
assert data.itemStatus["s000001"].color == QColor(200, 50, 0)
|
||||
assert data.itemStatus["s000002"].color == QColor(182, 60, 0)
|
||||
assert data.itemStatus["s000003"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000004"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000005"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s000006"].color == QColor(58, 180, 58)
|
||||
|
||||
assert data.itemImport.cols("i000007") == (100, 100, 100)
|
||||
assert data.itemImport.cols("i000008") == (0, 122, 188)
|
||||
assert data.itemImport.cols("i000009") == (21, 0, 180)
|
||||
assert data.itemImport.cols("i00000a") == (117, 0, 175)
|
||||
assert data.itemImport["i000007"].color == QColor(100, 100, 100)
|
||||
assert data.itemImport["i000008"].color == QColor(0, 122, 188)
|
||||
assert data.itemImport["i000009"].color == QColor(21, 0, 180)
|
||||
assert data.itemImport["i00000a"].color == QColor(117, 0, 175)
|
||||
|
||||
assert data.itemStatus.count("s000000") == 0
|
||||
assert data.itemStatus.count("s000001") == 0
|
||||
assert data.itemStatus.count("s000002") == 0
|
||||
assert data.itemStatus.count("s000003") == 0
|
||||
assert data.itemStatus.count("s000004") == 0
|
||||
assert data.itemStatus.count("s000005") == 0
|
||||
assert data.itemStatus.count("s000006") == 0
|
||||
assert data.itemStatus["s000000"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000001"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000002"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000003"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000004"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000005"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s000006"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemImport.count("i000007") == 0
|
||||
assert data.itemImport.count("i000008") == 0
|
||||
assert data.itemImport.count("i000009") == 0
|
||||
assert data.itemImport.count("i00000a") == 0
|
||||
assert data.itemImport["i000007"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i000008"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i000009"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i00000a"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemStatus["s000000"].count == 0
|
||||
assert data.itemStatus["s000001"].count == 0
|
||||
assert data.itemStatus["s000002"].count == 0
|
||||
assert data.itemStatus["s000003"].count == 0
|
||||
assert data.itemStatus["s000004"].count == 0
|
||||
assert data.itemStatus["s000005"].count == 0
|
||||
assert data.itemStatus["s000006"].count == 0
|
||||
|
||||
assert data.itemImport["i000007"].count == 0
|
||||
assert data.itemImport["i000008"].count == 0
|
||||
assert data.itemImport["i000009"].count == 0
|
||||
assert data.itemImport["i00000a"].count == 0
|
||||
|
||||
# Compare content
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy13.json"
|
||||
@@ -730,7 +798,7 @@ def testCoreProjectXML_ReadLegacy13(tstPaths, fncPath, mockRnd):
|
||||
for entry in content:
|
||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||
item.unpack(entry)
|
||||
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||
status[item.itemHandle] = item.getImportStatus()[0]
|
||||
packedContent.append(item.pack())
|
||||
|
||||
assert status == {
|
||||
@@ -814,44 +882,56 @@ def testCoreProjectXML_ReadLegacy14(tstPaths, fncPath, mockRnd):
|
||||
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.3
|
||||
assert data.getLastHandle("outline") is None # Doesn't exist in 1.3
|
||||
|
||||
assert data.itemStatus.name("sf12341") == "New"
|
||||
assert data.itemStatus.name("sf24ce6") == "Notes"
|
||||
assert data.itemStatus.name("sc24b8f") == "Started"
|
||||
assert data.itemStatus.name("s90e6c9") == "1st Draft"
|
||||
assert data.itemStatus.name("sd51c5b") == "2nd Draft"
|
||||
assert data.itemStatus.name("s8ae72a") == "3rd Draft"
|
||||
assert data.itemStatus.name("s78ea90") == "Finished"
|
||||
assert data.itemStatus["sf12341"].name == "New"
|
||||
assert data.itemStatus["sf24ce6"].name == "Notes"
|
||||
assert data.itemStatus["sc24b8f"].name == "Started"
|
||||
assert data.itemStatus["s90e6c9"].name == "1st Draft"
|
||||
assert data.itemStatus["sd51c5b"].name == "2nd Draft"
|
||||
assert data.itemStatus["s8ae72a"].name == "3rd Draft"
|
||||
assert data.itemStatus["s78ea90"].name == "Finished"
|
||||
|
||||
assert data.itemImport.name("ia857f0") == "None"
|
||||
assert data.itemImport.name("icfb3a5") == "Minor"
|
||||
assert data.itemImport.name("i2d7a54") == "Major"
|
||||
assert data.itemImport.name("i56be10") == "Main"
|
||||
assert data.itemImport["ia857f0"].name == "None"
|
||||
assert data.itemImport["icfb3a5"].name == "Minor"
|
||||
assert data.itemImport["i2d7a54"].name == "Major"
|
||||
assert data.itemImport["i56be10"].name == "Main"
|
||||
|
||||
assert data.itemStatus.cols("sf12341") == (100, 100, 100)
|
||||
assert data.itemStatus.cols("sf24ce6") == (200, 50, 0)
|
||||
assert data.itemStatus.cols("sc24b8f") == (182, 60, 0)
|
||||
assert data.itemStatus.cols("s90e6c9") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("sd51c5b") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s8ae72a") == (193, 129, 0)
|
||||
assert data.itemStatus.cols("s78ea90") == (58, 180, 58)
|
||||
assert data.itemStatus["sf12341"].color == QColor(100, 100, 100)
|
||||
assert data.itemStatus["sf24ce6"].color == QColor(200, 50, 0)
|
||||
assert data.itemStatus["sc24b8f"].color == QColor(182, 60, 0)
|
||||
assert data.itemStatus["s90e6c9"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["sd51c5b"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s8ae72a"].color == QColor(193, 129, 0)
|
||||
assert data.itemStatus["s78ea90"].color == QColor(58, 180, 58)
|
||||
|
||||
assert data.itemImport.cols("ia857f0") == (100, 100, 100)
|
||||
assert data.itemImport.cols("icfb3a5") == (0, 122, 188)
|
||||
assert data.itemImport.cols("i2d7a54") == (21, 0, 180)
|
||||
assert data.itemImport.cols("i56be10") == (117, 0, 175)
|
||||
assert data.itemImport["ia857f0"].color == QColor(100, 100, 100)
|
||||
assert data.itemImport["icfb3a5"].color == QColor(0, 122, 188)
|
||||
assert data.itemImport["i2d7a54"].color == QColor(21, 0, 180)
|
||||
assert data.itemImport["i56be10"].color == QColor(117, 0, 175)
|
||||
|
||||
assert data.itemStatus.count("sf12341") == 4
|
||||
assert data.itemStatus.count("sf24ce6") == 2
|
||||
assert data.itemStatus.count("sc24b8f") == 3
|
||||
assert data.itemStatus.count("s90e6c9") == 7
|
||||
assert data.itemStatus.count("sd51c5b") == 0
|
||||
assert data.itemStatus.count("s8ae72a") == 0
|
||||
assert data.itemStatus.count("s78ea90") == 1
|
||||
assert data.itemStatus["sf12341"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["sf24ce6"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["sc24b8f"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s90e6c9"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["sd51c5b"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s8ae72a"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemStatus["s78ea90"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemImport.count("ia857f0") == 5
|
||||
assert data.itemImport.count("icfb3a5") == 2
|
||||
assert data.itemImport.count("i2d7a54") == 2
|
||||
assert data.itemImport.count("i56be10") == 1
|
||||
assert data.itemImport["ia857f0"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["icfb3a5"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i2d7a54"].shape == nwStatusShape.SQUARE
|
||||
assert data.itemImport["i56be10"].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert data.itemStatus["sf12341"].count == 4
|
||||
assert data.itemStatus["sf24ce6"].count == 2
|
||||
assert data.itemStatus["sc24b8f"].count == 3
|
||||
assert data.itemStatus["s90e6c9"].count == 7
|
||||
assert data.itemStatus["sd51c5b"].count == 0
|
||||
assert data.itemStatus["s8ae72a"].count == 0
|
||||
assert data.itemStatus["s78ea90"].count == 1
|
||||
assert data.itemImport["ia857f0"].count == 5
|
||||
assert data.itemImport["icfb3a5"].count == 2
|
||||
assert data.itemImport["i2d7a54"].count == 2
|
||||
assert data.itemImport["i56be10"].count == 1
|
||||
|
||||
# Compare content
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy14.json"
|
||||
@@ -867,7 +947,7 @@ def testCoreProjectXML_ReadLegacy14(tstPaths, fncPath, mockRnd):
|
||||
for entry in content:
|
||||
item = NWItem(mockProject, "0000000000000") # type: ignore
|
||||
item.unpack(entry)
|
||||
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||
status[item.itemHandle] = item.getImportStatus()[0]
|
||||
packedContent.append(item.pack())
|
||||
|
||||
assert status == {
|
||||
|
||||
+223
-175
@@ -24,24 +24,50 @@ import pytest
|
||||
|
||||
from tools import C
|
||||
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtGui import QColor, QIcon
|
||||
|
||||
from novelwriter.core.status import NWStatus
|
||||
from novelwriter.core.status import NWStatus, StatusEntry, _ShapeCache
|
||||
from novelwriter.enum import nwStatusShape
|
||||
|
||||
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
|
||||
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Internal(mockRnd):
|
||||
"""Test all the internal functions of the NWStatus class.
|
||||
"""
|
||||
def testCoreStatus_StatusEntry():
|
||||
"""Test the StatusEntry class."""
|
||||
color = QColor(255, 0, 0)
|
||||
icon = NWStatus.createIcon(24, color, nwStatusShape.CIRCLE)
|
||||
entry = StatusEntry("Test", color, nwStatusShape.CIRCLE, icon, 42)
|
||||
|
||||
# Check values
|
||||
assert entry.name == "Test"
|
||||
assert entry.color is color
|
||||
assert entry.shape == nwStatusShape.CIRCLE
|
||||
assert entry.icon is icon
|
||||
assert entry.count == 42
|
||||
|
||||
# Make a copy
|
||||
other = StatusEntry.duplicate(entry)
|
||||
assert other is not entry
|
||||
|
||||
# Check copy is not the same
|
||||
assert other.name == "Test"
|
||||
assert other.color is not color # Not the same object
|
||||
assert other.color == color # But same colours
|
||||
assert other.shape == nwStatusShape.CIRCLE
|
||||
assert other.icon is not icon # Not the same icon, but a copy
|
||||
assert other.count == 42
|
||||
|
||||
# END Test testCoreStatus_StatusEntry
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Internal(mockGUI, mockRnd):
|
||||
"""Test all the internal functions of the NWStatus class."""
|
||||
nStatus = NWStatus(NWStatus.STATUS)
|
||||
nImport = NWStatus(NWStatus.IMPORT)
|
||||
|
||||
with pytest.raises(Exception):
|
||||
NWStatus(999)
|
||||
|
||||
# Generate Key
|
||||
# ============
|
||||
|
||||
@@ -49,14 +75,14 @@ def testCoreStatus_Internal(mockRnd):
|
||||
assert nStatus._newKey() == statusKeys[1]
|
||||
|
||||
# Key collision, should move to key 3
|
||||
nStatus.write(statusKeys[2], "Crash", (0, 0, 0))
|
||||
nStatus.add(statusKeys[2], "Crash", (0, 0, 0), "SQUARE", 0)
|
||||
assert nStatus._newKey() == statusKeys[3]
|
||||
|
||||
assert nImport._newKey() == importKeys[0]
|
||||
assert nImport._newKey() == importKeys[1]
|
||||
|
||||
# Key collision, should move to key 3
|
||||
nImport.write(importKeys[2], "Crash", (0, 0, 0))
|
||||
nImport.add(importKeys[2], "Crash", (0, 0, 0), "SQUARE", 0)
|
||||
assert nImport._newKey() == importKeys[3]
|
||||
|
||||
# Check Key
|
||||
@@ -82,81 +108,94 @@ def testCoreStatus_Internal(mockRnd):
|
||||
assert nImport._isKey("i12345F") is False # Not a lower case hex value
|
||||
assert nImport._isKey("i12345f") is True # Valid hex value
|
||||
|
||||
assert nStatus._checkKey(None) == "s000008" # Creates next key
|
||||
assert nStatus._checkKey("s654321") == "s654321" # Status key accepted
|
||||
assert nStatus._checkKey("i123456") == "s000009" # Import key not accepted
|
||||
|
||||
assert nImport._checkKey(None) == "i00000a" # Creates next key
|
||||
assert nImport._checkKey("s654321") == "i00000b" # Status key not accepted
|
||||
assert nImport._checkKey("i123456") == "i123456" # Import key accepted
|
||||
|
||||
# END Test testCoreStatus_Internal
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Iterator(mockRnd):
|
||||
"""Test the iterator functions of the NWStatus class.
|
||||
"""
|
||||
def testCoreStatus_Iterator(mockGUI, mockRnd):
|
||||
"""Test the iterator functions of the NWStatus class."""
|
||||
nStatus = NWStatus(NWStatus.STATUS)
|
||||
|
||||
nStatus.write(None, "New", (100, 100, 100))
|
||||
nStatus.write(None, "Note", (200, 50, 0))
|
||||
nStatus.write(None, "Draft", (200, 150, 0))
|
||||
nStatus.write(None, "Finished", (50, 200, 0))
|
||||
nStatus.add(None, "New", (100, 100, 100), "SQUARE", 0)
|
||||
nStatus.add(None, "Note", (200, 50, 0), "CIRCLE", 1)
|
||||
nStatus.add(None, "Draft", (200, 150, 0), "SQUARE", 2)
|
||||
nStatus.add(None, "Finished", (50, 200, 0), "CIRCLE", 3)
|
||||
|
||||
# Direct access
|
||||
entry = nStatus[statusKeys[0]]
|
||||
assert entry["cols"] == (100, 100, 100)
|
||||
assert entry["name"] == "New"
|
||||
assert entry["count"] == 0
|
||||
assert isinstance(entry["icon"], QIcon)
|
||||
assert entry.color == QColor(100, 100, 100)
|
||||
assert entry.name == "New"
|
||||
assert entry.count == 0
|
||||
assert isinstance(entry.icon, QIcon)
|
||||
|
||||
# Iterate
|
||||
entries = list(nStatus)
|
||||
assert len(entries) == 4
|
||||
# Length
|
||||
assert len(nStatus._store) == 4
|
||||
assert len(nStatus) == 4
|
||||
|
||||
# Keys
|
||||
assert list(nStatus.keys()) == statusKeys
|
||||
# Content : Keys
|
||||
assert [k for k, _ in nStatus.iterItems()] == [
|
||||
"s000000", "s000001", "s000002", "s000003"
|
||||
]
|
||||
|
||||
# Items
|
||||
for index, (key, entry) in enumerate(nStatus.items()):
|
||||
assert key == statusKeys[index]
|
||||
assert "cols" in entry
|
||||
assert "name" in entry
|
||||
assert "count" in entry
|
||||
assert "icon" in entry
|
||||
# Content : Names
|
||||
assert [e.name for _, e in nStatus.iterItems()] == [
|
||||
"New", "Note", "Draft", "Finished"
|
||||
]
|
||||
|
||||
# Valuse
|
||||
for entry in nStatus.values():
|
||||
assert "cols" in entry
|
||||
assert "name" in entry
|
||||
assert "count" in entry
|
||||
assert "icon" in entry
|
||||
# Content : Colours
|
||||
assert [e.color for _, e in nStatus.iterItems()] == [
|
||||
QColor(100, 100, 100), QColor(200, 50, 0), QColor(200, 150, 0), QColor(50, 200, 0)
|
||||
]
|
||||
|
||||
# Content : Shape
|
||||
assert [e.shape for _, e in nStatus.iterItems()] == [
|
||||
nwStatusShape.SQUARE, nwStatusShape.CIRCLE, nwStatusShape.SQUARE, nwStatusShape.CIRCLE
|
||||
]
|
||||
|
||||
# Content : Count
|
||||
assert [e.count for _, e in nStatus.iterItems()] == [0, 1, 2, 3]
|
||||
|
||||
# END Test testCoreStatus_Iterator
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Entries(mockRnd):
|
||||
"""Test all the simple setters for the NWStatus class.
|
||||
"""
|
||||
def testCoreStatus_Entries(mockGUI, mockRnd):
|
||||
"""Test all the simple setters for the NWStatus class."""
|
||||
nStatus = NWStatus(NWStatus.STATUS)
|
||||
|
||||
# Write
|
||||
# =====
|
||||
# Add
|
||||
# ===
|
||||
|
||||
# Have a key
|
||||
nStatus.write(statusKeys[0], "Entry 1", (200, 100, 50))
|
||||
assert nStatus[statusKeys[0]]["name"] == "Entry 1"
|
||||
assert nStatus[statusKeys[0]]["cols"] == (200, 100, 50)
|
||||
# Has a key
|
||||
nStatus.add(statusKeys[0], "Entry 1", (200, 100, 50), "SQUARE", 0)
|
||||
assert nStatus[statusKeys[0]].name == "Entry 1"
|
||||
assert nStatus[statusKeys[0]].color == QColor(200, 100, 50)
|
||||
assert nStatus[statusKeys[0]].shape == nwStatusShape.SQUARE
|
||||
|
||||
# Don't have a key
|
||||
nStatus.write(None, "Entry 2", (210, 110, 60))
|
||||
assert nStatus[statusKeys[1]]["name"] == "Entry 2"
|
||||
assert nStatus[statusKeys[1]]["cols"] == (210, 110, 60)
|
||||
# Doesn't have a key
|
||||
nStatus.add(None, "Entry 2", (210, 110, 60), "SQUARE", 0)
|
||||
assert nStatus[statusKeys[1]].name == "Entry 2"
|
||||
assert nStatus[statusKeys[1]].color == QColor(210, 110, 60)
|
||||
assert nStatus[statusKeys[1]].shape == nwStatusShape.SQUARE
|
||||
|
||||
# Wrong colour spec
|
||||
nStatus.write(None, "Entry 3", "what?")
|
||||
assert nStatus[statusKeys[2]]["name"] == "Entry 3"
|
||||
assert nStatus[statusKeys[2]]["cols"] == (100, 100, 100)
|
||||
# Wrong colour spec, unknown shape
|
||||
nStatus.add(None, "Entry 3", "what?", "", 0) # type: ignore
|
||||
assert nStatus[statusKeys[2]].name == "Entry 3"
|
||||
assert nStatus[statusKeys[2]].color == QColor(100, 100, 100)
|
||||
assert nStatus[statusKeys[2]].shape == nwStatusShape.SQUARE
|
||||
|
||||
# Wrong colour count
|
||||
nStatus.write(None, "Entry 4", (10, 20))
|
||||
assert nStatus[statusKeys[3]]["name"] == "Entry 4"
|
||||
assert nStatus[statusKeys[3]]["cols"] == (100, 100, 100)
|
||||
nStatus.add(None, "Entry 4", (10, 20), "CIRCLE", 0) # type: ignore
|
||||
assert nStatus[statusKeys[3]].name == "Entry 4"
|
||||
assert nStatus[statusKeys[3]].color == QColor(100, 100, 100)
|
||||
assert nStatus[statusKeys[3]].shape == nwStatusShape.CIRCLE
|
||||
|
||||
# Check
|
||||
# =====
|
||||
@@ -171,29 +210,38 @@ def testCoreStatus_Entries(mockRnd):
|
||||
# Name Access
|
||||
# ===========
|
||||
|
||||
assert nStatus.name(statusKeys[0]) == "Entry 1"
|
||||
assert nStatus.name(statusKeys[1]) == "Entry 2"
|
||||
assert nStatus.name(statusKeys[2]) == "Entry 3"
|
||||
assert nStatus.name(statusKeys[3]) == "Entry 4"
|
||||
assert nStatus.name("blablabla") == "Entry 1"
|
||||
assert nStatus[statusKeys[0]].name == "Entry 1"
|
||||
assert nStatus[statusKeys[1]].name == "Entry 2"
|
||||
assert nStatus[statusKeys[2]].name == "Entry 3"
|
||||
assert nStatus[statusKeys[3]].name == "Entry 4"
|
||||
assert nStatus["blablabla"].name == "Entry 1"
|
||||
|
||||
# Colour Access
|
||||
# =============
|
||||
|
||||
assert nStatus.cols(statusKeys[0]) == (200, 100, 50)
|
||||
assert nStatus.cols(statusKeys[1]) == (210, 110, 60)
|
||||
assert nStatus.cols(statusKeys[2]) == (100, 100, 100)
|
||||
assert nStatus.cols(statusKeys[3]) == (100, 100, 100)
|
||||
assert nStatus.cols("blablabla") == (200, 100, 50)
|
||||
assert nStatus[statusKeys[0]].color == QColor(200, 100, 50)
|
||||
assert nStatus[statusKeys[1]].color == QColor(210, 110, 60)
|
||||
assert nStatus[statusKeys[2]].color == QColor(100, 100, 100)
|
||||
assert nStatus[statusKeys[3]].color == QColor(100, 100, 100)
|
||||
assert nStatus["blablabla"].color == QColor(200, 100, 50)
|
||||
|
||||
# Icon Access
|
||||
# ===========
|
||||
|
||||
assert isinstance(nStatus.icon(statusKeys[0]), QIcon)
|
||||
assert isinstance(nStatus.icon(statusKeys[1]), QIcon)
|
||||
assert isinstance(nStatus.icon(statusKeys[2]), QIcon)
|
||||
assert isinstance(nStatus.icon(statusKeys[3]), QIcon)
|
||||
assert isinstance(nStatus.icon("blablabla"), QIcon)
|
||||
assert isinstance(nStatus[statusKeys[0]].icon, QIcon)
|
||||
assert isinstance(nStatus[statusKeys[1]].icon, QIcon)
|
||||
assert isinstance(nStatus[statusKeys[2]].icon, QIcon)
|
||||
assert isinstance(nStatus[statusKeys[3]].icon, QIcon)
|
||||
assert isinstance(nStatus["blablabla"].icon, QIcon)
|
||||
|
||||
# Shape Access
|
||||
# ============
|
||||
|
||||
assert nStatus[statusKeys[0]].shape == nwStatusShape.SQUARE
|
||||
assert nStatus[statusKeys[1]].shape == nwStatusShape.SQUARE
|
||||
assert nStatus[statusKeys[2]].shape == nwStatusShape.SQUARE
|
||||
assert nStatus[statusKeys[3]].shape == nwStatusShape.CIRCLE
|
||||
assert nStatus["blablabla"].shape == nwStatusShape.SQUARE
|
||||
|
||||
# Increment and Count Access
|
||||
# ==========================
|
||||
@@ -203,50 +251,32 @@ def testCoreStatus_Entries(mockRnd):
|
||||
for _ in range(n):
|
||||
nStatus.increment(statusKeys[i])
|
||||
|
||||
assert nStatus.count(statusKeys[0]) == countTo[0]
|
||||
assert nStatus.count(statusKeys[1]) == countTo[1]
|
||||
assert nStatus.count(statusKeys[2]) == countTo[2]
|
||||
assert nStatus.count(statusKeys[3]) == countTo[3]
|
||||
assert nStatus.count("blablabla") == countTo[0]
|
||||
assert nStatus[statusKeys[0]].count == countTo[0]
|
||||
assert nStatus[statusKeys[1]].count == countTo[1]
|
||||
assert nStatus[statusKeys[2]].count == countTo[2]
|
||||
assert nStatus[statusKeys[3]].count == countTo[3]
|
||||
assert nStatus["blablabla"].count == countTo[0]
|
||||
|
||||
nStatus.resetCounts()
|
||||
|
||||
assert nStatus.count(statusKeys[0]) == 0
|
||||
assert nStatus.count(statusKeys[1]) == 0
|
||||
assert nStatus.count(statusKeys[2]) == 0
|
||||
assert nStatus.count(statusKeys[3]) == 0
|
||||
assert nStatus[statusKeys[0]].count == 0
|
||||
assert nStatus[statusKeys[1]].count == 0
|
||||
assert nStatus[statusKeys[2]].count == 0
|
||||
assert nStatus[statusKeys[3]].count == 0
|
||||
|
||||
# Reorder
|
||||
# =======
|
||||
# Update
|
||||
# ======
|
||||
|
||||
cOrder = list(nStatus.keys())
|
||||
assert cOrder == statusKeys
|
||||
assert list(nStatus._store.keys()) == statusKeys
|
||||
|
||||
# Wrong length
|
||||
assert nStatus.reorder([]) is False
|
||||
# Reverse
|
||||
order: list[tuple[str | None, StatusEntry]] = list(nStatus.iterItems())
|
||||
nStatus.update(list(reversed(order)))
|
||||
assert list(nStatus._store.keys()) == list(reversed(statusKeys))
|
||||
|
||||
# No change
|
||||
assert nStatus.reorder(cOrder) is False
|
||||
|
||||
# Actual reaorder
|
||||
nOrder = [
|
||||
statusKeys[0],
|
||||
statusKeys[2],
|
||||
statusKeys[1],
|
||||
statusKeys[3],
|
||||
]
|
||||
assert nStatus.reorder(nOrder) is True
|
||||
assert list(nStatus.keys()) == nOrder
|
||||
|
||||
# Add an unknown key
|
||||
wOrder = nOrder.copy()
|
||||
wOrder[3] = nStatus._newKey()
|
||||
assert nStatus.reorder(wOrder) is False
|
||||
assert list(nStatus.keys()) == nOrder
|
||||
|
||||
# Put it back
|
||||
assert nStatus.reorder(cOrder) is True
|
||||
assert list(nStatus.keys()) == cOrder
|
||||
# Restore
|
||||
nStatus.update(order)
|
||||
assert list(nStatus._store.keys()) == statusKeys
|
||||
|
||||
# Default
|
||||
# =======
|
||||
@@ -255,56 +285,41 @@ def testCoreStatus_Entries(mockRnd):
|
||||
nStatus._default = None
|
||||
|
||||
assert nStatus.check("Entry 5") == ""
|
||||
assert nStatus.name("blablabla") == ""
|
||||
assert nStatus.cols("blablabla") == (100, 100, 100)
|
||||
assert nStatus.count("blablabla") == 0
|
||||
assert isinstance(nStatus.icon("blablabla"), QIcon)
|
||||
assert nStatus["blablabla"].name == ""
|
||||
assert nStatus["blablabla"].color == QColor(0, 0, 0)
|
||||
assert nStatus["blablabla"].shape == nwStatusShape.SQUARE
|
||||
assert nStatus["blablabla"].icon.isNull()
|
||||
assert nStatus["blablabla"].count == 0
|
||||
|
||||
nStatus._default = default
|
||||
|
||||
# Remove
|
||||
# ======
|
||||
# This uses update with deleted items
|
||||
|
||||
# Non-existing entry
|
||||
assert nStatus.remove("blablabla") is False
|
||||
order: list[tuple[str | None, StatusEntry]] = list(nStatus.iterItems())
|
||||
|
||||
# Non-zero entry
|
||||
nStatus.increment(statusKeys[3])
|
||||
assert nStatus.remove(statusKeys[3]) is False
|
||||
# Remove Entry 0
|
||||
nStatus.update([order[1], order[3], order[2]])
|
||||
assert list(nStatus._store.keys()) == [statusKeys[1], statusKeys[3], statusKeys[2]]
|
||||
assert nStatus._default == statusKeys[1]
|
||||
|
||||
# Delete last entry
|
||||
nStatus.resetCounts()
|
||||
lastName = nStatus.name(statusKeys[3])
|
||||
assert lastName == "Entry 4"
|
||||
assert nStatus.remove(statusKeys[3]) is True
|
||||
assert nStatus.check(statusKeys[3]) == nStatus._default
|
||||
assert nStatus.check(lastName) == nStatus._default
|
||||
|
||||
# Delete default entry, Entry 2 is new default
|
||||
firstName = nStatus.name(nStatus._default)
|
||||
assert firstName == "Entry 1"
|
||||
assert nStatus.remove(nStatus._default) is True
|
||||
assert nStatus.name(firstName) == "Entry 2"
|
||||
|
||||
# Remove remaining entries
|
||||
assert nStatus.remove(statusKeys[1]) is True
|
||||
assert nStatus.remove(statusKeys[2]) is True
|
||||
|
||||
assert len(nStatus) == 0
|
||||
assert nStatus._default is None
|
||||
# Remove Entry 1
|
||||
nStatus.update([order[3], order[2]])
|
||||
assert list(nStatus._store.keys()) == [statusKeys[3], statusKeys[2]]
|
||||
assert nStatus._default == statusKeys[3]
|
||||
|
||||
# END Test testCoreStatus_Entries
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_PackUnpack(mockRnd):
|
||||
"""Test all the pack/unpack of the NWStatus class.
|
||||
"""
|
||||
def testCoreStatus_Pack(mockGUI, mockRnd):
|
||||
"""Test data packing of the NWStatus class."""
|
||||
nStatus = NWStatus(NWStatus.STATUS)
|
||||
nStatus.write(None, "New", (100, 100, 100))
|
||||
nStatus.write(None, "Note", (200, 50, 0))
|
||||
nStatus.write(None, "Draft", (200, 150, 0))
|
||||
nStatus.write(None, "Finished", (50, 200, 0))
|
||||
nStatus.add(None, "New", (100, 100, 100), "SQUARE", 0)
|
||||
nStatus.add(None, "Note", (200, 50, 0), "CIRCLE", 0)
|
||||
nStatus.add(None, "Draft", (200, 150, 0), "SQUARE", 0)
|
||||
nStatus.add(None, "Finished", (50, 200, 0), "SQUARE", 0)
|
||||
|
||||
countTo = [3, 5, 7, 9]
|
||||
for i, n in enumerate(countTo):
|
||||
@@ -318,52 +333,85 @@ def testCoreStatus_PackUnpack(mockRnd):
|
||||
"count": "3",
|
||||
"red": "100",
|
||||
"green": "100",
|
||||
"blue": "100"
|
||||
"blue": "100",
|
||||
"shape": "SQUARE",
|
||||
}),
|
||||
("Note", {
|
||||
"key": statusKeys[1],
|
||||
"count": "5",
|
||||
"red": "200",
|
||||
"green": "50",
|
||||
"blue": "0"
|
||||
"blue": "0",
|
||||
"shape": "CIRCLE",
|
||||
}),
|
||||
("Draft", {
|
||||
"key": statusKeys[2],
|
||||
"count": "7",
|
||||
"red": "200",
|
||||
"green": "150",
|
||||
"blue": "0"
|
||||
"blue": "0",
|
||||
"shape": "SQUARE",
|
||||
}),
|
||||
("Finished", {
|
||||
"key": statusKeys[3],
|
||||
"count": "9",
|
||||
"red": "50",
|
||||
"green": "200",
|
||||
"blue": "0"
|
||||
"blue": "0",
|
||||
"shape": "SQUARE",
|
||||
}),
|
||||
]
|
||||
|
||||
# Unpack
|
||||
nStatus = NWStatus(NWStatus.STATUS)
|
||||
nStatus.unpack({
|
||||
statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
|
||||
statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
|
||||
statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]},
|
||||
statusKeys[3]: {"label": "New3", "colour": (250, 250, 250), "count": countTo[3]},
|
||||
})
|
||||
assert len(nStatus._store) == 4
|
||||
assert list(nStatus._store.keys()) == statusKeys
|
||||
assert nStatus._store[statusKeys[0]]["name"] == "New0"
|
||||
assert nStatus._store[statusKeys[1]]["name"] == "New1"
|
||||
assert nStatus._store[statusKeys[2]]["name"] == "New2"
|
||||
assert nStatus._store[statusKeys[3]]["name"] == "New3"
|
||||
assert nStatus._store[statusKeys[0]]["cols"] == (100, 100, 100)
|
||||
assert nStatus._store[statusKeys[1]]["cols"] == (150, 150, 150)
|
||||
assert nStatus._store[statusKeys[2]]["cols"] == (200, 200, 200)
|
||||
assert nStatus._store[statusKeys[3]]["cols"] == (250, 250, 250)
|
||||
assert nStatus._store[statusKeys[0]]["count"] == countTo[0]
|
||||
assert nStatus._store[statusKeys[1]]["count"] == countTo[1]
|
||||
assert nStatus._store[statusKeys[2]]["count"] == countTo[2]
|
||||
assert nStatus._store[statusKeys[3]]["count"] == countTo[3]
|
||||
# END Test testCoreStatus_Pack
|
||||
|
||||
# END Test testCoreStatus_PackUnpack
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_ShapeCache():
|
||||
"""Test the _ShapeCache class."""
|
||||
shapes = _ShapeCache()
|
||||
|
||||
# Generate all shapes
|
||||
square = shapes.getShape(nwStatusShape.SQUARE)
|
||||
circleQ = shapes.getShape(nwStatusShape.CIRCLE_Q)
|
||||
circleH = shapes.getShape(nwStatusShape.CIRCLE_H)
|
||||
circleT = shapes.getShape(nwStatusShape.CIRCLE_T)
|
||||
circle = shapes.getShape(nwStatusShape.CIRCLE)
|
||||
triangle = shapes.getShape(nwStatusShape.TRIANGLE)
|
||||
nabla = shapes.getShape(nwStatusShape.NABLA)
|
||||
diamond = shapes.getShape(nwStatusShape.DIAMOND)
|
||||
pentagon = shapes.getShape(nwStatusShape.PENTAGON)
|
||||
hexagon = shapes.getShape(nwStatusShape.HEXAGON)
|
||||
star = shapes.getShape(nwStatusShape.STAR)
|
||||
pacman = shapes.getShape(nwStatusShape.PACMAN)
|
||||
bars1 = shapes.getShape(nwStatusShape.BARS_1)
|
||||
bars2 = shapes.getShape(nwStatusShape.BARS_2)
|
||||
bars3 = shapes.getShape(nwStatusShape.BARS_3)
|
||||
bars4 = shapes.getShape(nwStatusShape.BARS_4)
|
||||
block1 = shapes.getShape(nwStatusShape.BLOCK_1)
|
||||
block2 = shapes.getShape(nwStatusShape.BLOCK_2)
|
||||
block3 = shapes.getShape(nwStatusShape.BLOCK_3)
|
||||
block4 = shapes.getShape(nwStatusShape.BLOCK_4)
|
||||
|
||||
# Request again should return from cache
|
||||
assert shapes.getShape(nwStatusShape.SQUARE) is square
|
||||
assert shapes.getShape(nwStatusShape.CIRCLE_Q) is circleQ
|
||||
assert shapes.getShape(nwStatusShape.CIRCLE_H) is circleH
|
||||
assert shapes.getShape(nwStatusShape.CIRCLE_T) is circleT
|
||||
assert shapes.getShape(nwStatusShape.CIRCLE) is circle
|
||||
assert shapes.getShape(nwStatusShape.TRIANGLE) is triangle
|
||||
assert shapes.getShape(nwStatusShape.NABLA) is nabla
|
||||
assert shapes.getShape(nwStatusShape.DIAMOND) is diamond
|
||||
assert shapes.getShape(nwStatusShape.PENTAGON) is pentagon
|
||||
assert shapes.getShape(nwStatusShape.HEXAGON) is hexagon
|
||||
assert shapes.getShape(nwStatusShape.STAR) is star
|
||||
assert shapes.getShape(nwStatusShape.PACMAN) is pacman
|
||||
assert shapes.getShape(nwStatusShape.BARS_1) is bars1
|
||||
assert shapes.getShape(nwStatusShape.BARS_2) is bars2
|
||||
assert shapes.getShape(nwStatusShape.BARS_3) is bars3
|
||||
assert shapes.getShape(nwStatusShape.BARS_4) is bars4
|
||||
assert shapes.getShape(nwStatusShape.BLOCK_1) is block1
|
||||
assert shapes.getShape(nwStatusShape.BLOCK_2) is block2
|
||||
assert shapes.getShape(nwStatusShape.BLOCK_3) is block3
|
||||
assert shapes.getShape(nwStatusShape.BLOCK_4) is block4
|
||||
|
||||
# END Test testCoreStatus_ShapeCache
|
||||
|
||||
@@ -24,14 +24,13 @@ import pytest
|
||||
|
||||
from tools import C, buildTestProject
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QColor
|
||||
from PyQt5.QtWidgets import QDialog, QAction, QColorDialog
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
||||
from novelwriter.enum import nwItemType
|
||||
from novelwriter.enum import nwItemType, nwStatusShape
|
||||
from novelwriter.types import QtMouseLeft
|
||||
|
||||
KEY_DELAY = 1
|
||||
@@ -169,8 +168,8 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
|
||||
nwGUI.rebuildTrees()
|
||||
project.countStatus()
|
||||
|
||||
assert [e["count"] for _, e in project.data.itemStatus.items()] == [2, 0, 2, 1]
|
||||
assert [e["count"] for _, e in project.data.itemImport.items()] == [3, 0, 2, 1]
|
||||
assert [e.count for _, e in project.data.itemStatus.iterItems()] == [2, 0, 2, 1]
|
||||
assert [e.count for _, e in project.data.itemImport.iterItems()] == [3, 0, 2, 1]
|
||||
|
||||
# Create Dialog
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.PAGE_STATUS)
|
||||
@@ -182,20 +181,20 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
|
||||
|
||||
status = projSettings.statusPage
|
||||
|
||||
assert status.wasChanged is False
|
||||
assert status.getNewList() == ([], [])
|
||||
assert status.changed is False
|
||||
assert status.getNewList() == []
|
||||
assert status.listBox.topLevelItemCount() == 4
|
||||
|
||||
# Can't delete the first item (it's in use)
|
||||
status.listBox.clearSelection()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(0))
|
||||
qtbot.mouseClick(status.delButton, QtMouseLeft)
|
||||
status.delButton.click()
|
||||
assert status.listBox.topLevelItemCount() == 4
|
||||
|
||||
# Can delete the second item
|
||||
status.listBox.clearSelection()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(1))
|
||||
qtbot.mouseClick(status.delButton, QtMouseLeft)
|
||||
status.delButton.click()
|
||||
assert status.listBox.topLevelItemCount() == 3
|
||||
|
||||
# Add a new item
|
||||
@@ -204,39 +203,38 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
|
||||
status.addButton.click()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(3))
|
||||
status.editName.setText("Final")
|
||||
status.colButton.click()
|
||||
status.saveButton.click()
|
||||
status.colorButton.click()
|
||||
status._selectShape(nwStatusShape.CIRCLE)
|
||||
status.applyButton.click()
|
||||
assert status.listBox.topLevelItemCount() == 4
|
||||
|
||||
assert status.wasChanged is True
|
||||
assert status.getNewList() == (
|
||||
[
|
||||
{
|
||||
"key": C.sNew,
|
||||
"name": "New",
|
||||
"cols": (100, 100, 100)
|
||||
}, {
|
||||
"key": C.sDraft,
|
||||
"name": "Draft",
|
||||
"cols": (200, 150, 0)
|
||||
}, {
|
||||
"key": C.sFinished,
|
||||
"name": "Finished",
|
||||
"cols": (50, 200, 0)
|
||||
}, {
|
||||
"key": None,
|
||||
"name": "Final",
|
||||
"cols": (20, 30, 40)
|
||||
}
|
||||
], [
|
||||
C.sNote # Deleted item
|
||||
]
|
||||
)
|
||||
assert status.changed is True
|
||||
update = status.getNewList()
|
||||
|
||||
assert update[0][0] == C.sNew
|
||||
assert update[0][1].name == "New"
|
||||
assert update[0][1].color == QColor(100, 100, 100)
|
||||
assert update[0][1].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert update[1][0] == C.sDraft
|
||||
assert update[1][1].name == "Draft"
|
||||
assert update[1][1].color == QColor(200, 150, 0)
|
||||
assert update[1][1].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert update[2][0] == C.sFinished
|
||||
assert update[2][1].name == "Finished"
|
||||
assert update[2][1].color == QColor(50, 200, 0)
|
||||
assert update[2][1].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert update[3][0] is None
|
||||
assert update[3][1].name == "Final"
|
||||
assert update[3][1].color == QColor(20, 30, 40)
|
||||
assert update[3][1].shape == nwStatusShape.CIRCLE
|
||||
|
||||
# Move items, none selected -> no change
|
||||
status.listBox.clearSelection()
|
||||
status._moveItem(1)
|
||||
assert [x["key"] for x in status.getNewList()[0]] == [
|
||||
assert [x[0] for x in status.getNewList()] == [
|
||||
C.sNew, C.sDraft, C.sFinished, None
|
||||
]
|
||||
|
||||
@@ -244,7 +242,7 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
|
||||
status.listBox.clearSelection()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(0))
|
||||
status._moveItem(-1)
|
||||
assert [x["key"] for x in status.getNewList()[0]] == [
|
||||
assert [x[0] for x in status.getNewList()] == [
|
||||
C.sNew, C.sDraft, C.sFinished, None
|
||||
]
|
||||
|
||||
@@ -252,13 +250,13 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
|
||||
status.listBox.clearSelection()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(3))
|
||||
status._moveItem(-1)
|
||||
assert [x["key"] for x in status.getNewList()[0]] == [
|
||||
assert [x[0] for x in status.getNewList()] == [
|
||||
C.sNew, C.sDraft, None, C.sFinished
|
||||
]
|
||||
|
||||
# Move items, same selected, move down -> allowed
|
||||
status._moveItem(1)
|
||||
assert [x["key"] for x in status.getNewList()[0]] == [
|
||||
assert [x[0] for x in status.getNewList()] == [
|
||||
C.sNew, C.sDraft, C.sFinished, None
|
||||
]
|
||||
|
||||
@@ -271,62 +269,58 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRn
|
||||
# Delete unused entry
|
||||
importance.listBox.clearSelection()
|
||||
importance.listBox.setCurrentItem(importance.listBox.topLevelItem(1))
|
||||
qtbot.mouseClick(importance.delButton, QtMouseLeft)
|
||||
importance.delButton.click()
|
||||
assert importance.listBox.topLevelItemCount() == 3
|
||||
|
||||
# Add a new entry
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
||||
qtbot.mouseClick(importance.addButton, QtMouseLeft)
|
||||
importance.addButton.click()
|
||||
importance.listBox.clearSelection()
|
||||
importance.listBox.setCurrentItem(importance.listBox.topLevelItem(3))
|
||||
for _ in range(8):
|
||||
qtbot.keyClick(importance.editName, Qt.Key.Key_Backspace, delay=KEY_DELAY)
|
||||
for c in "Final":
|
||||
qtbot.keyClick(importance.editName, c, delay=KEY_DELAY)
|
||||
qtbot.mouseClick(importance.colButton, QtMouseLeft)
|
||||
qtbot.mouseClick(importance.saveButton, QtMouseLeft)
|
||||
importance.editName.setText("Final")
|
||||
importance.colorButton.click()
|
||||
importance._selectShape(nwStatusShape.TRIANGLE)
|
||||
importance.applyButton.click()
|
||||
assert importance.listBox.topLevelItemCount() == 4
|
||||
|
||||
assert importance.wasChanged is True
|
||||
assert importance.getNewList() == (
|
||||
[
|
||||
{
|
||||
"key": C.iNew,
|
||||
"name": "New",
|
||||
"cols": (100, 100, 100)
|
||||
}, {
|
||||
"key": C.iMajor,
|
||||
"name": "Major",
|
||||
"cols": (200, 150, 0)
|
||||
}, {
|
||||
"key": C.iMain,
|
||||
"name": "Main",
|
||||
"cols": (50, 200, 0)
|
||||
}, {
|
||||
"key": None,
|
||||
"name": "Final",
|
||||
"cols": (20, 30, 40)
|
||||
}
|
||||
], [
|
||||
C.iMinor # Deleted item
|
||||
]
|
||||
)
|
||||
assert importance.changed is True
|
||||
update = importance.getNewList()
|
||||
|
||||
assert update[0][0] == C.iNew
|
||||
assert update[0][1].name == "New"
|
||||
assert update[0][1].color == QColor(100, 100, 100)
|
||||
assert update[0][1].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert update[1][0] == C.iMajor
|
||||
assert update[1][1].name == "Major"
|
||||
assert update[1][1].color == QColor(200, 150, 0)
|
||||
assert update[1][1].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert update[2][0] == C.iMain
|
||||
assert update[2][1].name == "Main"
|
||||
assert update[2][1].color == QColor(50, 200, 0)
|
||||
assert update[2][1].shape == nwStatusShape.SQUARE
|
||||
|
||||
assert update[3][0] is None
|
||||
assert update[3][1].name == "Final"
|
||||
assert update[3][1].color == QColor(20, 30, 40)
|
||||
assert update[3][1].shape == nwStatusShape.TRIANGLE
|
||||
|
||||
# Check Project
|
||||
projSettings._doSave()
|
||||
|
||||
statusItems = dict(project.data.itemStatus.items())
|
||||
assert statusItems[C.sNew]["name"] == "New"
|
||||
assert statusItems[C.sDraft]["name"] == "Draft"
|
||||
assert statusItems[C.sFinished]["name"] == "Finished"
|
||||
assert statusItems["s000013"]["name"] == "Final"
|
||||
statusItems = dict(project.data.itemStatus.iterItems())
|
||||
assert statusItems[C.sNew].name == "New"
|
||||
assert statusItems[C.sDraft].name == "Draft"
|
||||
assert statusItems[C.sFinished].name == "Finished"
|
||||
assert statusItems["s000013"].name == "Final"
|
||||
|
||||
importItems = dict(project.data.itemImport.items())
|
||||
assert importItems[C.iNew]["name"] == "New"
|
||||
assert importItems[C.iMajor]["name"] == "Major"
|
||||
assert importItems[C.iMain]["name"] == "Main"
|
||||
assert importItems["i000014"]["name"] == "Final"
|
||||
importItems = dict(project.data.itemImport.iterItems())
|
||||
assert importItems[C.iNew].name == "New"
|
||||
assert importItems[C.iMajor].name == "Major"
|
||||
assert importItems[C.iMain].name == "Main"
|
||||
assert importItems["i000014"].name == "Final"
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
@@ -363,7 +357,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
|
||||
# Nothing to save or delete
|
||||
replace.listBox.clearSelection()
|
||||
replace._saveEntry()
|
||||
replace._applyChanges()
|
||||
replace._delEntry()
|
||||
assert replace.listBox.topLevelItemCount() == 2
|
||||
|
||||
@@ -381,7 +375,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
replace.editValue.setText("")
|
||||
for c in "With This Stuff ":
|
||||
qtbot.keyClick(replace.editValue, c, delay=KEY_DELAY)
|
||||
qtbot.mouseClick(replace.saveButton, QtMouseLeft)
|
||||
qtbot.mouseClick(replace.applyButton, QtMouseLeft)
|
||||
assert replace.listBox.topLevelItem(2).text(0) == "<This>" # type: ignore
|
||||
assert replace.listBox.topLevelItem(2).text(1) == "With This Stuff " # type: ignore
|
||||
|
||||
|
||||
Reference in New Issue
Block a user