Moved the project tree into its own iterable class NWTree
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user