Handle item consistency in the nodes tree

This commit is contained in:
Veronica Berglyd Olsen
2024-11-19 17:47:21 +01:00
parent 53e70c118c
commit 442b065834
3 changed files with 352 additions and 414 deletions
+31 -15
View File
@@ -45,7 +45,6 @@ COL_MASK = 0x0100
C_LABEL_TEXT = 0x0000 | Qt.ItemDataRole.DisplayRole
C_LABEL_ICON = 0x0000 | Qt.ItemDataRole.DecorationRole
C_LABEL_TIP = 0x0000 | Qt.ItemDataRole.ToolTipRole
C_COUNT_TEXT = 0x0100 | Qt.ItemDataRole.DisplayRole
C_COUNT_ICON = 0x0100 | Qt.ItemDataRole.DecorationRole
C_COUNT_ALIGN = 0x0100 | Qt.ItemDataRole.TextAlignmentRole
@@ -95,34 +94,31 @@ class ProjectNode:
##
def refresh(self) -> None:
cache: dict[int, str | QIcon | Qt.AlignmentFlag] = {}
"""Refresh data values."""
# Label
cache[C_LABEL_ICON] = SHARED.theme.getItemIcon(
self._cache[C_LABEL_ICON] = SHARED.theme.getItemIcon(
self._item.itemType, self._item.itemClass,
self._item.itemLayout, self._item.mainHeading
)
cache[C_LABEL_TEXT] = self._item.itemName
cache[C_LABEL_TIP] = self._item.itemName
self._cache[C_LABEL_TEXT] = self._item.itemName
# Count
cache[C_COUNT_ALIGN] = QtAlignRight
self._cache[C_COUNT_ALIGN] = QtAlignRight
# Active
if self._item.isFileType():
if self._item.isActive:
cache[C_ACTIVE_ICON] = SHARED.theme.getIcon("checked")
self._cache[C_ACTIVE_ICON] = SHARED.theme.getIcon("checked")
else:
cache[C_ACTIVE_ICON] = SHARED.theme.getIcon("unchecked")
self._cache[C_ACTIVE_ICON] = SHARED.theme.getIcon("unchecked")
else:
cache[C_ACTIVE_ICON] = SHARED.theme.getIcon("noncheckable")
self._cache[C_ACTIVE_ICON] = SHARED.theme.getIcon("noncheckable")
# Status
sText, sIcon = self._item.getImportStatus()
cache[C_STATUS_ICON] = sIcon
cache[C_STATUS_TIP] = sText
self._cache[C_STATUS_ICON] = sIcon
self._cache[C_STATUS_TIP] = sText
self._cache = cache
self.updateCount()
return
@@ -145,7 +141,7 @@ class ProjectNode:
return len(self._children)
def data(self, column: int, role: Qt.ItemDataRole) -> T_NodeData:
""""""
"""Return cached node data."""
return self._cache.get(COL_MASK*column | role)
def parent(self) -> ProjectNode | None:
@@ -157,6 +153,7 @@ class ProjectNode:
return None
def allChildren(self) -> list[ProjectNode]:
"""Return a recursive list of all children."""
nodes: list[ProjectNode] = []
self._recursiveAppendChildren(nodes)
return nodes
@@ -168,15 +165,24 @@ class ProjectNode:
def addChild(self, child: ProjectNode, pos: int = -1) -> None:
"""Add a child item to this item."""
child._parent = self
self._updateRelationships(child)
if 0 <= pos < len(self._children):
self._children.insert(pos, child)
self._refreshChildrenPos()
else:
child._row = len(self._children)
self._children.append(child)
self.refresh()
self.updateCount()
return
def takeChild(self, pos: int) -> ProjectNode | None:
"""Remove a child item and return it."""
if 0 <= pos < len(self._children):
node = self._children.pop()
self._refreshChildrenPos()
return node
return None
def moveChild(self, source: int, target: int) -> None:
"""Move a child internally."""
count = len(self._children)
@@ -203,6 +209,16 @@ class ProjectNode:
child._row = n
return
def _updateRelationships(self, child: ProjectNode) -> None:
"""Update a child item's relationships."""
if self._parent:
child.item.setParent(self.item.itemHandle)
child.item.setRoot(self.item.itemRoot)
else:
child.item.setParent(None)
child.item.setRoot(child.item.itemHandle)
return
class ProjectModel(QAbstractItemModel):
+7 -38
View File
@@ -87,10 +87,10 @@ class NWTree:
# Properties
##
# @property
# def trashRoot(self) -> str | None:
# """Return the handle of the trash folder, or None."""
# return self._trash
@property
def trash(self) -> ProjectNode | None:
"""Return trash node, if it exists."""
return self._getTrashNode()
@property
def model(self) -> ProjectModel:
@@ -158,7 +158,6 @@ class NWTree:
nwItem.setType(itemType)
nwItem.setClass(itemClass)
if self.add(nwItem, pos):
self.updateItemData(tHandle)
self._project.setProjectChanged(True)
return tHandle
return None
@@ -238,23 +237,15 @@ class NWTree:
mark recovered files.
"""
storage = self._project.storage
files = set(storage.scanContent())
for tHandle in self._nodes:
if self.updateItemData(tHandle):
logger.debug("Checking item '%s' ... OK", tHandle)
files.discard(tHandle) # Remove it from the record
else:
logger.error("Checking item '%s' ... ERROR", tHandle)
self.__delitem__(tHandle) # The file will be re-added as orphaned
orphans = len(files)
remains = set(storage.scanContent()).difference(set(self._nodes.keys()))
orphans = len(remains)
if orphans == 0:
logger.info("Checked project files: OK")
return 0, 0
logger.warning("Found %d file(s) not tracked in project", orphans)
recovered = 0
for cHandle in files:
for cHandle in remains:
aDoc = storage.getDocument(cHandle)
aDoc.readDocument(isOrphan=True)
oName, oParent, oClass, oLayout = aDoc.getMeta()
@@ -282,7 +273,6 @@ class NWTree:
newItem.setClass(oClass)
newItem.setLayout(oLayout)
if self.add(newItem):
self.updateItemData(cHandle)
recovered += 1
return orphans, recovered
@@ -346,25 +336,6 @@ class NWTree:
# Tree Item Methods
##
def updateItemData(self, tHandle: str) -> bool:
"""Update the root item handle of a given item. Returns True if
a root was found and data updated, otherwise False.
"""
if tItem := self._items.get(tHandle):
iItem = tItem
for _ in range(MAX_DEPTH):
if iItem.itemParent is None:
tItem.setRoot(iItem.itemHandle)
tItem.setClassDefaults(iItem.itemClass)
return True
else:
iItem = self.__getitem__(iItem.itemParent)
if iItem is None:
return False
else:
raise RecursionError("Critical internal error")
return False
def checkType(self, tHandle: str, itemType: nwItemType) -> bool:
"""Check if item exists and is of the specified item type."""
tItem = self.__getitem__(tHandle)
@@ -515,8 +486,6 @@ class NWTree:
elif pHandle in items:
remains[handle] = item
logger.warning("Item '%s' found before its parent", handle)
else:
logger.error("Item '%s' has no parent in current tree", handle)
elif item.isRootType():
node = ProjectNode(item)
self._model.root.addChild(node)