Refresh model when document heading count changes
This commit is contained in:
+41
-36
@@ -120,35 +120,6 @@ class Index:
|
|||||||
self._generateNovelModel(tHandle)
|
self._generateNovelModel(tHandle)
|
||||||
return self._novelModels.get(tHandle)
|
return self._novelModels.get(tHandle)
|
||||||
|
|
||||||
def refreshNovelModel(self, tHandle: str) -> None:
|
|
||||||
"""Refresh a novel model."""
|
|
||||||
if model := self.getNovelModel(tHandle):
|
|
||||||
logger.debug("Refreshing novel model '%s'", tHandle)
|
|
||||||
model.beginResetModel()
|
|
||||||
model.clear()
|
|
||||||
self._appendSubTreeToModel(tHandle, model)
|
|
||||||
model.endResetModel()
|
|
||||||
return
|
|
||||||
|
|
||||||
def _generateNovelModel(self, tHandle: str) -> None:
|
|
||||||
"""Generate a novel model for a specific handle."""
|
|
||||||
if (item := SHARED.project.tree[tHandle]) and item.isRootType() and item.isNovelLike():
|
|
||||||
model = NovelModel()
|
|
||||||
self._appendSubTreeToModel(tHandle, model)
|
|
||||||
self._novelModels[tHandle] = model
|
|
||||||
return
|
|
||||||
|
|
||||||
def _appendSubTreeToModel(self, tHandle: str, model: NovelModel) -> None:
|
|
||||||
"""Append all active novel documents to a novel model."""
|
|
||||||
for handle in SHARED.project.tree.subTree(tHandle):
|
|
||||||
if (
|
|
||||||
(node := self._itemIndex[handle])
|
|
||||||
and node.item.isDocumentLayout()
|
|
||||||
and node.item.isActive
|
|
||||||
):
|
|
||||||
model.append(node)
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Public Methods
|
# Public Methods
|
||||||
##
|
##
|
||||||
@@ -207,6 +178,16 @@ class Index:
|
|||||||
return self._rootChange.get(rootHandle, self._indexChange) > float(checkTime)
|
return self._rootChange.get(rootHandle, self._indexChange) > float(checkTime)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def refreshNovelModel(self, tHandle: str) -> None:
|
||||||
|
"""Refresh a novel model."""
|
||||||
|
if model := self.getNovelModel(tHandle):
|
||||||
|
logger.info("Refreshing novel model '%s'", tHandle)
|
||||||
|
model.beginResetModel()
|
||||||
|
model.clear()
|
||||||
|
self._appendSubTreeToModel(tHandle, model)
|
||||||
|
model.endResetModel()
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Load and Save Index to/from File
|
# Load and Save Index to/from File
|
||||||
##
|
##
|
||||||
@@ -304,6 +285,7 @@ class Index:
|
|||||||
|
|
||||||
# Keep a record of existing tags, and create a new item entry
|
# Keep a record of existing tags, and create a new item entry
|
||||||
itemTags = dict.fromkeys(self._itemIndex.allItemTags(tHandle), False)
|
itemTags = dict.fromkeys(self._itemIndex.allItemTags(tHandle), False)
|
||||||
|
itemHeadings = self._itemIndex.headingCount(tHandle)
|
||||||
self._itemIndex.add(tHandle, tItem)
|
self._itemIndex.add(tHandle, tItem)
|
||||||
|
|
||||||
# Run word counter for the whole text
|
# Run word counter for the whole text
|
||||||
@@ -327,6 +309,11 @@ class Index:
|
|||||||
else:
|
else:
|
||||||
self._scanActive(tHandle, tItem, text, itemTags)
|
self._scanActive(tHandle, tItem, text, itemTags)
|
||||||
|
|
||||||
|
if itemHeadings == self._itemIndex.headingCount(tHandle):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
tItem.notifyNovelStructureChange()
|
||||||
|
|
||||||
# Update timestamps for index changes
|
# Update timestamps for index changes
|
||||||
nowTime = time()
|
nowTime = time()
|
||||||
self._indexChange = nowTime
|
self._indexChange = nowTime
|
||||||
@@ -467,6 +454,25 @@ class Index:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _generateNovelModel(self, tHandle: str) -> None:
|
||||||
|
"""Generate a novel model for a specific handle."""
|
||||||
|
if (item := SHARED.project.tree[tHandle]) and item.isRootType() and item.isNovelLike():
|
||||||
|
model = NovelModel()
|
||||||
|
self._appendSubTreeToModel(tHandle, model)
|
||||||
|
self._novelModels[tHandle] = model
|
||||||
|
return
|
||||||
|
|
||||||
|
def _appendSubTreeToModel(self, tHandle: str, model: NovelModel) -> None:
|
||||||
|
"""Append all active novel documents to a novel model."""
|
||||||
|
for handle in SHARED.project.tree.subTree(tHandle):
|
||||||
|
if (
|
||||||
|
(node := self._itemIndex[handle])
|
||||||
|
and node.item.isDocumentLayout()
|
||||||
|
and node.item.isActive
|
||||||
|
):
|
||||||
|
model.append(node)
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Check @ Lines
|
# Check @ Lines
|
||||||
##
|
##
|
||||||
@@ -607,13 +613,6 @@ class Index:
|
|||||||
hCount[iLevel] += 1
|
hCount[iLevel] += 1
|
||||||
return hCount
|
return hCount
|
||||||
|
|
||||||
def getHandleHeaderCount(self, tHandle: str) -> int:
|
|
||||||
"""Get the number of headers in an item."""
|
|
||||||
tItem = self._itemIndex[tHandle]
|
|
||||||
if isinstance(tItem, IndexNode):
|
|
||||||
return len(tItem)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def getTableOfContents(
|
def getTableOfContents(
|
||||||
self, rHandle: str | None, maxDepth: int, activeOnly: bool = True
|
self, rHandle: str | None, maxDepth: int, activeOnly: bool = True
|
||||||
) -> list[tuple[str, int, str, int]]:
|
) -> list[tuple[str, int, str, int]]:
|
||||||
@@ -916,6 +915,12 @@ class ItemIndex:
|
|||||||
self._items[tHandle] = IndexNode(tHandle, nwItem)
|
self._items[tHandle] = IndexNode(tHandle, nwItem)
|
||||||
return
|
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]:
|
def allItemTags(self, tHandle: str) -> list[str]:
|
||||||
"""Get all tags set for headings of an item."""
|
"""Get all tags set for headings of an item."""
|
||||||
if tHandle in self._items:
|
if tHandle in self._items:
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
editedStatusChanged = pyqtSignal(bool)
|
editedStatusChanged = pyqtSignal(bool)
|
||||||
itemHandleChanged = pyqtSignal(str)
|
itemHandleChanged = pyqtSignal(str)
|
||||||
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
loadDocumentTagRequest = pyqtSignal(str, Enum)
|
||||||
novelItemMetaChanged = pyqtSignal(str)
|
|
||||||
novelStructureChanged = pyqtSignal()
|
novelStructureChanged = pyqtSignal()
|
||||||
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
|
openDocumentRequest = pyqtSignal(str, Enum, str, bool)
|
||||||
requestNewNoteCreation = pyqtSignal(str, nwItemClass)
|
requestNewNoteCreation = pyqtSignal(str, nwItemClass)
|
||||||
@@ -498,18 +497,8 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
|
|
||||||
self.setDocumentChanged(False)
|
self.setDocumentChanged(False)
|
||||||
self.docTextChanged.emit(self._docHandle, self._lastEdit)
|
self.docTextChanged.emit(self._docHandle, self._lastEdit)
|
||||||
|
|
||||||
oldCount = SHARED.project.index.getHandleHeaderCount(tHandle)
|
|
||||||
SHARED.project.index.scanText(tHandle, text)
|
SHARED.project.index.scanText(tHandle, text)
|
||||||
newCount = SHARED.project.index.getHandleHeaderCount(tHandle)
|
|
||||||
|
|
||||||
if self._nwItem.itemClass == nwItemClass.NOVEL:
|
|
||||||
if oldCount == newCount:
|
|
||||||
self.novelItemMetaChanged.emit(tHandle)
|
|
||||||
else:
|
|
||||||
self.novelStructureChanged.emit()
|
|
||||||
|
|
||||||
# Update the status bar
|
|
||||||
self.updateStatusMessage.emit(self.tr("Saved Document: {0}").format(self._nwItem.itemName))
|
self.updateStatusMessage.emit(self.tr("Saved Document: {0}").format(self._nwItem.itemName))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ class GuiNovelView(QWidget):
|
|||||||
def updateTheme(self) -> None:
|
def updateTheme(self) -> None:
|
||||||
"""Update theme elements."""
|
"""Update theme elements."""
|
||||||
self.novelBar.updateTheme()
|
self.novelBar.updateTheme()
|
||||||
self.refreshTree()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def initSettings(self) -> None:
|
def initSettings(self) -> None:
|
||||||
@@ -174,12 +173,6 @@ class GuiNovelView(QWidget):
|
|||||||
self.novelTree.setActiveHandle(tHandle)
|
self.novelTree.setActiveHandle(tHandle)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def refreshTree(self) -> None:
|
|
||||||
"""Refresh the current tree."""
|
|
||||||
# self.novelTree.refreshTree(rootHandle=SHARED.project.data.getLastHandle("novelTree"))
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot(str, Enum)
|
@pyqtSlot(str, Enum)
|
||||||
def updateRootItem(self, tHandle: str, change: nwChange) -> None:
|
def updateRootItem(self, tHandle: str, change: nwChange) -> None:
|
||||||
"""If any root item changes, rebuild the novel root menu."""
|
"""If any root item changes, rebuild the novel root menu."""
|
||||||
@@ -296,6 +289,8 @@ class GuiNovelToolBar(QWidget):
|
|||||||
self.novelValue.refreshNovelList()
|
self.novelValue.refreshNovelList()
|
||||||
self.tbNovel.setVisible(self.novelValue.count() > 1)
|
self.tbNovel.setVisible(self.novelValue.count() > 1)
|
||||||
|
|
||||||
|
self._forceRefreshNovelTree()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def clearContent(self) -> None:
|
def clearContent(self) -> None:
|
||||||
@@ -328,7 +323,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
refresh when content structure changes.
|
refresh when content structure changes.
|
||||||
"""
|
"""
|
||||||
self._active = state
|
self._active = state
|
||||||
if self._active:
|
if self._active and self._refresh.get(self.novelValue.handle, False):
|
||||||
self._refreshNovelTree(self.novelValue.handle)
|
self._refreshNovelTree(self.novelValue.handle)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -339,21 +334,19 @@ class GuiNovelToolBar(QWidget):
|
|||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _forceRefreshNovelTree(self) -> None:
|
def _forceRefreshNovelTree(self) -> None:
|
||||||
"""Rebuild the current tree."""
|
"""Rebuild the current tree."""
|
||||||
self._refresh[self.novelValue.handle] = True
|
if tHandle := self.novelValue.handle:
|
||||||
self._refreshNovelTree(self.novelValue.handle)
|
SHARED.project.index.refreshNovelModel(tHandle)
|
||||||
|
self._refresh[tHandle] = False
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def _refreshNovelTree(self, tHandle: str) -> None:
|
def _refreshNovelTree(self, tHandle: str) -> None:
|
||||||
"""Refresh or schedule refresh of a novel tree."""
|
"""Refresh or schedule refresh of a novel tree."""
|
||||||
if tHandle:
|
if self._active:
|
||||||
if self._active:
|
SHARED.project.index.refreshNovelModel(tHandle)
|
||||||
if self._refresh.get(tHandle, False):
|
self._refresh[tHandle] = False
|
||||||
SHARED.project.index.refreshNovelModel(tHandle)
|
else:
|
||||||
self._refresh[tHandle] = False
|
self._refresh[tHandle] = True
|
||||||
else:
|
|
||||||
self._refresh[tHandle] = True
|
|
||||||
print(self._refresh)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
|
|||||||
@@ -694,7 +694,7 @@ class GuiIcons:
|
|||||||
else:
|
else:
|
||||||
icon = self._loadIcon(name, color, w, h)
|
icon = self._loadIcon(name, color, w, h)
|
||||||
self._qIcons[key] = icon
|
self._qIcons[key] = icon
|
||||||
logger.info("Icon: %s", key)
|
logger.debug("Icon: %s", key)
|
||||||
return icon
|
return icon
|
||||||
|
|
||||||
def getToggleIcon(self, name: str, size: tuple[int, int], color: str | None = None) -> QIcon:
|
def getToggleIcon(self, name: str, size: tuple[int, int], color: str | None = None) -> QIcon:
|
||||||
|
|||||||
@@ -247,8 +247,6 @@ class GuiMain(QMainWindow):
|
|||||||
self.docEditor.itemHandleChanged.connect(self.novelView.setActiveHandle)
|
self.docEditor.itemHandleChanged.connect(self.novelView.setActiveHandle)
|
||||||
self.docEditor.itemHandleChanged.connect(self.projView.setActiveHandle)
|
self.docEditor.itemHandleChanged.connect(self.projView.setActiveHandle)
|
||||||
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
self.docEditor.loadDocumentTagRequest.connect(self._followTag)
|
||||||
self.docEditor.novelItemMetaChanged.connect(self.novelView.updateNovelItemMeta)
|
|
||||||
self.docEditor.novelStructureChanged.connect(self.novelView.refreshTree)
|
|
||||||
self.docEditor.openDocumentRequest.connect(self._openDocument)
|
self.docEditor.openDocumentRequest.connect(self._openDocument)
|
||||||
self.docEditor.requestNewNoteCreation.connect(SHARED.createNewNote)
|
self.docEditor.requestNewNoteCreation.connect(SHARED.createNewNote)
|
||||||
self.docEditor.requestNextDocument.connect(self.openNextDocument)
|
self.docEditor.requestNextDocument.connect(self.openNextDocument)
|
||||||
@@ -735,7 +733,6 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
SHARED.project.index.rebuild()
|
SHARED.project.index.rebuild()
|
||||||
SHARED.project.tree.refreshAllItems()
|
SHARED.project.tree.refreshAllItems()
|
||||||
self.novelView.refreshTree()
|
|
||||||
|
|
||||||
tEnd = time()
|
tEnd = time()
|
||||||
self.mainStatus.setStatusMessage(
|
self.mainStatus.setStatusMessage(
|
||||||
@@ -1163,20 +1160,23 @@ class GuiMain(QMainWindow):
|
|||||||
elif view == nwView.PROJECT:
|
elif view == nwView.PROJECT:
|
||||||
self.mainStack.setCurrentWidget(self.splitMain)
|
self.mainStack.setCurrentWidget(self.splitMain)
|
||||||
self.projStack.setCurrentWidget(self.projView)
|
self.projStack.setCurrentWidget(self.projView)
|
||||||
self.novelView.setActive(False)
|
|
||||||
elif view == nwView.NOVEL:
|
elif view == nwView.NOVEL:
|
||||||
self.mainStack.setCurrentWidget(self.splitMain)
|
self.mainStack.setCurrentWidget(self.splitMain)
|
||||||
self.projStack.setCurrentWidget(self.novelView)
|
self.projStack.setCurrentWidget(self.novelView)
|
||||||
self.novelView.setActive(True)
|
|
||||||
elif view == nwView.SEARCH:
|
elif view == nwView.SEARCH:
|
||||||
self.mainStack.setCurrentWidget(self.splitMain)
|
self.mainStack.setCurrentWidget(self.splitMain)
|
||||||
self.projStack.setCurrentWidget(self.projSearch)
|
self.projStack.setCurrentWidget(self.projSearch)
|
||||||
self.projSearch.beginSearch(
|
self.projSearch.beginSearch(
|
||||||
self.docEditor.getSelectedText() if self.docEditor.anyFocus() else ""
|
self.docEditor.getSelectedText() if self.docEditor.anyFocus() else ""
|
||||||
)
|
)
|
||||||
self.novelView.setActive(False)
|
|
||||||
elif view == nwView.OUTLINE:
|
elif view == nwView.OUTLINE:
|
||||||
self.mainStack.setCurrentWidget(self.outlineView)
|
self.mainStack.setCurrentWidget(self.outlineView)
|
||||||
|
|
||||||
|
# Set active status
|
||||||
|
isMain = self.mainStack.currentWidget() == self.splitMain
|
||||||
|
isNovel = self.projStack.currentWidget() == self.novelView
|
||||||
|
self.novelView.setActive(isMain and isNovel)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot(nwDocAction)
|
@pyqtSlot(nwDocAction)
|
||||||
|
|||||||
@@ -708,15 +708,6 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
|||||||
assert wC == 12 # Words in text and title only
|
assert wC == 12 # Words in text and title only
|
||||||
assert pC == 2 # Paragraphs in text only
|
assert pC == 2 # Paragraphs in text only
|
||||||
|
|
||||||
# getItemData + getHandleHeaderCount
|
|
||||||
# ==================================
|
|
||||||
|
|
||||||
item = index.getItemData(nHandle)
|
|
||||||
assert isinstance(item, IndexNode)
|
|
||||||
assert item.headings() == ["T0001"]
|
|
||||||
assert index.getHandleHeaderCount(nHandle) == 1
|
|
||||||
assert index.getHandleHeaderCount("foo") == 0
|
|
||||||
|
|
||||||
# getReferences
|
# getReferences
|
||||||
# =============
|
# =============
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user