Allow refreshing a novel model node
This commit is contained in:
+18
-13
@@ -178,9 +178,9 @@ class Index:
|
||||
return self._rootChange.get(rootHandle, self._indexChange) > float(checkTime)
|
||||
return False
|
||||
|
||||
def refreshNovelModel(self, tHandle: str) -> None:
|
||||
def refreshNovelModel(self, tHandle: str | None) -> None:
|
||||
"""Refresh a novel model."""
|
||||
if model := self.getNovelModel(tHandle):
|
||||
if tHandle and (model := self.getNovelModel(tHandle)):
|
||||
logger.info("Refreshing novel model '%s'", tHandle)
|
||||
model.beginResetModel()
|
||||
model.clear()
|
||||
@@ -188,6 +188,19 @@ class Index:
|
||||
model.endResetModel()
|
||||
return
|
||||
|
||||
def updateNovelModelData(self, nwItem: NWItem) -> bool:
|
||||
"""Refresh a novel model."""
|
||||
if (
|
||||
(rHandle := nwItem.itemRoot)
|
||||
and (model := self._novelModels.get(rHandle))
|
||||
and (node := self._itemIndex[nwItem.itemHandle])
|
||||
and node.item.isDocumentLayout()
|
||||
and node.item.isActive
|
||||
):
|
||||
logger.info("Updating novel model data '%s'", nwItem.itemHandle)
|
||||
return model.refresh(node)
|
||||
return False
|
||||
|
||||
##
|
||||
# Load and Save Index to/from File
|
||||
##
|
||||
@@ -285,7 +298,6 @@ class Index:
|
||||
|
||||
# Keep a record of existing tags, and create a new item entry
|
||||
itemTags = dict.fromkeys(self._itemIndex.allItemTags(tHandle), False)
|
||||
itemHeadings = self._itemIndex.headingCount(tHandle)
|
||||
self._itemIndex.add(tHandle, tItem)
|
||||
|
||||
# Run word counter for the whole text
|
||||
@@ -309,10 +321,9 @@ class Index:
|
||||
else:
|
||||
self._scanActive(tHandle, tItem, text, itemTags)
|
||||
|
||||
if itemHeadings == self._itemIndex.headingCount(tHandle):
|
||||
pass
|
||||
else:
|
||||
tItem.notifyNovelStructureChange()
|
||||
if tItem.itemClass == nwItemClass.NOVEL and not blockSignal:
|
||||
if not self.updateNovelModelData(tItem):
|
||||
self.refreshNovelModel(tItem.itemRoot)
|
||||
|
||||
# Update timestamps for index changes
|
||||
nowTime = time()
|
||||
@@ -915,12 +926,6 @@ class ItemIndex:
|
||||
self._items[tHandle] = IndexNode(tHandle, nwItem)
|
||||
return
|
||||
|
||||
def headingCount(self, tHandle: str) -> int:
|
||||
"""Return the number of headings in this item."""
|
||||
if tHandle in self._items:
|
||||
return len(self._items[tHandle])
|
||||
return 0
|
||||
|
||||
def allItemTags(self, tHandle: str) -> list[str]:
|
||||
"""Get all tags set for headings of an item."""
|
||||
if tHandle in self._items:
|
||||
|
||||
@@ -30,7 +30,8 @@ from PyQt6.QtGui import QIcon, QPixmap
|
||||
|
||||
from novelwriter import SHARED
|
||||
from novelwriter.constants import nwStyles
|
||||
from novelwriter.core.indexdata import IndexNode
|
||||
from novelwriter.core.indexdata import IndexHeading, IndexNode
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.types import QtAlignRight
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -121,15 +122,68 @@ class NovelModel(QAbstractTableModel):
|
||||
handle = node.handle
|
||||
for key, head in node.items():
|
||||
if key != "T0000":
|
||||
iLevel = nwStyles.H_LEVEL.get(head.level, 0)
|
||||
more = self._columns - 1
|
||||
data = {}
|
||||
data[C_FACTOR*0 | R_TEXT] = head.title
|
||||
data[C_FACTOR*0 | R_ICON] = SHARED.theme.getHeaderDecoration(iLevel)
|
||||
data[C_FACTOR*1 | R_TEXT] = f"{head.mainCount:n}"
|
||||
data[C_FACTOR*1 | R_ALIGN] = QtAlignRight
|
||||
data[C_FACTOR*more | R_ICON] = self._more
|
||||
data[R_HANDLE] = handle
|
||||
data[R_KEY] = key
|
||||
self._rows.append(data)
|
||||
self._rows.append(self._generateEntry(handle, key, head))
|
||||
return
|
||||
|
||||
def refresh(self, node: IndexNode) -> bool:
|
||||
"""Refresh an index node."""
|
||||
handle = node.handle
|
||||
current = []
|
||||
for i, row in enumerate(self._rows):
|
||||
if row.get(R_HANDLE) == handle:
|
||||
current.append(i)
|
||||
|
||||
if current == []:
|
||||
return False
|
||||
|
||||
cols = self._columns - 1
|
||||
|
||||
first = current[0]
|
||||
last = current[-1]
|
||||
remains = []
|
||||
try:
|
||||
for key, head in node.items():
|
||||
if key != "T0000":
|
||||
if current:
|
||||
j = current.pop(0)
|
||||
self._rows[j] = self._generateEntry(handle, key, head)
|
||||
else:
|
||||
remains.append((key, head))
|
||||
|
||||
self.dataChanged.emit(self.createIndex(first, 0), self.createIndex(last, cols))
|
||||
|
||||
if remains:
|
||||
self.beginInsertRows(QModelIndex(), last, last + len(remains) - 1)
|
||||
for k, (key, head) in enumerate(remains, last + 1):
|
||||
self._rows.insert(k, self._generateEntry(handle, key, head))
|
||||
self.endInsertRows()
|
||||
elif current:
|
||||
self.beginRemoveRows(QModelIndex(), current[0], current[-1])
|
||||
del self._rows[current[0]:current[-1] + 1]
|
||||
self.endRemoveRows()
|
||||
|
||||
except Exception:
|
||||
# This is faster than to check for index boundaries.
|
||||
# We definitely don't want to cause a crash.
|
||||
logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _generateEntry(self, handle: str, key: str, heading: IndexHeading) -> dict:
|
||||
"""Generate a cache entry."""
|
||||
iLevel = nwStyles.H_LEVEL.get(heading.level, 0)
|
||||
more = self._columns - 1
|
||||
data = {}
|
||||
data[C_FACTOR*0 | R_TEXT] = heading.title
|
||||
data[C_FACTOR*0 | R_ICON] = SHARED.theme.getHeaderDecoration(iLevel)
|
||||
data[C_FACTOR*1 | R_TEXT] = f"{heading.mainCount:n}"
|
||||
data[C_FACTOR*1 | R_ALIGN] = QtAlignRight
|
||||
data[C_FACTOR*more | R_ICON] = self._more
|
||||
data[R_HANDLE] = handle
|
||||
data[R_KEY] = key
|
||||
return data
|
||||
|
||||
Reference in New Issue
Block a user