From 21d91193a2b557a54d4d56eb2d533d4d1b270162 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 2 Jan 2021 17:33:53 +0100 Subject: [PATCH] Keep project tree and outline tree in sync --- nw/gui/outline.py | 10 +++++----- nw/gui/projtree.py | 29 +++++++++++++++++++++++++++-- nw/guimain.py | 29 +++++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/nw/gui/outline.py b/nw/gui/outline.py index cb259565..c2ab5fa5 100644 --- a/nw/gui/outline.py +++ b/nw/gui/outline.py @@ -165,7 +165,7 @@ class GuiOutline(QTreeWidget): return - def refreshTree(self, overRide=False): + def refreshTree(self, overRide=False, novelChanged=False): """Called whenever the Outline tab is activated and controls what data to load, and if necessary, force a rebuild of the tree. @@ -177,10 +177,10 @@ class GuiOutline(QTreeWidget): self.firstView = False return - # If the novel index has changed since the tree was last built, - # we rebuild the tree from the updated index. - idxChanged = self.theParent.theIndex.novelChangedSince(self.lastBuild) - doBuild = idxChanged and self.theProject.autoOutline + # If the novel index or novel tree has changed since the tree + # was last built, we rebuild the tree from the updated index. + indexChanged = self.theIndex.novelChangedSince(self.lastBuild) + doBuild = (novelChanged or indexChanged) and self.theProject.autoOutline if doBuild or overRide: logger.debug("Rebuilding Project Outline") self._populateTree() diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index a74493ff..7180f9d1 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -31,7 +31,7 @@ import logging from time import time -from PyQt5.QtCore import Qt, QSize +from PyQt5.QtCore import Qt, QSize, pyqtSignal from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import ( qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction @@ -51,6 +51,9 @@ class GuiProjectTree(QTreeWidget): C_EXPORT = 2 C_FLAGS = 3 + novelItemChanged = pyqtSignal() + noteItemChanged = pyqtSignal() + def __init__(self, theParent): QTreeWidget.__init__(self, theParent) @@ -285,8 +288,11 @@ class GuiProjectTree(QTreeWidget): pHandle = nwItem.itemParent if pHandle is not None and pHandle in self._treeMap: self._treeMap[pHandle].setExpanded(True) + + self._emitItemChange(tHandle) self.clearSelection() trItem.setSelected(True) + return True def moveTreeItem(self, nStep): @@ -327,6 +333,7 @@ class GuiProjectTree(QTreeWidget): self.clearSelection() cItem.setSelected(True) self._setTreeChanged(True) + self._emitItemChange(tHandle) return True @@ -788,6 +795,10 @@ class GuiProjectTree(QTreeWidget): else: self.theIndex.reIndexHandle(sHandle) + # Trigger dependent updates + self._setTreeChanged(True) + self._emitItemChange(sHandle) + else: theEvent.ignore() logger.debug("Drag'n'drop of item %s not accepted" % sHandle) @@ -915,7 +926,6 @@ class GuiProjectTree(QTreeWidget): pHandle = trItemP.data(self.C_NAME, Qt.UserRole) nwItemS.setParent(pHandle) self.setTreeItemValues(tHandle) - self._setTreeChanged(True) logger.debug("The parent of item %s has been changed to %s" % (tHandle, pHandle)) @@ -930,6 +940,21 @@ class GuiProjectTree(QTreeWidget): self.theProject.setProjectChanged(True) return + def _emitItemChange(self, tHandle): + """Emit an item change signal for a given handle. + """ + nwItem = self.theProject.projTree[tHandle] + if nwItem is None: + return + + if nwItem.itemType == nwItemType.FILE: + if nwItem.itemClass == nwItemClass.NOVEL: + self.novelItemChanged.emit() + else: + self.noteItemChanged.emit() + + return + # END Class GuiProjectTree class GuiProjectTreeMenu(QMenu): diff --git a/nw/guimain.py b/nw/guimain.py index 56185e89..a2ce1444 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -32,7 +32,7 @@ import os from datetime import datetime from time import time -from PyQt5.QtCore import Qt, QTimer, QThreadPool +from PyQt5.QtCore import Qt, QTimer, QThreadPool, pyqtSlot from PyQt5.QtGui import QIcon, QPixmap, QColor, QKeySequence, QCursor from PyQt5.QtWidgets import ( qApp, QMainWindow, QVBoxLayout, QWidget, QSplitter, QFileDialog, QShortcut, @@ -193,6 +193,7 @@ class GuiMain(QMainWindow): # Initialise the Project Tree self.treeView.itemSelectionChanged.connect(self._treeSingleClick) self.treeView.itemDoubleClicked.connect(self._treeDoubleClick) + self.treeView.novelItemChanged.connect(self._treeNovelItemChanged) self.rebuildTrees() # Set Main Window Elements @@ -1319,9 +1320,10 @@ class GuiMain(QMainWindow): return ## - # Signal Handlers + # Slots ## + @pyqtSlot() def _treeSingleClick(self): """Single click on a project tree item just updates the details panel below the tree. @@ -1331,12 +1333,14 @@ class GuiMain(QMainWindow): self.treeMeta.updateViewBox(sHandle) return + @pyqtSlot("QTreeWidgetItem*", int) def _treeDoubleClick(self, tItem, colNo): """The user double-clicked an item in the tree. If it is a file, we open it. Otherwise, we do nothing. """ tHandle = tItem.data(self.treeView.C_NAME, Qt.UserRole) logger.verbose("User double clicked tree item with handle %s" % tHandle) + nwItem = self.theProject.projTree[tHandle] if nwItem is not None: if nwItem.itemType == nwItemType.FILE: @@ -1347,6 +1351,20 @@ class GuiMain(QMainWindow): return + @pyqtSlot() + def _treeNovelItemChanged(self): + """Triggered when there is a change to a novel item in the + project tree. + """ + if self.mainTabs.currentIndex() == self.idxTabProj: + logger.verbose("Novel tree changed while Outline tab active") + if self.hasProject: + self.treeView.flushTreeOrder() + self.projView.refreshTree(novelChanged=True) + + return + + @pyqtSlot() def _treeKeyPressReturn(self): """The user pressed return on an item in the tree. If it is a file, we open it. Otherwise, we do nothing. Pressing return does @@ -1354,6 +1372,7 @@ class GuiMain(QMainWindow): """ tHandle = self.treeView.getSelectedHandle() logger.verbose("User pressed return on tree item with handle %s" % tHandle) + nwItem = self.theProject.projTree[tHandle] if nwItem is not None: if nwItem.itemType == nwItemType.FILE: @@ -1361,8 +1380,10 @@ class GuiMain(QMainWindow): self.openDocument(tHandle, changeFocus=False, doScroll=False) else: logger.verbose("Requested item %s is a folder" % tHandle) + return + @pyqtSlot() def _keyPressEscape(self): """When the escape key is pressed somewhere in the main window, do the following, in order: @@ -1371,8 +1392,10 @@ class GuiMain(QMainWindow): self.docEditor.closeSearch() elif self.isFocusMode: self.toggleFocusMode() + return + @pyqtSlot(int) def _mainTabChanged(self, tabIndex): """Activated when the main window tab is changed. """ @@ -1382,8 +1405,10 @@ class GuiMain(QMainWindow): logger.verbose("Project outline tab activated") if self.hasProject: self.projView.refreshTree() + return + @pyqtSlot(int) def _projTabsChanged(self, tabIndex): """Activated when the project view tab is changed. """