diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index e4210dd2..503cb4ea 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -596,7 +596,7 @@ class NWIndex: return tRefs - def getBackReferenceList(self, tHandle: str) -> dict[str, str]: + def getBackReferenceList(self, tHandle: str) -> dict[str, tuple[str, IndexHeading]]: """Build a list of files referring back to our file, specified by tHandle. """ @@ -611,7 +611,7 @@ class NWIndex: for aHandle, sTitle, hItem in self._itemIndex.iterAllHeaders(): for aTag in hItem.references: if aTag in tTags and aHandle not in tRefs: - tRefs[aHandle] = sTitle + tRefs[aHandle] = (sTitle, hItem) return tRefs diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index 5376a872..75c088d6 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -56,6 +56,7 @@ logger = logging.getLogger(__name__) class GuiDocViewer(QTextBrowser): + documentLoaded = pyqtSignal(str) loadDocumentTagRequest = pyqtSignal(str, Enum) togglePanelVisibility = pyqtSignal() @@ -191,6 +192,7 @@ class GuiDocViewer(QTextBrowser): """Load text into the viewer from an item handle.""" if not SHARED.project.tree.checkType(tHandle, nwItemType.FILE): logger.warning("Item not found") + self.documentLoaded.emit("") return False logger.debug("Generating preview for item '%s'", tHandle) @@ -248,6 +250,7 @@ class GuiDocViewer(QTextBrowser): # the document dirty again to make sure it's re-rendered properly. self.redrawText() qApp.restoreOverrideCursor() + self.documentLoaded.emit(tHandle) return True diff --git a/novelwriter/gui/docviewerpanel.py b/novelwriter/gui/docviewerpanel.py index 36290b8c..2b780060 100644 --- a/novelwriter/gui/docviewerpanel.py +++ b/novelwriter/gui/docviewerpanel.py @@ -25,10 +25,14 @@ from __future__ import annotations import logging -from PyQt5.QtWidgets import QTabWidget, QVBoxLayout, QWidget +from PyQt5.QtCore import QSize, Qt, pyqtSlot +from PyQt5.QtWidgets import ( + QFrame, QHBoxLayout, QHeaderView, QTabWidget, QTreeWidget, QTreeWidgetItem, + QVBoxLayout, QWidget +) -from novelwriter import CONFIG -from novelwriter.constants import nwLabels, nwLists, trConst +from novelwriter import CONFIG, SHARED +from novelwriter.constants import nwHeaders, nwLabels, nwLists, trConst logger = logging.getLogger(__name__) @@ -70,6 +74,7 @@ class GuiDocViewerPanel(QWidget): hCol = self.palette().highlight().color() styleSheet = ( + "QTabWidget::pane {border: 0;} " "QTabWidget QTabBar::tab {" f"border: 0; padding: {vPx}px {rPx}px {vPx}px {lPx}px;" "} " @@ -81,25 +86,98 @@ class GuiDocViewerPanel(QWidget): return + ## + # Public Slots + ## + + @pyqtSlot(str) + def updateHandle(self, tHandle: str | None) -> None: + """Update the document handle.""" + self.tabBackRefs.refreshContent(tHandle or None) + return + # END Class GuiDocViewerPanel class _ViewPanelBackRefs(QWidget): + C_DATA = 0 + C_TITLE = 0 + C_NAME = 1 + C_SYNOPSIS = 2 + + D_HANDLE = Qt.ItemDataRole.UserRole + D_TITLE = Qt.ItemDataRole.UserRole + 1 + def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) + + iPx = SHARED.theme.baseIconSize + + # Content + self.listBox = QTreeWidget(self) + # self.listBox.setHeaderHidden(True) + # self.listBox.setColumnCount(3) + self.listBox.setHeaderLabels([ + self.tr("Title"), self.tr("Document"), self.tr("Synopsis") + ]) + self.listBox.setIndentation(0) + self.listBox.setIconSize(QSize(iPx, iPx)) + self.listBox.setFrameStyle(QFrame.Shape.NoFrame) + + treeHeader = self.listBox.header() + treeHeader.setStretchLastSection(True) + treeHeader.setSectionResizeMode(self.C_NAME, QHeaderView.ResizeMode.ResizeToContents) + treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.ResizeMode.ResizeToContents) + + fH1 = self.font() + fH1.setBold(True) + fH1.setUnderline(True) + + fH2 = self.font() + fH2.setBold(True) + + self._hFonts = [self.font(), fH1, fH2, self.font(), self.font()] + + # Assemble + self.outerBox = QHBoxLayout() + self.outerBox.addWidget(self.listBox) + self.outerBox.setContentsMargins(0, 0, 0, 0) + + self.setLayout(self.outerBox) + self.setContentsMargins(0, 0, 0, 0) + return - def refreshContent(self): - """""" - # theRefs = SHARED.project.index.getBackReferenceList(tHandle) - # theList = [] - # for tHandle in theRefs: - # tItem = SHARED.project.tree[tHandle] - # if tItem is not None: - # theList.append("%s" % ( - # tHandle, theRefs[tHandle], self.linkStyle, tItem.itemName - # )) + def refreshContent(self, dHandle: str | None) -> None: + """Update the content.""" + self.listBox.clear() + if dHandle: + refs = SHARED.project.index.getBackReferenceList(dHandle) + for tHandle, (sTitle, hItem) in refs.items(): + nwItem = SHARED.project.tree[tHandle] + if nwItem is None: + continue + + icon = SHARED.theme.getItemIcon( + nwItem.itemType, nwItem.itemClass, + nwItem.itemLayout, nwItem.mainHeading + ) + iLevel = nwHeaders.H_LEVEL.get(hItem.level, 0) + hDec = SHARED.theme.getHeaderDecoration(iLevel) + + trItem = QTreeWidgetItem() + trItem.setText(self.C_TITLE, hItem.title) + trItem.setData(self.C_TITLE, Qt.ItemDataRole.DecorationRole, hDec) + trItem.setFont(self.C_TITLE, self._hFonts[iLevel]) + trItem.setIcon(self.C_NAME, icon) + trItem.setText(self.C_NAME, nwItem.itemName) + trItem.setText(self.C_SYNOPSIS, hItem.synopsis) + + trItem.setData(self.C_DATA, self.D_HANDLE, tHandle) + trItem.setData(self.C_DATA, self.D_TITLE, sTitle) + + self.listBox.addTopLevelItem(trItem) return # END Class _ViewPanelBackRefs diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index b8e4b8ed..8c146ac8 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -277,6 +277,7 @@ class GuiMain(QMainWindow): self.docEditor.closeDocumentRequest.connect(self.closeDocEditor) self.docEditor.toggleFocusModeRequest.connect(self.toggleFocusMode) + self.docViewer.documentLoaded.connect(self.docViewerPanel.updateHandle) self.docViewer.loadDocumentTagRequest.connect(self._followTag) self.docViewer.togglePanelVisibility.connect(self._toggleViewerPanelVisibility)