diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index b05fa952..b9b1e5f9 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -208,8 +208,6 @@ class NWIndex(): text. """ theItem = self.theProject.projTree[tHandle] - theRoot = self.theProject.projTree.getRootItem(tHandle) - if theItem is None: logger.info("Not indexing unknown item '%s'", tHandle) return False @@ -232,7 +230,7 @@ class NWIndex(): if self.theProject.projTree.isTrashRoot(theItem.itemParent): logger.debug("Not indexing trash item '%s'", tHandle) return False - if theRoot.itemClass == nwItemClass.ARCHIVE: + if self.theProject.projTree.getItemClass(tHandle) == nwItemClass.ARCHIVE: logger.debug("Not indexing archived item '%s'", tHandle) return False diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index a3c16de6..47397e5c 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -608,6 +608,7 @@ class NWProject(): self.theParent.setStatus(self.tr("Opened Project: {0}").format(self.projName)) self._scanProjectFolder() + self._checkProjectTree() self._loadProjectLocalisation() self.updateWordCounts() @@ -1340,6 +1341,17 @@ class NWProject(): xEntry.text = aValue return + def _checkProjectTree(self): + """Check the project tree and make sure all items have sensible + values. + """ + for tItem in self.projTree: + tHandle = tItem.itemHandle + logger.verbose("Checking item '%s'", tHandle) + if tItem.itemRoot is None: + self.projTree.updateItemRoot(tHandle) + logger.warning("Corrected the root setting of item '%s'", tHandle) + def _scanProjectFolder(self): """Scan the project folder and check that the files in it are also in the project XML file. If they aren't, import them as diff --git a/novelwriter/core/tree.py b/novelwriter/core/tree.py index f429873b..3316f993 100644 --- a/novelwriter/core/tree.py +++ b/novelwriter/core/tree.py @@ -47,7 +47,7 @@ class NWTree(): self._projTree = {} # Holds all the items of the project self._treeOrder = [] # The order of the tree items on the tree view - self._treeRoots = [] # The root items of the tree + self._treeRoots = {} # The root items of the tree self._trashRoot = None # The handle of the trash root folder self._archRoot = None # The handle of the archive root folder self._theIndex = 0 # The current iterator index @@ -67,7 +67,7 @@ class NWTree(): """ self._projTree = {} self._treeOrder = [] - self._treeRoots = [] + self._treeRoots = {} self._trashRoot = None self._archRoot = None self._theIndex = 0 @@ -98,7 +98,7 @@ class NWTree(): if nwItem.itemType == nwItemType.ROOT: logger.verbose("Item '%s' is a root item", str(tHandle)) - self._treeRoots.append(tHandle) + self._treeRoots[tHandle] = nwItem if nwItem.itemClass == nwItemClass.ARCHIVE: logger.verbose("Item '%s' is the archive folder", str(tHandle)) self._archRoot = tHandle @@ -253,20 +253,34 @@ class NWTree(): return tItem.itemHandle return None - def getRootItem(self, tHandle): - """Iterate upwards in the tree until we find the item with - parent None, the root item. We do this with a for loop with a - maximum depth to make infinite loops impossible. + def isRoot(self, tHandle): + """Check if a handle is a root item. + """ + return tHandle in self._treeRoots + + def updateItemRoot(self, tHandle): + """Update the root item handle of a given item. + """ + tItem = self.__getitem__(tHandle) + iItem = tItem + if iItem is not None: + for _ in range(nwConst.MAX_DEPTH + 1): + if iItem.itemParent is None: + tItem.setRoot(iItem.itemHandle) + return iItem.itemHandle + else: + tHandle = iItem.itemParent + iItem = self.__getitem__(tHandle) + return None + + def getItemClass(self, tHandle): + """Return the class of a given item. """ tItem = self.__getitem__(tHandle) if tItem is not None: - for i in range(nwConst.MAX_DEPTH + 1): - if tItem.itemParent is None: - return tItem - else: - tHandle = tItem.itemParent - tItem = self.__getitem__(tHandle) - return None + if tItem.itemRoot in self._treeRoots: + return self._treeRoots[tItem.itemRoot].itemClass + return nwItemClass.NO_CLASS def getItemPath(self, tHandle): """Iterate upwards in the tree until we find the item with @@ -405,7 +419,7 @@ class NWTree(): return if tHandle in self._treeRoots: - self._treeRoots.remove(tHandle) + del self._treeRoots[tHandle] if tHandle == self._trashRoot: self._trashRoot = None if tHandle == self._archRoot: diff --git a/novelwriter/tools/build.py b/novelwriter/tools/build.py index 7ebed572..0bb593a6 100644 --- a/novelwriter/tools/build.py +++ b/novelwriter/tools/build.py @@ -799,8 +799,7 @@ class GuiBuildNovel(QDialog): if isNovel and not novelFiles: return False - rootItem = self.theProject.projTree.getRootItem(theItem.itemHandle) - if rootItem.itemClass == nwItemClass.ARCHIVE: + if self.theProject.projTree.getItemClass(theItem.itemHandle) == nwItemClass.ARCHIVE: return False return True diff --git a/tests/reference/coreProject_NewRoot_nwProject.nwx b/tests/reference/coreProject_NewRoot_nwProject.nwx index cf55336e..b69de2b3 100644 --- a/tests/reference/coreProject_NewRoot_nwProject.nwx +++ b/tests/reference/coreProject_NewRoot_nwProject.nwx @@ -1,5 +1,5 @@ - + New Project @@ -39,7 +39,7 @@ Main - + Novel @@ -72,19 +72,35 @@ New Scene - + + + Novel + + + + Plot + + + + Character + + + + World + + Timeline - + Object - + Custom1 - + Custom2 diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 9a7f7b88..5e5d3eb9 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -284,6 +284,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI): aHandle = theProject.newRoot("Archive", nwItemClass.ARCHIVE) assert theProject.projTree[aHandle] is not None xItem.setParent(aHandle) + xItem.setRoot(aHandle) assert theIndex.scanText(xHandle, "Hello World!") is False # Make some usable items diff --git a/tests/test_core/test_core_tree.py b/tests/test_core/test_core_tree.py index 66d76459..a86993a3 100644 --- a/tests/test_core/test_core_tree.py +++ b/tests/test_core/test_core_tree.py @@ -224,17 +224,10 @@ def testCoreTree_Methods(mockGUI, mockItems): assert theTree.checkType("c000000000001", nwItemType.FILE) is True # Root item lookup - theTree._treeRoots.append("stuff") assert theTree.findRoot(nwItemClass.WORLD) is None assert theTree.findRoot(nwItemClass.NOVEL) == "a000000000001" assert theTree.findRoot(nwItemClass.CHARACTER) == "a000000000004" - # Find root item of child item - assert theTree.getRootItem("b000000000001").itemHandle == "a000000000001" - assert theTree.getRootItem("c000000000001").itemHandle == "a000000000001" - assert theTree.getRootItem("c000000000002").itemHandle == "a000000000001" - assert theTree.getRootItem("stuff") is None - # Get item path assert theTree.getItemPath("stuff") == [] assert theTree.getItemPath("c000000000001") == [