Simplify further how status labels are added and updated
This commit is contained in:
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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()):
|
||||||
|
|||||||
Reference in New Issue
Block a user