Moved the project tree into its own iterable class NWTree

This commit is contained in:
Veronica K. B. Olsen
2020-05-07 23:08:15 +02:00
parent 64ae52e7aa
commit 58eb8e78ba
18 changed files with 342 additions and 176 deletions
+2 -2
View File
@@ -164,12 +164,12 @@ class TextFile():
* Items that appear in the TRASH folder
"""
theItem = self.theProject.getItem(tHandle)
theItem = self.theProject.projTree[tHandle]
isNone = theItem.itemType != nwItemType.FILE
isNone |= theItem.itemLayout == nwItemLayout.NO_LAYOUT
isNone |= theItem.itemClass == nwItemClass.NO_CLASS
isNone |= theItem.itemClass == nwItemClass.TRASH
isNone |= theItem.parHandle == self.theProject.trashRoot
isNone |= theItem.parHandle == self.theProject.projTree.trashRoot()
isNote = theItem.itemLayout == nwItemLayout.NOTE
isNovel = not isNone and not isNote
+1 -1
View File
@@ -142,7 +142,7 @@ class Tokenizer():
def setText(self, theHandle, theText=None):
self.theHandle = theHandle
self.theItem = self.theProject.getItem(theHandle)
self.theItem = self.theProject.projTree[theHandle]
if theText is not None:
# If the text is set, just use that
+3 -3
View File
@@ -115,7 +115,7 @@ class GuiDocMerge(QDialog):
), nwAlert.ERROR)
return
srcItem = self.theProject.getItem(self.sourceItem)
srcItem = self.theProject.projTree[self.sourceItem]
nHandle = self.theProject.newFile(srcItem.itemName, srcItem.itemClass, srcItem.parHandle)
self.theParent.treeView.revealTreeItem(nHandle)
theDoc.openDocument(nHandle, False)
@@ -149,7 +149,7 @@ class GuiDocMerge(QDialog):
if tHandle is None:
return
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
if nwItem is None:
return
if nwItem.itemType is not nwItemType.FOLDER:
@@ -160,7 +160,7 @@ class GuiDocMerge(QDialog):
for sHandle in self.theParent.treeView.getTreeFromHandle(tHandle):
newItem = QListWidgetItem()
nwItem = self.theProject.getItem(sHandle)
nwItem = self.theProject.projTree[sHandle]
if nwItem.itemType is not nwItemType.FILE:
continue
newItem.setText(nwItem.itemName)
+3 -3
View File
@@ -120,7 +120,7 @@ class GuiDocSplit(QDialog):
), nwAlert.ERROR)
return
srcItem = self.theProject.getItem(self.sourceItem)
srcItem = self.theProject.projTree[self.sourceItem]
if srcItem is None:
self.theParent.makeAlert((
"Could not parse source document."
@@ -172,7 +172,7 @@ class GuiDocSplit(QDialog):
wTitle = wTitle.strip()
nHandle = self.theProject.newFile(wTitle, srcItem.itemClass, fHandle)
newItem = self.theProject.getItem(nHandle)
newItem = self.theProject.projTree[nHandle]
newItem.setLayout(itemLayout)
logger.verbose(
"Creating new document %s with text from line %d to %d" % (nHandle, iStart, iEnd-1)
@@ -213,7 +213,7 @@ class GuiDocSplit(QDialog):
if self.sourceItem is None:
return
nwItem = self.theProject.getItem(self.sourceItem)
nwItem = self.theProject.projTree[self.sourceItem]
if nwItem is None:
return
if nwItem.itemType is not nwItemType.FILE:
+3 -5
View File
@@ -134,7 +134,7 @@ class GuiExport(QDialog):
self.exportStatus.setText("Export failed ...")
return False
nItems = len(self.theProject.treeOrder)
nItems = len(self.theProject.projTree)
if eFormat == GuiExportMain.FMT_PDOC:
nItems += int(0.2*nItems)
self.exportProgress.setMinimum(0)
@@ -182,16 +182,14 @@ class GuiExport(QDialog):
time.sleep(0.5)
nDone = 0
for tHandle in self.theProject.treeOrder:
for tItem in self.theProject.projTree:
self.exportProgress.setValue(nDone)
tItem = self.theProject.getItem(tHandle)
self.exportStatus.setText("Exporting: %s" % tItem.itemName)
logger.verbose("Exporting: %s" % tItem.itemName)
if tItem is not None and tItem.itemType == nwItemType.FILE:
outFile.addText(tHandle)
outFile.addText(tItem.itemHandle)
nDone += 1
+1 -1
View File
@@ -48,7 +48,7 @@ class GuiItemEditor(QDialog):
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theParent = theParent
self.theItem = self.theProject.getItem(tHandle)
self.theItem = self.theProject.projTree[tHandle]
self.outerBox = QHBoxLayout()
self.innerBox = QVBoxLayout()
+1 -1
View File
@@ -83,7 +83,7 @@ class GuiDocDetails(QFrame):
def buildViewBox(self, tHandle):
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
if nwItem is None:
colTwo = [""]*4
+2 -2
View File
@@ -89,13 +89,13 @@ class GuiDocTitleBar(QLabel):
tTitle = []
tTree = self.theProject.getItemPath(tHandle)
for aHandle in reversed(tTree):
nwItem = self.theProject.getItem(aHandle)
nwItem = self.theProject.projTree[aHandle]
if nwItem is not None:
tTitle.append(nwItem.itemName)
sSep = " %s " % nwUnicode.U_RSAQUO
self.setText(sSep.join(tTitle))
else:
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
if nwItem is None:
return False
+28 -25
View File
@@ -118,7 +118,7 @@ class GuiDocTree(QTreeWidget):
return False
if itemClass is None and pHandle is not None:
pItem = self.theProject.getItem(pHandle)
pItem = self.theProject.projTree[pHandle]
if pItem is not None:
itemClass = pItem.itemClass
@@ -150,7 +150,7 @@ class GuiDocTree(QTreeWidget):
# If no parent has been selected, make the new file under
# the root NOVEL item.
if pHandle is None:
pHandle = self.theProject.findRootItem(nwItemClass.NOVEL)
pHandle = self.theProject.projTree.findRoot(nwItemClass.NOVEL)
# If still nothing, give up
if pHandle is None:
@@ -159,7 +159,7 @@ class GuiDocTree(QTreeWidget):
# Now check if the selected item is a file, in which case
# the new file will be a sibling
pItem = self.theProject.getItem(pHandle)
pItem = self.theProject.projTree[pHandle]
if pItem.itemType == nwItemType.FILE:
pHandle = pItem.parHandle
@@ -170,7 +170,7 @@ class GuiDocTree(QTreeWidget):
)
return False
if pHandle == self.theProject.trashRoot:
if pHandle == self.theProject.projTree.trashRoot():
self.makeAlert(
"Cannot add new files or folders to the trash folder.", nwAlert.ERROR
)
@@ -194,7 +194,7 @@ class GuiDocTree(QTreeWidget):
def revealTreeItem(self, tHandle):
"""Reveal a newly added project item in the project tree.
"""
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
trItem = self._addTreeItem(nwItem)
pHandle = nwItem.parHandle
if pHandle is not None and pHandle in self.theMap.keys():
@@ -267,14 +267,16 @@ class GuiDocTree(QTreeWidget):
deleteItem function for each document in the Trash folder.
"""
trashHandle = self.theProject.projTree.trashRoot()
logger.debug("Emptying Trash folder")
if self.theProject.trashRoot is None:
if trashHandle is None:
self.makeAlert("There is no Trash folder.", nwAlert.INFO)
return False
theTrash = self.getTreeFromHandle(self.theProject.trashRoot)
if self.theProject.trashRoot in theTrash:
theTrash.remove(self.theProject.trashRoot)
theTrash = self.getTreeFromHandle(trashHandle)
if trashHandle in theTrash:
theTrash.remove(trashHandle)
nTrash = len(theTrash)
if nTrash == 0:
@@ -291,8 +293,8 @@ class GuiDocTree(QTreeWidget):
return False
logger.verbose("Deleting %d files from Trash" % nTrash)
for tHandle in self.getTreeFromHandle(self.theProject.trashRoot):
if tHandle == self.theProject.trashRoot:
for tHandle in self.getTreeFromHandle(trashHandle):
if tHandle == trashHandle:
continue
self.deleteItem(tHandle, True)
@@ -313,7 +315,7 @@ class GuiDocTree(QTreeWidget):
return False
trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.getItem(tHandle)
nwItemS = self.theProject.projTree[tHandle]
if nwItemS is None:
return False
@@ -327,7 +329,7 @@ class GuiDocTree(QTreeWidget):
return False
pHandle = nwItemS.parHandle
if pHandle is not None and pHandle == self.theProject.trashRoot:
if pHandle is not None and pHandle == self.theProject.projTree.trashRoot():
# If the file is in the trash folder already, as the
# user if they want to permanently delete the file.
@@ -353,7 +355,7 @@ class GuiDocTree(QTreeWidget):
theDoc = NWDoc(self.theProject, self.theParent)
theDoc.deleteDocument(tHandle)
self.theProject.deleteItem(tHandle)
del self.theProject.projTree[tHandle]
self.theParent.theIndex.deleteHandle(tHandle)
else:
@@ -366,7 +368,7 @@ class GuiDocTree(QTreeWidget):
tIndex = trItemP.indexOfChild(trItemS)
trItemC = trItemP.takeChild(tIndex)
trItemT.addChild(trItemC)
nwItemS.setParent(self.theProject.trashRoot)
nwItemS.setParent(self.theProject.projTree.trashRoot())
self.theProject.setProjectChanged(True)
self.theParent.theIndex.deleteHandle(tHandle)
@@ -380,7 +382,7 @@ class GuiDocTree(QTreeWidget):
tIndex = trItemP.indexOfChild(trItemS)
if trItemS.childCount() == 0:
trItemP.takeChild(tIndex)
self.theProject.deleteItem(tHandle)
del self.theProject.projTree[tHandle]
else:
self.makeAlert(["Cannot delete folder.","It is not empty."], nwAlert.ERROR)
return False
@@ -401,7 +403,7 @@ class GuiDocTree(QTreeWidget):
def setTreeItemValues(self, tHandle):
trItem = self._getTreeItem(tHandle)
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
tName = nwItem.itemName
tClass = nwItem.itemClass
tHandle = nwItem.itemHandle
@@ -552,14 +554,15 @@ class GuiDocTree(QTreeWidget):
"""Adds the trash root folder if it doesn't already exist in the
project tree.
"""
if self.theProject.trashRoot is None:
trashHandle = self.theProject.projTree.trashRoot()
if trashHandle is None:
self.theProject.addTrash()
trItem = self._addTreeItem(
self.theProject.getItem(self.theProject.trashRoot)
self.theProject.projTree[trashHandle]
)
trItem.setExpanded(True)
else:
trItem = self._getTreeItem(self.theProject.trashRoot)
trItem = self._getTreeItem(trashHandle)
return trItem
def _addOrphanedRoot(self):
@@ -589,7 +592,7 @@ class GuiDocTree(QTreeWidget):
"""
trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.getItem(tHandle)
nwItemS = self.theProject.projTree[tHandle]
trItemP = trItemS.parent()
if trItemP is None:
logger.error("Failed to find new parent item of %s" % tHandle)
@@ -609,8 +612,8 @@ class GuiDocTree(QTreeWidget):
def _moveOrphanedItem(self, tHandle, dHandle):
trItemS = self._getTreeItem(tHandle)
nwItemS = self.theProject.getItem(tHandle)
nwItemD = self.theProject.getItem(dHandle)
nwItemS = self.theProject.projTree[tHandle]
nwItemD = self.theProject.projTree[dHandle]
trItemP = trItemS.parent()
nwItemS.setClass(nwItemD.itemClass)
if trItemP is None:
@@ -652,8 +655,8 @@ class GuiDocTree(QTreeWidget):
dItem = self.itemFromIndex(dIndex)
dHandle = dItem.text(self.C_HANDLE)
snItem = self.theProject.getItem(sHandle)
dnItem = self.theProject.getItem(dHandle)
snItem = self.theProject.projTree[sHandle]
dnItem = self.theProject.projTree[dHandle]
if dnItem is None:
self.makeAlert("The item cannot be moved to that location.", nwAlert.ERROR)
return
+1 -1
View File
@@ -123,7 +123,7 @@ class GuiDocViewer(QTextBrowser):
"""Load text into the viewer from an item handle.
"""
tItem = self.theProject.getItem(tHandle)
tItem = self.theProject.projTree[tHandle]
if tItem is None:
logger.warning("Item not found")
return False
+1 -1
View File
@@ -405,7 +405,7 @@ class GuiProjectOutline(QTreeWidget):
"""Populate a tree item with all the column values.
"""
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
novIdx = self.theIndex.novelIndex[tHandle][sTitle]
newItem = QTreeWidgetItem()
+1 -1
View File
@@ -103,7 +103,7 @@ class GuiDocViewDetails(QWidget):
theRefs = self.theParent.theIndex.getBackReferenceList(tHandle)
theList = []
for tHandle in theRefs:
tItem = self.theProject.getItem(tHandle)
tItem = self.theProject.projTree[tHandle]
if tItem is not None:
theList.append("<a href='#tag=%s'>%s</a>" % (tHandle,tItem.itemName))
+1 -1
View File
@@ -68,7 +68,7 @@ class GuiMainMenu(QMenuBar):
if itemClass == nwItemClass.NO_CLASS: continue
if itemClass == nwItemClass.TRASH: continue
self.rootItems[itemClass].setEnabled(
self.theProject.checkRootUnique(itemClass)
self.theProject.projTree.checkRootUnique(itemClass)
)
return
+1 -1
View File
@@ -228,7 +228,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
return
if theText.startswith("@"): # Keywords and commands
tItem = self.theParent.theProject.getItem(self.theHandle)
tItem = self.theParent.theProject.projTree[self.theHandle]
isValid, theBits, thePos = self.theIndex.scanThis(theText)
isGood = self.theIndex.checkThese(theBits, tItem)
if isValid:
+13 -13
View File
@@ -617,7 +617,7 @@ class GuiMain(QMainWindow):
return False
logger.verbose("Opening item %s" % tHandle)
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
if nwItem.itemType == nwItemType.FILE:
logger.verbose("Requested item %s is a file" % tHandle)
self.openDocument(tHandle)
@@ -656,7 +656,7 @@ class GuiMain(QMainWindow):
self.treeView.saveTreeOrder()
self.theIndex.clearIndex()
nItems = len(self.theProject.treeOrder)
nItems = len(self.theProject.projTree)
dlgProg = QProgressDialog("Scanning files ...", "Cancel", 0, nItems, self)
dlgProg.setWindowModality(Qt.WindowModal)
@@ -668,27 +668,27 @@ class GuiMain(QMainWindow):
time.sleep(0.5)
nDone = 0
for tHandle in self.theProject.treeOrder:
tItem = self.theProject.getItem(tHandle)
for tItem in self.theProject.projTree:
dlgProg.setValue(nDone)
dlgProg.setLabelText("Scanning: %s" % tItem.itemName)
logger.verbose("Scanning: %s" % tItem.itemName)
if tItem is not None and tItem.itemType == nwItemType.FILE:
dlgProg.setLabelText("Scanning: %s" % tItem.itemName)
logger.verbose("Scanning: %s" % tItem.itemName)
theDoc = NWDoc(self.theProject, self)
theText = theDoc.openDocument(tHandle, False)
theText = theDoc.openDocument(tItem.itemHandle, False)
# Build tag index
self.theIndex.scanText(tHandle, theText)
self.theIndex.scanText(tItem.itemHandle, theText)
# Get Word Counts
cC, wC, pC = self.theIndex.getCounts(tHandle)
cC, wC, pC = self.theIndex.getCounts(tItem.itemHandle)
tItem.setCharCount(cC)
tItem.setWordCount(wC)
tItem.setParaCount(pC)
self.treeView.propagateCount(tHandle, wC)
self.treeView.propagateCount(tItem.itemHandle, wC)
self.treeView.projectWordCount()
nDone += 1
@@ -1020,7 +1020,7 @@ class GuiMain(QMainWindow):
def _treeDoubleClick(self, tItem, colNo):
tHandle = tItem.text(3)
logger.verbose("User double clicked tree item with handle %s" % tHandle)
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
if nwItem.itemType == nwItemType.FILE:
logger.verbose("Requested item %s is a file" % tHandle)
self.openDocument(tHandle)
@@ -1031,7 +1031,7 @@ class GuiMain(QMainWindow):
def _treeKeyPressReturn(self):
tHandle = self.treeView.getSelectedHandle()
logger.verbose("User pressed return on tree item with handle %s" % tHandle)
nwItem = self.theProject.getItem(tHandle)
nwItem = self.theProject.projTree[tHandle]
if nwItem.itemType == nwItemType.FILE:
logger.verbose("Requested item %s is a file" % tHandle)
self.openDocument(tHandle)
+2 -2
View File
@@ -62,7 +62,7 @@ class NWDoc():
def openDocument(self, tHandle, showStatus=True):
self.docHandle = tHandle
self.theItem = self.theProject.getItem(tHandle)
self.theItem = self.theProject.projTree[tHandle]
if self.theItem is None:
self.clearDocument()
@@ -71,7 +71,7 @@ class NWDoc():
# By default, the document is editable.
# Except for files in the trash folder.
self.docEditable = True
if self.theItem.parHandle == self.theProject.trashRoot:
if self.theItem.parHandle == self.theProject.projTree.trashRoot():
self.docEditable = False
docDir, docFile = self.assemblePath(self.docHandle, self.FILE_MN)
+4 -4
View File
@@ -249,12 +249,12 @@ class NWIndex():
files before we save them, unless we're rebuilding the index.
"""
theItem = self.theProject.getItem(tHandle)
theItem = self.theProject.projTree[tHandle]
if theItem is None:
return False
if theItem.itemType != nwItemType.FILE:
return False
if theItem.parHandle == self.theProject.trashRoot:
if theItem.parHandle == self.theProject.projTree.trashRoot():
return False
if theItem.itemLayout == nwItemLayout.NO_LAYOUT:
return False
@@ -539,7 +539,7 @@ class NWIndex():
"""
theStructure = []
for tHandle in self.theProject.treeOrder:
for tHandle in self.theProject.projTree.handles():
if tHandle not in self.novelIndex:
continue
for sTitle in sorted(self.novelIndex[tHandle].keys()):
@@ -605,7 +605,7 @@ class NWIndex():
theRefs = {}
tItem = self.theProject.getItem(tHandle)
tItem = self.theProject.projTree[tHandle]
if tHandle is None:
return theRefs
+274 -109
View File
@@ -59,18 +59,12 @@ class NWProject():
self.saveCount = None # Meta data: number of saves
self.autoCount = None # Meta data: number of automatic saves
# Debug
self.handleSeed = None
# Class Settings
self.projTree = None # Holds all the items of the project
self.treeOrder = None # The order of the tree items on the tree view
self.treeRoots = None # The root items of the tree
self.trashRoot = None # The handle of the trash root folder
self.projPath = None # The full path to where the currently open project is saved
self.projMeta = None # The full path to the project's meta data folder
self.projDict = None # The spell check dictionary
self.projFile = None # The file name of the project main xml file
self.projTree = NWTree(self)
self.projPath = None # The full path to where the currently open project is saved
self.projMeta = None # The full path to the project's meta data folder
self.projDict = None # The spell check dictionary
self.projFile = None # The file name of the project main xml file
# Project Meta
self.projName = None
@@ -104,7 +98,10 @@ class NWProject():
##
def newRoot(self, rootName, rootClass):
if not self.checkRootUnique(rootClass):
"""Add a new root item. These items are unique, except for item class
CUSTOM, and always have parent handle set to None.
"""
if not self.projTree.checkRootUnique(rootClass):
self.makeAlert("Duplicate root item detected!", nwAlert.ERROR)
return None
newItem = NWItem(self)
@@ -112,19 +109,24 @@ class NWProject():
newItem.setType(nwItemType.ROOT)
newItem.setClass(rootClass)
newItem.setStatus(0)
self._appendItem(None,None,newItem)
self.projTree.append(None, None, newItem)
return newItem.itemHandle
def newFolder(self, folderName, folderClass, pHandle):
"""Add a new folder with a given name and class and parent item.
"""
newItem = NWItem(self)
newItem.setName(folderName)
newItem.setType(nwItemType.FOLDER)
newItem.setClass(folderClass)
newItem.setStatus(0)
self._appendItem(None,pHandle,newItem)
self.projTree.append(None, pHandle, newItem)
return newItem.itemHandle
def newFile(self, fileName, fileClass, pHandle):
"""Add a new file with a given name and class, and set a default
layout based on the class. SCENE for NOVEL, and otherwise NOTE.
"""
newItem = NWItem(self)
newItem.setName(fileName)
newItem.setType(nwItemType.FILE)
@@ -134,15 +136,17 @@ class NWProject():
newItem.setLayout(nwItemLayout.NOTE)
newItem.setClass(fileClass)
newItem.setStatus(0)
self._appendItem(None,pHandle,newItem)
self.projTree.append(None, pHandle, newItem)
return newItem.itemHandle
def addTrash(self):
"""Add the special trash root folder to the project.
"""
newItem = NWItem(self)
newItem.setName("Trash")
newItem.setType(nwItemType.TRASH)
newItem.setClass(nwItemClass.TRASH)
self._appendItem(None,None,newItem)
self.projTree.append(None, None, newItem)
return newItem.itemHandle
##
@@ -165,17 +169,17 @@ class NWProject():
default values.
"""
# Project Status
self.projOpened = None
self.projChanged = None
self.projAltered = False
self.saveCount = 0
self.autoCount = 0
# Project Tree
self.projTree.clear()
# Project Settings
self.projTree = {}
self.treeOrder = []
self.treeRoots = []
self.trashRoot = None
self.projPath = None
self.projMeta = None
self.projDict = None
@@ -336,7 +340,7 @@ class NWProject():
for xItem in xChild:
nwItem = NWItem(self)
if nwItem.unpackXML(xItem):
self._appendItem(nwItem.itemHandle, nwItem.parHandle, nwItem)
self.projTree.append(nwItem.itemHandle, nwItem.parHandle, nwItem)
self.optState.loadSettings()
@@ -414,9 +418,9 @@ class NWProject():
# Save Tree Content
logger.debug("Writing project content")
xContent = etree.SubElement(nwXML, "content", attrib={"count":str(len(self.treeOrder))})
for tHandle in self.treeOrder:
self.projTree[tHandle].packXML(xContent)
xContent = etree.SubElement(nwXML, "content", attrib={"count":str(len(self.projTree))})
for tItem in self.projTree:
tItem.packXML(xContent)
# Write the xml tree to file
tempFile = path.join(self.projPath, self.projFile+"~")
@@ -529,9 +533,9 @@ class NWProject():
return True
def setTreeOrder(self, newOrder):
if len(self.treeOrder) != len(newOrder):
if len(self.projTree) != len(newOrder):
logger.warning("Size of new and old tree order does not match")
self.treeOrder = newOrder
self.projTree.setOrder(newOrder)
self.setProjectChanged(True)
return True
@@ -555,8 +559,8 @@ class NWProject():
def setStatusColours(self, newCols):
replaceMap = self.statusItems.setNewEntries(newCols)
if self.projTree is not None:
for nwItem in self.projTree.values():
if self.projTree:
for nwItem in self.projTree:
if nwItem.itemClass == nwItemClass.NOVEL:
if nwItem.itemStatus in replaceMap.keys():
nwItem.setStatus(replaceMap[nwItem.itemStatus])
@@ -565,8 +569,8 @@ class NWProject():
def setImportColours(self, newCols):
replaceMap = self.importItems.setNewEntries(newCols)
if self.projTree is not None:
for nwItem in self.projTree.values():
if self.projTree:
for nwItem in self.projTree:
if nwItem.itemClass != nwItemClass.NOVEL:
if nwItem.itemStatus in replaceMap.keys():
nwItem.setStatus(replaceMap[nwItem.itemStatus])
@@ -589,15 +593,6 @@ class NWProject():
# Get Functions
##
def getItem(self, tHandle):
"""Return a project item based on its handle. Returns None if
the handle doesn't exist in the project.
"""
if tHandle in self.projTree:
return self.projTree[tHandle]
logger.error("No tree item with handle %s" % str(tHandle))
return None
def getSessionWordCount(self):
return self.currWCount - self.lastWCount
@@ -606,14 +601,14 @@ class NWProject():
parent None, the root item. We do this with a for loop with a
maximum depth of 200 to make infinite loops impossible.
"""
tItem = self.getItem(tHandle)
tItem = self.projTree[tHandle]
if tItem is not None:
for i in range(200):
if tItem.parHandle is None:
return tHandle
else:
tHandle = tItem.parHandle
tItem = self.getItem(tHandle)
tItem = self.projTree[tHandle]
if tItem is None:
return tHandle
return None
@@ -625,7 +620,7 @@ class NWProject():
infinite loops impossible.
"""
tTree = []
tItem = self.getItem(tHandle)
tItem = self.projTree[tHandle]
if tItem is not None:
tTree.append(tHandle)
for i in range(200):
@@ -633,7 +628,7 @@ class NWProject():
return tTree
else:
tHandle = tItem.parHandle
tItem = self.getItem(tHandle)
tItem = self.projTree[tHandle]
if tItem is None:
return tTree
else:
@@ -647,12 +642,12 @@ class NWProject():
already sent to the tree.
"""
sentItems = []
iterItems = self.treeOrder.copy()
iterItems = self.projTree.handles()
n = 0
nMax = len(iterItems)
while n < nMax:
tHandle = iterItems[n]
tItem = self.getItem(tHandle)
tItem = self.projTree[tHandle]
n += 1
if n > 10000:
return # Just in case
@@ -685,43 +680,13 @@ class NWProject():
# Class Methods
##
def deleteItem(self, tHandle):
"""This only removes the item from the order list, but not from
the project tree.
"""
if tHandle not in self.treeOrder:
logger.warning(
"Could not remove item %s from treeOrder as it does not exist" % tHandle
)
return False
self.treeOrder.remove(tHandle)
self.setProjectChanged(True)
return True
def findRootItem(self, theClass):
for aRoot in self.treeRoots:
if theClass == self.projTree[aRoot].itemClass:
return self.projTree[aRoot].itemHandle
return None
def checkRootUnique(self, theClass):
"""Checks if there already is a root entry of class 'theClass'
in the root of the project tree.
"""
if theClass == nwItemClass.CUSTOM:
return True
for aRoot in self.treeRoots:
if theClass == self.projTree[aRoot].itemClass:
return False
return True
def countStatus(self):
"""Count how many times the various status flags are used in the
project tree.
"""
self.statusItems.resetCounts()
self.importItems.resetCounts()
for nwItem in self.projTree.values():
for nwItem in self.projTree:
if nwItem.itemClass == nwItemClass.NOVEL:
self.statusItems.countEntry(nwItem.itemStatus)
else:
@@ -844,7 +809,7 @@ class NWProject():
logger.warning("Skipping file %s" % fileItem)
continue
fHandle = fileItem[5]+fileItem[7:19]
if fHandle in self.treeOrder:
if fHandle in self.projTree:
logger.debug("Checking file %s, handle %s: OK" % (fileItem,fHandle))
else:
logger.debug("Checking file %s, handle %s: Orphaned" % (fileItem,fHandle))
@@ -869,33 +834,7 @@ class NWProject():
orItem.setType(nwItemType.FILE)
orItem.setClass(nwItemClass.NO_CLASS)
orItem.setLayout(nwItemLayout.NO_LAYOUT)
self._appendItem(oHandle,None,orItem)
return
def _appendItem(self, tHandle, pHandle, nwItem):
tHandle = checkString(tHandle,self._makeHandle(),False)
pHandle = checkString(pHandle,None,True)
logger.verbose("Adding entry %s with parent %s" % (str(tHandle),str(pHandle)))
nwItem.setHandle(tHandle)
nwItem.setParent(pHandle)
self.projTree[tHandle] = nwItem
self.treeOrder.append(tHandle)
if nwItem.itemType == nwItemType.ROOT:
logger.verbose("Entry %s is a root item" % str(tHandle))
self.treeRoots.append(tHandle)
if nwItem.itemType == nwItemType.TRASH:
if self.trashRoot is None:
logger.verbose("Entry %s is the trash folder" % str(tHandle))
self.trashRoot = tHandle
else:
logger.error("Only one trash folder allowed")
self.setProjectChanged(True)
self.projTree.append(oHandle, None, orItem)
return
@@ -919,21 +858,247 @@ class NWProject():
return True
# END Class NWProject
# ================================================================================================ #
# NWTree
# Class holding the project tree for the NWProject
# ================================================================================================ #
class NWTree():
def __init__(self, theProject):
self.theProject = theProject
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._trashRoot = "" # 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
return
##
# Class Methods
##
def clear(self):
"""Clear the item tree entirely.
"""
self._projTree = {}
self._treeOrder = []
self._treeRoots = []
self._trashRoot = ""
self._theLength = 0
self._theIndex = 0
self._treeChanged = False
return
def handles(self):
"""Returns a copy of the list of all the active handles.
"""
return self._treeOrder.copy()
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)
if tHandle is None:
tHandle = self._makeHandle()
logger.verbose("Adding entry %s with parent %s" % (str(tHandle), str(pHandle)))
nwItem.setHandle(tHandle)
nwItem.setParent(pHandle)
self._projTree[tHandle] = nwItem
self._treeOrder.append(tHandle)
if nwItem.itemType == nwItemType.ROOT:
logger.verbose("Entry %s is a root item" % str(tHandle))
self._treeRoots.append(tHandle)
if nwItem.itemType == nwItemType.TRASH:
if self._trashRoot is None:
logger.verbose("Entry %s is the trash folder" % str(tHandle))
self._trashRoot = tHandle
else:
logger.error("Only one trash folder allowed")
self._theLength = len(self._treeOrder)
self._setTreeChanged(True)
return
def trashRoot(self):
"""Returns the handle of the trash folder, or None if there
isn't one.
"""
if self._trashRoot:
return self._trashRoot
return None
def findRoot(self, theClass):
"""Find the root item for a given class.
Note: This returns the first item for class CUSTOM.
"""
for aRoot in self._treeRoots:
tItem = self.__getitem__(aRoot)
if tItem is None:
continue
if theClass == tItem.itemClass:
return tItem.itemHandle
return None
def checkRootUnique(self, theClass):
"""Checks if there already is a root entry of class 'theClass'
in the root of the project tree.
"""
if theClass == nwItemClass.CUSTOM:
return True
for aRoot in self._treeRoots:
tItem = self.__getitem__(aRoot)
if theClass == tItem.itemClass:
return False
return True
##
# Setters
##
def setOrder(self, newOrder):
"""Reorders the tree based on a list of items.
"""
tmpOrder = []
# Add all known elements to a new temp list
for tHandle in newOrder:
if tHandle in self._projTree:
tmpOrder.append(tHandle)
else:
logger.error("Handle %s in new tree order is not in project tree" % tHandle)
# Do a reverse lookup to check for items that will be lost
# This is mainly for debugging purposes
for tHandle in self._treeOrder:
if tHandle not in tmpOrder:
logger.warning("Handle %s in old tree order is not in new tree order" % tHandle)
# Save the temp list
self._treeOrder = tmpOrder
self._theLength = len(self._treeOrder)
self._setTreeChanged(True)
return
def setSeed(self, theSeed):
"""Used for debugging!
Sets a seed for generating handles so that they always come out
in a predictable order.
"""
self._handleSeed = theSeed
return
##
# Meta Methods
##
def __len__(self):
return self._theLength
def __bool__(self):
return self._theLength > 0
##
# Item Access Methods
##
def __getitem__(self, tHandle):
"""Return a project item based on its handle. Returns None if
the handle doesn't exist in the project.
"""
if tHandle in self._projTree:
return self._projTree[tHandle]
logger.error("No tree item with handle %s" % str(tHandle))
return None
def __delitem__(self, tHandle):
"""This only removes the item from the order list, but not from
the project tree.
"""
if tHandle not in self._treeOrder:
logger.warning(
"Could not remove item %s from project tree as it does not exist" % tHandle
)
return False
self._treeOrder.remove(tHandle)
self._theLength = len(self._treeOrder)
self._setTreeChanged(True)
return True
def __contains__(self, tHandle):
"""Checks if a handle exists in the tree.
"""
return tHandle in self._treeOrder
##
# Iterator Methods
##
def __iter__(self):
"""Initiates the iterator,
"""
self._theIndex = 0
return self
def __next__(self):
"""Returns the item from the next entry in the _treeOrder list.
"""
if self._theIndex < self._theLength:
theItem = self.__getitem__(self._treeOrder[self._theIndex])
self._theIndex += 1
return theItem
else:
raise StopIteration
##
# Internal Functions
##
def _setTreeChanged(self, theState):
"""Set the changed flag to theState, and if being set to True,
propagate that change to the parent NWProject class.
"""
self._treeChanged = theState
if theState:
self.theProject.setProjectChanged(True)
return
def _makeHandle(self, addSeed=""):
if self.handleSeed is None:
"""Generate a unique item handle. In the unlikely event that the
key already exists, salt the seed and generate a new handle.
"""
if self._handleSeed is None:
newSeed = str(time()) + addSeed
else:
# This is used for debugging
newSeed = str(self.handleSeed)
self.handleSeed += 1
newSeed = str(self._handleSeed)
self._handleSeed += 1
logger.verbose("Generating handle with seed '%s'" % newSeed)
itemHandle = sha256(newSeed.encode()).hexdigest()[0:13]
if itemHandle in self.projTree.keys():
if itemHandle in self._projTree:
logger.warning("Duplicate handle encountered! Retrying ...")
itemHandle = self._makeHandle(addSeed+"!")
return itemHandle
# END Class NWProject
# END Class NWTree
# ================================================================================================ #
# NWItem