Add handlers for document label change

This commit is contained in:
Veronica Berglyd Olsen
2023-11-15 20:13:23 +01:00
parent 8508248f12
commit 6b9d92e75a
5 changed files with 72 additions and 48 deletions
+18 -18
View File
@@ -36,7 +36,7 @@ from typing import TYPE_CHECKING, ItemsView, Iterable, Iterator
from pathlib import Path
from novelwriter import SHARED
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout, nwTrinary
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
from novelwriter.error import logException
from novelwriter.common import checkInt, isHandle, isItemClass, isTitleTag, jsonEncode
from novelwriter.constants import nwFiles, nwKeyWords, nwRegEx, nwUnicode, nwHeaders
@@ -255,7 +255,7 @@ class NWIndex:
return False
# Keep a record of existing tags, and create a new item entry
itemTags = dict.fromkeys(self._itemIndex.allItemTags(tHandle), nwTrinary.NEGATIVE)
itemTags = dict.fromkeys(self._itemIndex.allItemTags(tHandle), False)
self._itemIndex.add(tHandle, tItem)
# Run word counter for the whole text
@@ -290,8 +290,7 @@ class NWIndex:
# Internal Indexer Helpers
##
def _scanActive(self, tHandle: str, nwItem: NWItem, text: str,
tags: dict[str, nwTrinary]) -> None:
def _scanActive(self, tHandle: str, nwItem: NWItem, text: str, tags: dict[str, bool]) -> None:
"""Scan an active document for meta data."""
nTitle = 0 # Line Number of the previous title
cTitle = TT_NONE # Tag of the current title
@@ -347,20 +346,18 @@ class NWIndex:
self._indexWordCounts(tHandle, text, cTitle)
# Prune no longer used tags
for tTag, tStatus in tags.items():
added = []
for tTag, isActive in tags.items():
updated = []
deleted = []
if tStatus == nwTrinary.NEGATIVE:
if isActive:
logger.debug("Added/updated tag '%s'", tTag)
updated.append(tTag)
else:
logger.debug("Removed tag '%s'", tTag)
del self._tagsIndex[tTag]
deleted.append(tTag)
elif tStatus == nwTrinary.POSITIVE:
logger.debug("Added new tag '%s'", tTag)
added.append(tTag)
else:
logger.debug("Unchanged tag '%s'", tTag)
if added or deleted:
SHARED.indexUpdatedTags(added, deleted)
if updated or deleted:
SHARED.indexUpdatedTags(updated, deleted)
return
@@ -397,7 +394,7 @@ class NWIndex:
return
def _indexKeyword(self, tHandle: str, line: str, sTitle: str,
itemClass: nwItemClass, tags: dict[str, nwTrinary]) -> None:
itemClass: nwItemClass, tags: dict[str, bool]) -> None:
"""Validate and save the information about a reference to a tag
in another file, or the setting of a tag in the file. A record
of active tags is updated so that no longer used tags can be
@@ -414,10 +411,9 @@ class NWIndex:
if tBits[0] == nwKeyWords.TAG_KEY:
tagName = tBits[1]
tagKey = tagName.lower()
self._tagsIndex.add(tagName, tHandle, sTitle, itemClass)
self._itemIndex.setHeadingTag(tHandle, sTitle, tagName)
tags[tagKey] = nwTrinary.NEUTRAL if tagKey in tags else nwTrinary.POSITIVE
tags[tagName.lower()] = True
else:
self._itemIndex.addHeadingRef(tHandle, sTitle, tBits[1:], tBits[0])
@@ -634,7 +630,11 @@ class NWIndex:
sTitle = self._tagsIndex.tagHeading(tagKey)
return tHandle, sTitle
def getTags(self, itemClass: nwItemClass) -> list[str]:
def getDocumentTags(self, tHandle: str | None) -> list[str]:
"""Return all tags used by a specific document."""
return self._itemIndex.allItemTags(tHandle) if tHandle else []
def getClassTags(self, itemClass: nwItemClass) -> list[str]:
"""Return all tags based on itemClass."""
return self._tagsIndex.filterTagNames(itemClass.name)
+1 -1
View File
@@ -2054,7 +2054,7 @@ class MetaCompleter(QMenu):
length = len(lookup)
suffix = ""
options = list(filter(
lambda x: lookup in x.lower(), SHARED.project.index.getTags(
lambda x: lookup in x.lower(), SHARED.project.index.getClassTags(
nwKeyWords.KEY_CLASS.get(kw.strip(), nwItemClass.NO_CLASS)
)
))[:15]
+50 -25
View File
@@ -102,8 +102,8 @@ class GuiDocViewerPanel(QWidget):
"""Run open project tasks."""
self.clearClassTabs()
for key, name, tClass, iItem, hItem in SHARED.project.index.getTagsData():
if tClass in self.kwTabs:
self.kwTabs[tClass].addEntry(key, name, iItem, hItem)
if tClass in self.kwTabs and iItem and hItem:
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
self._updateTabVisibility()
return
@@ -123,6 +123,16 @@ class GuiDocViewerPanel(QWidget):
# Public Slots
##
@pyqtSlot(str)
def projectItemChanged(self, tHandle: str) -> None:
"""Update meta data for project item."""
self.tabBackRefs.refreshDocument(tHandle)
for key in SHARED.project.index.getDocumentTags(tHandle):
name, tClass, iItem, hItem = SHARED.project.index.getSingleTag(key)
if tClass in self.kwTabs and iItem and hItem:
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
return
@pyqtSlot(str)
def updateHandle(self, tHandle: str | None) -> None:
"""Update the document handle."""
@@ -131,22 +141,19 @@ class GuiDocViewerPanel(QWidget):
return
@pyqtSlot(list, list)
def updateChangedTags(self, added: list[str], deleted: list[str]) -> None:
def updateChangedTags(self, updated: list[str], deleted: list[str]) -> None:
"""Forward tags changes to the lists."""
for key in added:
for key in updated:
name, tClass, iItem, hItem = SHARED.project.index.getSingleTag(key)
if tClass in self.kwTabs:
self.kwTabs[tClass].addEntry(key, name, iItem, hItem)
if tClass in self.kwTabs and iItem and hItem:
self.kwTabs[tClass].addUpdateEntry(key, name, iItem, hItem)
for key in deleted:
for cTab in self.kwTabs.values():
if cTab.removeEntry(key):
break
else:
logger.warning("Could not remove tag '%s' from view panel", key)
self._updateTabVisibility()
return
##
@@ -180,9 +187,7 @@ class _ViewPanelBackRefs(QTreeWidget):
cMg = CONFIG.pxInt(6)
# Content
self.setHeaderLabels([
self.tr("Heading"), "", "", self.tr("Document")
])
self.setHeaderLabels([self.tr("Heading"), "", "", self.tr("Document")])
self.setIndentation(0)
self.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
self.setIconSize(QSize(iPx, iPx))
@@ -233,6 +238,21 @@ class _ViewPanelBackRefs(QTreeWidget):
self.addTopLevelItem(trItem)
return
def refreshDocument(self, tHandle: str) -> None:
"""Refresh document meta data."""
nwItem = SHARED.project.tree[tHandle]
if nwItem:
docIcon = SHARED.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass,
nwItem.itemLayout, nwItem.mainHeading
)
for i in range(self.topLevelItemCount()):
trItem = self.topLevelItem(i)
if trItem and trItem.data(self.C_DATA, self.D_HANDLE) == tHandle:
trItem.setIcon(self.C_DOCUMENT, docIcon)
trItem.setText(self.C_DOCUMENT, nwItem.itemName)
return
# END Class _ViewPanelBackRefs
@@ -250,15 +270,13 @@ class _ViewPanelKeyWords(QTreeWidget):
def __init__(self, parent: QWidget, itemClass: nwItemClass) -> None:
super().__init__(parent=parent)
self._itemClass = nwItemClass
self._tagMap: dict[str, QTreeWidgetItem] = {}
iPx = SHARED.theme.baseIconSize
cMg = CONFIG.pxInt(6)
self.setHeaderLabels([
self.tr("Tag"), "", "", self.tr("Heading"), self.tr("Document")
])
self.setHeaderLabels([self.tr("Tag"), "", "", self.tr("Heading"), self.tr("Document")])
self.setIndentation(0)
self.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
self.setIconSize(QSize(iPx, iPx))
@@ -266,6 +284,7 @@ class _ViewPanelKeyWords(QTreeWidget):
self.setSortingEnabled(True)
self.sortByColumn(self.C_NAME, Qt.SortOrder.AscendingOrder)
# Set Header Sizes
treeHeader = self.header()
treeHeader.setStretchLastSection(True)
treeHeader.setSectionResizeMode(self.C_NAME, QHeaderView.ResizeMode.ResizeToContents)
@@ -274,6 +293,7 @@ class _ViewPanelKeyWords(QTreeWidget):
treeHeader.resizeSection(self.C_EDIT, iPx + cMg)
treeHeader.resizeSection(self.C_VIEW, iPx + cMg)
# Cache Icons Locally
self._classIcon = SHARED.theme.getIcon(nwLabels.CLASS_ICON[itemClass])
self._editIcon = SHARED.theme.getIcon("edit")
self._viewIcon = SHARED.theme.getIcon("view")
@@ -281,14 +301,11 @@ class _ViewPanelKeyWords(QTreeWidget):
return
def count(self) -> int:
"""Return the number of items in the list."""
return self.topLevelItemCount()
def addEntry(self, tag: str, name: str, iItem: IndexItem | None,
hItem: IndexHeading | None) -> None:
"""Add a tag entry to the list."""
if not iItem or not hItem:
return
def addUpdateEntry(self, tag: str, name: str, iItem: IndexItem, hItem: IndexHeading) -> None:
"""Add a new entry, or update an existing one."""
nwItem = iItem.item
docIcon = SHARED.theme.getItemIcon(
nwItem.itemType, nwItem.itemClass,
@@ -297,7 +314,13 @@ class _ViewPanelKeyWords(QTreeWidget):
iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) if nwItem.isDocumentLayout() else 5
hDec = SHARED.theme.getHeaderDecorationNarrow(iLevel)
trItem = QTreeWidgetItem()
# This can not use a get call to the dictionary as that creates
# some weird issue with Qt, so we need to do this with an if
if tag in self._tagMap:
trItem = self._tagMap[tag]
else:
trItem = QTreeWidgetItem()
trItem.setText(self.C_NAME, name)
trItem.setIcon(self.C_NAME, self._classIcon)
trItem.setIcon(self.C_EDIT, self._editIcon)
@@ -308,8 +331,10 @@ class _ViewPanelKeyWords(QTreeWidget):
trItem.setText(self.C_DOCUMENT, nwItem.itemName)
trItem.setData(self.C_DATA, self.D_TAG, tag)
trItem.setData(self.C_DATA, self.D_HANDLE, iItem.handle)
self.addTopLevelItem(trItem)
self._tagMap[tag] = trItem
if tag not in self._tagMap:
self.addTopLevelItem(trItem)
self._tagMap[tag] = trItem
return
+1 -2
View File
@@ -259,6 +259,7 @@ class GuiMain(QMainWindow):
self.projView.treeItemChanged.connect(self.docEditor.updateDocInfo)
self.projView.treeItemChanged.connect(self.docViewer.updateDocInfo)
self.projView.treeItemChanged.connect(self.itemDetails.updateViewBox)
self.projView.treeItemChanged.connect(self.docViewerPanel.projectItemChanged)
self.projView.rootFolderChanged.connect(self.outlineView.updateRootItem)
self.projView.rootFolderChanged.connect(self.novelView.updateRootItem)
self.projView.rootFolderChanged.connect(self.projView.updateRootItem)
@@ -788,11 +789,9 @@ class GuiMain(QMainWindow):
if not SHARED.hasProject:
logger.error("No project open")
return False
if tHandle is None and (self.docEditor.anyFocus() or self.isFocusMode):
tHandle = self.docEditor.docHandle
self.projView.renameTreeItem(tHandle)
return True
def rebuildTrees(self) -> None:
+2 -2
View File
@@ -215,9 +215,9 @@ class SharedData(QObject):
# Call-Back Functions
##
def indexUpdatedTags(self, added: list[str], deleted: list[str]) -> None:
def indexUpdatedTags(self, updated: list[str], deleted: list[str]) -> None:
"""Emit the index changed tags signal."""
self.indexChangedTags.emit(added, deleted)
self.indexChangedTags.emit(updated, deleted)
return
##