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