Simplify further how status labels are added and updated

This commit is contained in:
Veronica Berglyd Olsen
2024-04-10 21:38:43 +02:00
parent 63d2f5c57d
commit f17302439e
5 changed files with 69 additions and 142 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ class DocMerger:
docText = self._project.storage.getDocumentText(srcHandle).rstrip("\n") docText = self._project.storage.getDocumentText(srcHandle).rstrip("\n")
if addComment: if addComment:
docInfo = srcItem.describeMe() docInfo = srcItem.describeMe()
docSt, _ = srcItem.getImportStatus(incIcon=False) docSt, _ = srcItem.getImportStatus()
cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n" cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n"
docText = cmtLine + docText docText = cmtLine + docText
+21 -43
View File
@@ -26,34 +26,32 @@ from __future__ import annotations
import json import json
import logging import logging
from collections.abc import Iterable
from enum import Enum from enum import Enum
from functools import partial
from pathlib import Path
from time import time from time import time
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from pathlib import Path
from functools import partial
from collections.abc import Iterable
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication
from novelwriter import CONFIG, SHARED, __version__, __hexversion__ from novelwriter import CONFIG, SHARED, __version__, __hexversion__
from novelwriter.core.status import StatusEntry
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 ( from novelwriter.common import (
checkStringNone, formatInt, formatTimeStamp, getFileSize, hexToInt, makeFileNameSafe, minmax 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 if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
from novelwriter.core.status import NWStatus
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -462,14 +460,14 @@ class NWProject:
def setDefaultStatusImport(self) -> None: def setDefaultStatusImport(self) -> None:
"""Set the default status and importance values.""" """Set the default status and importance values."""
self._data.itemStatus.write(None, self.tr("New"), (100, 100, 100), "SQUARE") self._data.itemStatus.add(None, self.tr("New"), (100, 100, 100), "SQUARE", 0)
self._data.itemStatus.write(None, self.tr("Note"), (200, 50, 0), "SQUARE") self._data.itemStatus.add(None, self.tr("Note"), (200, 50, 0), "SQUARE", 0)
self._data.itemStatus.write(None, self.tr("Draft"), (200, 150, 0), "SQUARE") self._data.itemStatus.add(None, self.tr("Draft"), (200, 150, 0), "SQUARE", 0)
self._data.itemStatus.write(None, self.tr("Finished"), (50, 200, 0), "SQUARE") self._data.itemStatus.add(None, self.tr("Finished"), (50, 200, 0), "SQUARE", 0)
self._data.itemImport.write(None, self.tr("New"), (100, 100, 100), "SQUARE") self._data.itemImport.add(None, self.tr("New"), (100, 100, 100), "SQUARE", 0)
self._data.itemImport.write(None, self.tr("Minor"), (200, 50, 0), "SQUARE") self._data.itemImport.add(None, self.tr("Minor"), (200, 50, 0), "SQUARE", 0)
self._data.itemImport.write(None, self.tr("Major"), (200, 150, 0), "SQUARE") self._data.itemImport.add(None, self.tr("Major"), (200, 150, 0), "SQUARE", 0)
self._data.itemImport.write(None, self.tr("Main"), (50, 200, 0), "SQUARE") self._data.itemImport.add(None, self.tr("Main"), (50, 200, 0), "SQUARE", 0)
return return
def setProjectLang(self, language: str | None) -> None: def setProjectLang(self, language: str | None) -> None:
@@ -492,14 +490,6 @@ class NWProject:
self.setProjectChanged(True) self.setProjectChanged(True)
return return
def setStatus(self, update: list[tuple[str | None, StatusEntry]], remove: list[str]) -> None:
"""Update the list of novel file status flags."""
return self._setStatusImport(update, remove, self._data.itemStatus)
def setImport(self, update: list[tuple[str | None, StatusEntry]], remove: list[str]) -> None:
"""Update the list of note file importance flags."""
return self._setStatusImport(update, remove, self._data.itemImport)
def setProjectChanged(self, status: bool) -> bool: def setProjectChanged(self, status: bool) -> bool:
"""Toggle the project changed flag, and propagate the """Toggle the project changed flag, and propagate the
information to the GUI statusbar. information to the GUI statusbar.
@@ -585,18 +575,6 @@ class NWProject:
# Internal Functions # Internal Functions
## ##
def _setStatusImport(self, update: list[tuple[str | None, StatusEntry]],
remove: list[str], target: NWStatus) -> None:
"""Update the list of novel file status or importance flags, and
delete those that have been requested deleted.
"""
if update or remove:
order = [target.write(k, e.name, e.color, e.shape) for k, e in update]
for key in remove:
target.remove(key)
target.reorder(order)
return
def _loadProjectLocalisation(self) -> bool: def _loadProjectLocalisation(self) -> bool:
"""Load the language data for the current project language.""" """Load the language data for the current project language."""
if self._data.language is None or CONFIG._nwLangPath is None: if self._data.language is None or CONFIG._nwLangPath is None:
+1 -1
View File
@@ -439,7 +439,7 @@ class ProjectXMLReader:
blue = checkInt(xEntry.attrib.get("blue", 0), 0) blue = checkInt(xEntry.attrib.get("blue", 0), 0)
count = checkInt(xEntry.attrib.get("count", 0), 0) count = checkInt(xEntry.attrib.get("count", 0), 0)
shape = xEntry.attrib.get("shape", "") shape = xEntry.attrib.get("shape", "")
sObject.write(key, xEntry.text or "", (red, green, blue), shape, count) sObject.add(key, xEntry.text or "", (red, green, blue), shape, count)
return return
def _parseDictKeyText(self, xItem: ET.Element) -> dict: def _parseDictKeyText(self, xItem: ET.Element) -> dict:
+25 -62
View File
@@ -35,7 +35,7 @@ from typing import TYPE_CHECKING, Literal
from PyQt5.QtCore import QPointF, Qt from PyQt5.QtCore import QPointF, Qt
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor, QPolygonF from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor, QPolygonF
from novelwriter import CONFIG from novelwriter import SHARED
from novelwriter.common import simplified from novelwriter.common import simplified
from novelwriter.enum import nwStatusShape from novelwriter.enum import nwStatusShape
from novelwriter.types import QtPaintAnitAlias, QtTransparent from novelwriter.types import QtPaintAnitAlias, QtTransparent
@@ -77,7 +77,7 @@ class NWStatus:
self._store: dict[str, StatusEntry] = {} self._store: dict[str, StatusEntry] = {}
self._default = None self._default = None
self._iPX = CONFIG.pxInt(24) self._iPx = SHARED.theme.baseIconHeight
if self._type == self.STATUS: if self._type == self.STATUS:
self._prefix = "s" self._prefix = "s"
@@ -103,62 +103,42 @@ class NWStatus:
# Methods # Methods
## ##
def write(self, key: str | None, name: str, color: tuple[int, int, int] | QColor, def add(self, key: str | None, name: str, color: tuple[int, int, int],
shape: nwStatusShape | str, count: int = 0) -> str: shape: str, count: int) -> str:
"""Add or update a status entry. If the key is invalid, a new """Add or update a status entry. If the key is invalid, a new
key is generated. key is generated.
""" """
if not self._isKey(key): if isinstance(color, tuple) and len(color) == 3:
key = self._newKey()
if isinstance(color, QColor):
qColor = color
elif isinstance(color, tuple) and len(color) == 3:
qColor = QColor(*color) qColor = QColor(*color)
else: else:
qColor = QColor(100, 100, 100) qColor = QColor(100, 100, 100)
if not isinstance(shape, nwStatusShape): try:
try: iShape = nwStatusShape[shape]
shape = nwStatusShape[shape] except KeyError:
except KeyError: iShape = nwStatusShape.SQUARE
shape = nwStatusShape.SQUARE
key = self._checkKey(key)
name = simplified(name) name = simplified(name)
icon = self.createIcon(self._iPX, qColor, shape) icon = self.createIcon(self._iPx, qColor, iShape)
self._store[key] = StatusEntry(name, qColor, iShape, icon, count)
if key and key in self._store:
entry = self._store[key]
entry.name = name
entry.color = qColor
entry.shape = shape
entry.icon = icon
entry.count = count
else:
self._store[key] = StatusEntry(name, qColor, shape, icon, count)
if self._default is None: if self._default is None:
self._default = key self._default = key
return key return key
def remove(self, key: str) -> bool: def update(self, update: list[tuple[str | None, StatusEntry]]) -> None:
"""Remove an entry in the list, except if the count > 0.""" """Update the list of statuses, and from removed list."""
if key not in self._store: self._store.clear()
return False for key, entry in update:
if self._store[key].count > 0: self._store[self._checkKey(key)] = entry
return False
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()) return
if key == self._default:
if len(keys) > 0:
self._default = keys[0]
else:
self._default = None
return True
def check(self, value: str) -> str: def check(self, value: str) -> str:
"""Check the key against the stored status names.""" """Check the key against the stored status names."""
@@ -168,27 +148,6 @@ class NWStatus:
return self._default return self._default
return "" return ""
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: def resetCounts(self) -> None:
"""Clear the counts of references to the status entries.""" """Clear the counts of references to the status entries."""
for key in self._store: for key in self._store:
@@ -264,6 +223,10 @@ class NWStatus:
return False return False
return True return True
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 # END Class NWStatus
+21 -35
View File
@@ -181,19 +181,16 @@ class GuiProjectSettings(QDialog):
rebuildTrees = False rebuildTrees = False
if self.statusPage.wasChanged: if self.statusPage.changed:
update, remove = self.statusPage.getNewList() project.data.itemStatus.update(self.statusPage.getNewList())
project.setStatus(update, remove)
rebuildTrees = True rebuildTrees = True
if self.importPage.wasChanged: if self.importPage.changed:
update, remove = self.importPage.getNewList() project.data.itemImport.update(self.importPage.getNewList())
project.setImport(update, remove)
rebuildTrees = True rebuildTrees = True
if self.replacePage.wasChanged: if self.replacePage.changed:
update = self.replacePage.getNewList() project.data.setAutoReplace(self.replacePage.getNewList())
project.data.setAutoReplace(update)
self.newProjectSettingsReady.emit(rebuildTrees) self.newProjectSettingsReady.emit(rebuildTrees)
QApplication.processEvents() QApplication.processEvents()
@@ -327,7 +324,6 @@ class _StatusPage(NFixedPage):
) )
self._changed = False self._changed = False
self._colDeleted: list[str] = []
self._selColour = QColor(100, 100, 100) self._selColour = QColor(100, 100, 100)
self._iPx = SHARED.theme.baseIconHeight self._iPx = SHARED.theme.baseIconHeight
@@ -415,7 +411,7 @@ class _StatusPage(NFixedPage):
return return
@property @property
def wasChanged(self) -> bool: def changed(self) -> bool:
"""The user changed these settings.""" """The user changed these settings."""
return self._changed return self._changed
@@ -423,7 +419,7 @@ class _StatusPage(NFixedPage):
# Methods # Methods
## ##
def getNewList(self) -> tuple[list[tuple[str | None, StatusEntry]], list[str]]: def getNewList(self) -> list[tuple[str | None, StatusEntry]]:
"""Return list of entries.""" """Return list of entries."""
if self._changed: if self._changed:
update = [] update = []
@@ -432,8 +428,8 @@ class _StatusPage(NFixedPage):
key = item.data(self.C_DATA, self.D_KEY) key = item.data(self.C_DATA, self.D_KEY)
entry = item.data(self.C_DATA, self.D_ENTRY) entry = item.data(self.C_DATA, self.D_ENTRY)
update.append((key, entry)) update.append((key, entry))
return update, self._colDeleted return update
return [], [] return []
def columnWidth(self) -> int: def columnWidth(self) -> int:
"""Return the size of the header column.""" """Return the size of the header column."""
@@ -478,7 +474,6 @@ class _StatusPage(NFixedPage):
SHARED.error(self.tr("Cannot delete a status item that is in use.")) SHARED.error(self.tr("Cannot delete a status item that is in use."))
else: else:
self.listBox.takeTopLevelItem(iRow) self.listBox.takeTopLevelItem(iRow)
self._colDeleted.append(item.data(self.C_DATA, self.D_KEY))
self._changed = True self._changed = True
return return
@@ -543,24 +538,15 @@ class _StatusPage(NFixedPage):
def _moveItem(self, step: int) -> None: def _moveItem(self, step: int) -> None:
"""Move and item up or down step.""" """Move and item up or down step."""
selItem = self._getSelectedItem() if item := self._getSelectedItem():
if selItem is None: tIdx = self.listBox.indexOfTopLevelItem(item)
return nItm = self.listBox.topLevelItemCount()
nIdx = tIdx + step
tIndex = self.listBox.indexOfTopLevelItem(selItem) if (0 <= nIdx < nItm) and (cItem := self.listBox.takeTopLevelItem(tIdx)):
nChild = self.listBox.topLevelItemCount() self.listBox.insertTopLevelItem(nIdx, cItem)
nIndex = tIndex + step self.listBox.clearSelection()
if nIndex < 0 or nIndex >= nChild: cItem.setSelected(True)
return self._changed = True
cItem = self.listBox.takeTopLevelItem(tIndex)
self.listBox.insertTopLevelItem(nIndex, cItem)
self.listBox.clearSelection()
if cItem is not None:
cItem.setSelected(True)
self._changed = True
return return
def _getSelectedItem(self) -> QTreeWidgetItem | None: def _getSelectedItem(self) -> QTreeWidgetItem | None:
@@ -665,7 +651,7 @@ class _ReplacePage(NFixedPage):
return return
@property @property
def wasChanged(self) -> bool: def changed(self) -> bool:
"""The user changed these settings.""" """The user changed these settings."""
return self._changed return self._changed
@@ -673,7 +659,7 @@ class _ReplacePage(NFixedPage):
# Methods # Methods
## ##
def getNewList(self) -> dict: def getNewList(self) -> dict[str, str]:
"""Extract the list from the widget.""" """Extract the list from the widget."""
new = {} new = {}
for n in range(self.listBox.topLevelItemCount()): for n in range(self.listBox.topLevelItemCount()):