From 27ee82aad0abe157a515997ee12104b0676f5e31 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Thu, 17 Sep 2020 14:03:51 +0200 Subject: [PATCH] Improve the way the project tree is managed and elements deleted. --- nw/common.py | 12 ++++++++++++ nw/core/tree.py | 41 ++++++++++++++++++++--------------------- nw/gui/projtree.py | 10 +++++----- tests/test_common.py | 11 ++++++++++- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/nw/common.py b/nw/common.py index 2d82290b..febac7cc 100644 --- a/nw/common.py +++ b/nw/common.py @@ -82,6 +82,18 @@ def checkBool(checkValue, defaultValue, allowNone=False): return defaultValue return defaultValue +def checkHandle(checkValue, defaultValue, allowNone=False): + """Check if a value is a handle. + """ + if allowNone: + if checkValue is None: + return None + if checkValue == "None": + return None + if isHandle(checkValue): + return str(checkValue) + return defaultValue + def isHandle(theString): """Check if a string is a valid novelWriter handle. Note: This is case sensitive. Must be lower case! diff --git a/nw/core/tree.py b/nw/core/tree.py index 0fcf2061..e9e39820 100644 --- a/nw/core/tree.py +++ b/nw/core/tree.py @@ -34,7 +34,7 @@ from hashlib import sha256 from time import time from nw.core.item import NWItem -from nw.common import checkString +from nw.common import checkHandle from nw.constants import nwFiles, nwItemType, nwItemClass, nwItemLayout, nwConst logger = logging.getLogger(__name__) @@ -49,7 +49,6 @@ class NWTree(): self._treeOrder = [] # The order of the tree items on the tree view self._treeRoots = [] # The root items of the tree self._trashRoot = None # The handle of the trash root folder - self._theLength = 0 # Always the length of _treeOrder self._theIndex = 0 # The current iterator index self._treeChanged = False # True if tree structure has changed self._handleSeed = None # Used for generating handles for testing @@ -68,7 +67,6 @@ class NWTree(): self._treeRoots = [] self._trashRoot = None self._archRoot = None - self._theLength = 0 self._theIndex = 0 self._treeChanged = False return @@ -81,8 +79,8 @@ class NWTree(): def append(self, tHandle, pHandle, nwItem): """Add a new item to the end of the tree. """ - tHandle = checkString(tHandle, None, True) - pHandle = checkString(pHandle, None, True) + tHandle = checkHandle(tHandle, None, True) + pHandle = checkHandle(pHandle, None, True) if tHandle is None: tHandle = self._makeHandle() @@ -108,16 +106,16 @@ class NWTree(): else: logger.error("Only one trash folder allowed") - self._theLength = len(self._treeOrder) self._setTreeChanged(True) return def packXML(self, xParent): - """Pack the content of the tree into an XML object. + """Pack the content of the tree into the provided XML object. In + the order defined by the _treeOrder list. """ xContent = etree.SubElement(xParent, "content", attrib={ - "count": str(self._theLength)} + "count": str(len(self._treeOrder))} ) for tHandle in self._treeOrder: tItem = self.__getitem__(tHandle) @@ -309,7 +307,6 @@ class NWTree(): # Save the temp list self._treeOrder = tmpOrder - self._theLength = len(self._treeOrder) self._setTreeChanged(True) logger.verbose("Project tree order updated") @@ -330,7 +327,7 @@ class NWTree(): if tItem is None: return False if tItem.itemType != nwItemType.FILE: - logger.error("Item '%s' is not a file" % tHandle) + logger.error("Item %s is not a file" % tHandle) return False if not isinstance(itemLayout, nwItemLayout): return False @@ -370,12 +367,12 @@ class NWTree(): def __len__(self): """Return the length counter. Does not check that it is correct! """ - return self._theLength + return len(self._treeOrder) def __bool__(self): """Returns True if the tree has any entries. """ - return self._theLength > 0 + return len(self._treeOrder) > 0 ## # Item Access Methods @@ -391,19 +388,21 @@ class NWTree(): return None def __delitem__(self, tHandle): - """This only removes the item from the order list, but not from - the project tree. + """Remove an item from the internal lists and dictionaries. """ - if tHandle not in self._treeOrder: - logger.warning( - "Could not remove item %s from project tree as it does not exist" % tHandle - ) + if tHandle in self._treeOrder and tHandle in self._projTree: + self._treeOrder.remove(tHandle) + del self._projTree[tHandle] + else: + logger.warning("Failed to delete item %s: item not found" % tHandle) return False - self._treeOrder.remove(tHandle) - self._theLength = len(self._treeOrder) if tHandle in self._treeRoots: self._treeRoots.remove(tHandle) + if tHandle == self._trashRoot: + self._trashRoot = None + if tHandle == self._archRoot: + self._archRoot = None self._setTreeChanged(True) @@ -427,7 +426,7 @@ class NWTree(): def __next__(self): """Returns the item from the next entry in the _treeOrder list. """ - if self._theIndex < self._theLength: + if self._theIndex < len(self._treeOrder): theItem = self.__getitem__(self._treeOrder[self._theIndex]) self._theIndex += 1 return theItem diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py index 28d23102..0ca21507 100644 --- a/nw/gui/projtree.py +++ b/nw/gui/projtree.py @@ -378,11 +378,11 @@ class GuiProjectTree(QTreeWidget): return True def deleteItem(self, tHandle=None, alreadyAsked=False, askForTrash=False): - """Delete items from the tree. Note that this does not delete - the item from the item tree in the project object. However, - since this is only meta data, there isn't really a need to do - that to save memory. Items not in the tree are not saved to the - project file, so a loaded project will be clean anyway. + """Delete an item from the project tree. As a first step, files are + moved to the Trash folder. Permanent deletion is a second step. This + second step also deletes the item from the project object as well as + delete the files on disk. Folders are deleted if they're empty only, + and the deletion is always permanent. """ if tHandle is None: tHandle = self.getSelectedHandle() diff --git a/tests/test_common.py b/tests/test_common.py index 94e384db..5a73ad45 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -5,7 +5,7 @@ import pytest from nw.common import ( checkString, checkBool, checkInt, colRange, formatInt, transferCase, - fuzzyTime + fuzzyTime, checkHandle ) from nwtools import cmpList @@ -42,6 +42,15 @@ def testCheckBool(): assert checkBool(1.0, None, False) is None assert checkBool(2.0, None, False) is None +@pytest.mark.core +def testCheckHandle(): + assert checkHandle("None", 1, True) is None + assert checkHandle("None", 1, False) == 1 + assert checkHandle(None, 1, True) is None + assert checkHandle(None, 1, False) == 1 + assert checkHandle("47666c91c7ccf", None, False) == "47666c91c7ccf" + assert checkHandle("h7666c91c7ccf", None, False) is None + @pytest.mark.core def testColRange(): assert colRange([0, 0], [0, 0], 0) is None