Merge pull request #196 from vkbo/project_structure

Project Class Restructure
Merging into `dev` for further testing and code restructuring before we merge to master.
This commit is contained in:
Veronica K. Berglyd Olsen
2020-05-08 10:22:20 +02:00
committed by GitHub
29 changed files with 1017 additions and 670 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
+3 -3
View File
@@ -87,15 +87,15 @@ class GuiDocTitleBar(QLabel):
if self.mainConf.showFullPath:
tTitle = []
tTree = self.theProject.getItemPath(tHandle)
tTree = self.theProject.projTree.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
+29 -26
View File
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QApplication, QMessageBox
)
from nw.project import NWItem, NWDoc
from nw.project import NWDoc
from nw.constants import (
nwLabels, nwItemType, nwItemClass, nwItemLayout, nwAlert
)
@@ -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:
+14 -14
View File
@@ -45,7 +45,7 @@ from nw.gui import (
GuiConfigEditor, GuiProjectEditor, GuiItemEditor, GuiProjectOutline,
GuiSessionLogView, GuiDocMerge, GuiDocSplit, GuiProjectLoad
)
from nw.project import NWProject, NWDoc, NWItem, NWIndex, NWBackup
from nw.project import NWProject, NWDoc, NWIndex, NWBackup
from nw.tools import countWords
from nw.constants import nwFiles, nwItemType, nwAlert
@@ -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)
-4
View File
@@ -3,15 +3,11 @@
from nw.project.backup import NWBackup
from nw.project.document import NWDoc
from nw.project.index import NWIndex
from nw.project.item import NWItem
from nw.project.project import NWProject
from nw.project.status import NWStatus
__all__ = [
"NWBackup",
"NWDoc",
"NWIndex",
"NWItem",
"NWProject",
"NWStatus",
]
+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
-222
View File
@@ -1,222 +0,0 @@
# -*- coding: utf-8 -*-
"""novelWriter Project Item
novelWriter Project Item
============================
Class holding a project item
File History:
Created: 2018-10-27 [0.0.1]
This file is a part of novelWriter
Copyright 2020, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
import nw
from lxml import etree
from nw.common import checkInt
from nw.constants import nwItemType, nwItemClass, nwItemLayout
logger = logging.getLogger(__name__)
class NWItem():
def __init__(self, theProject):
self.theProject = theProject
self.itemName = ""
self.itemHandle = None
self.parHandle = None
self.itemOrder = None
self.itemType = nwItemType.NO_TYPE
self.itemClass = nwItemClass.NO_CLASS
self.itemLayout = nwItemLayout.NO_LAYOUT
self.itemStatus = None
self.isExpanded = False
# Document Meta Data
self.charCount = 0
self.wordCount = 0
self.paraCount = 0
self.cursorPos = 0
return
##
# XML Pack
##
def packXML(self, xParent):
xPack = etree.SubElement(xParent,"item",attrib={
"handle" : str(self.itemHandle),
"order" : str(self.itemOrder),
"parent" : str(self.parHandle),
})
xSub = self._subPack(xPack,"name", text=str(self.itemName))
xSub = self._subPack(xPack,"type", text=str(self.itemType.name))
xSub = self._subPack(xPack,"class", text=str(self.itemClass.name))
xSub = self._subPack(xPack,"status", text=str(self.itemStatus))
xSub = self._subPack(xPack,"expanded", text=str(self.isExpanded))
if self.itemType == nwItemType.FILE:
xSub = self._subPack(xPack,"layout", text=str(self.itemLayout.name))
xSub = self._subPack(xPack,"charCount", text=str(self.charCount), none=False)
xSub = self._subPack(xPack,"wordCount", text=str(self.wordCount), none=False)
xSub = self._subPack(xPack,"paraCount", text=str(self.paraCount), none=False)
xSub = self._subPack(xPack,"cursorPos", text=str(self.cursorPos), none=False)
return xPack
def _subPack(self, xParent, name, attrib=None, text=None, none=True):
if not none and (text == None or text == "None"):
return None
xSub = etree.SubElement(xParent,name,attrib=attrib)
if text is not None:
xSub.text = text
return xSub
##
# Settings Wrapper
##
def setFromTag(self, tagName, tagValue):
logger.verbose("Setting tag '%s' to value '%s'" % (tagName, str(tagValue)))
if tagName == "name":
self.setName(tagValue)
elif tagName == "order":
self.setOrder(tagValue)
elif tagName == "type":
self.setType(tagValue)
elif tagName == "class":
self.setClass(tagValue)
elif tagName == "layout":
self.setLayout(tagValue)
elif tagName == "status":
self.setStatus(tagValue)
elif tagName == "expanded":
self.setExpanded(tagValue)
elif tagName == "charCount":
self.setCharCount(tagValue)
elif tagName == "wordCount":
self.setWordCount(tagValue)
elif tagName == "paraCount":
self.setParaCount(tagValue)
elif tagName == "cursorPos":
self.setCursorPos(tagValue)
else:
logger.error("Unknown tag '%s'" % tagName)
return
##
# Set Item Values
##
def setName(self, theName):
self.itemName = theName.strip()
return
def setHandle(self, theHandle):
self.itemHandle = theHandle
return
def setParent(self, theParent):
self.parHandle = theParent
return
def setOrder(self, theOrder):
self.itemOrder = theOrder
return
def setType(self, theType):
if isinstance(theType, nwItemType):
self.itemType = theType
return
else:
for itemType in nwItemType:
if theType == itemType.name:
self.itemType = itemType
return
logger.error("Unrecognised item type '%s'" % theType)
self.itemType = nwItemType.NO_TYPE
return
def setClass(self, theClass):
if isinstance(theClass, nwItemClass):
self.itemClass = theClass
return
else:
for itemClass in nwItemClass:
if theClass == itemClass.name:
self.itemClass = itemClass
return
logger.error("Unrecognised item class '%s'" % theClass)
self.itemClass = nwItemClass.NO_CLASS
return
def setLayout(self, theLayout):
if isinstance(theLayout, nwItemLayout):
self.itemLayout = theLayout
return
else:
for itemLayout in nwItemLayout:
if theLayout == itemLayout.name:
self.itemLayout = itemLayout
return
logger.error("Unrecognised item layout '%s'" % theLayout)
self.itemLayout = nwItemLayout.NO_LAYOUT
return
def setStatus(self, theStatus):
if self.itemClass == nwItemClass.NOVEL:
self.itemStatus = self.theProject.statusItems.checkEntry(theStatus)
else:
self.itemStatus = self.theProject.importItems.checkEntry(theStatus)
return
def setExpanded(self, expState):
if isinstance(expState, str):
self.isExpanded = expState == str(True)
else:
self.isExpanded = expState
return
##
# Set Document Meta Data
##
def setCharCount(self, theCount):
theCount = checkInt(theCount,0)
self.charCount = theCount
return
def setWordCount(self, theCount):
theCount = checkInt(theCount,0)
self.wordCount = theCount
return
def setParaCount(self, theCount):
theCount = checkInt(theCount,0)
self.paraCount = theCount
return
def setCursorPos(self, thePosition):
thePosition = checkInt(thePosition,0)
self.cursorPos = thePosition
return
# END Class NWItem
+699 -174
View File
File diff suppressed because it is too large Load Diff
-170
View File
@@ -1,170 +0,0 @@
# -*- coding: utf-8 -*-
"""novelWriter Item Status
novelWriter Item Status
===========================
Class holding the project's item statuses
File History:
Created: 2019-05-19 [0.1.3]
This file is a part of novelWriter
Copyright 2020, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
import nw
from lxml import etree
from nw.common import checkInt
logger = logging.getLogger(__name__)
class NWStatus():
def __init__(self):
self.theLabels = []
self.theColours = []
self.theCounts = []
self.theMap = {}
self.theLength = 0
self.theIndex = 0
return
def addEntry(self, theLabel, theColours):
theLabel = theLabel.strip()
if self.lookupEntry(theLabel) is None:
self.theLabels.append(theLabel)
self.theColours.append(theColours)
self.theCounts.append(0)
self.theMap[theLabel] = self.theLength
self.theLength += 1
return True
def lookupEntry(self, theLabel):
if theLabel is None:
return None
theLabel = theLabel.strip()
if theLabel in self.theMap.keys():
return self.theMap[theLabel]
return None
def checkEntry(self, theStatus):
if isinstance(theStatus, str):
theStatus = theStatus.strip()
if self.lookupEntry(theStatus) is not None:
return theStatus
theStatus = checkInt(theStatus, 0, False)
if theStatus >= 0 and theStatus < self.theLength:
return self.theLabels[theStatus]
def setNewEntries(self, newList):
replaceMap = {}
if newList is not None:
self.theLabels = []
self.theColours = []
self.theCounts = []
self.theMap = {}
self.theLength = 0
self.theIndex = 0
for nName, nR, nG, nB, oName in newList:
self.addEntry(nName, (nR, nG, nB))
if nName != oName and oName is not None:
replaceMap[oName] = nName
return replaceMap
def resetCounts(self):
self.theCounts = [0]*self.theLength
return
def countEntry(self, theLabel):
theIndex = self.lookupEntry(theLabel)
if theIndex is not None:
self.theCounts[theIndex] += 1
return
def packEntries(self, xParent):
for n in range(self.theLength):
xSub = etree.SubElement(xParent,"entry",attrib={
"blue" : str(self.theColours[n][2]),
"green" : str(self.theColours[n][1]),
"red" : str(self.theColours[n][0]),
})
xSub.text = self.theLabels[n]
return True
def unpackEntries(self, xParent):
theLabels = []
theColours = []
for xChild in xParent:
theLabels.append(xChild.text)
if "red" in xChild.attrib:
cR = checkInt(xChild.attrib["red"],0,False)
else:
cR = 0
if "green" in xChild.attrib:
cG = checkInt(xChild.attrib["green"],0,False)
else:
cG = 0
if "blue" in xChild.attrib:
cB = checkInt(xChild.attrib["blue"],0,False)
else:
cB = 0
theColours.append((cR,cG,cB))
if len(theLabels) > 0:
self.theLabels = []
self.theColours = []
self.theCounts = []
self.theMap = {}
self.theLength = 0
self.theIndex = 0
for n in range(len(theLabels)):
self.addEntry(theLabels[n], theColours[n])
return True
##
# Iterator Bits
##
def __getitem__(self, n):
if n >= 0 and n < self.theLength:
return self.theLabels[n], self.theColours[n], self.theCounts[n]
return None, None, None
def __iter__(self):
self.theIndex = 0
return self
def __next__(self):
if self.theIndex < self.theLength:
theLabel, theColour, theCount = self.__getitem__(self.theIndex)
self.theIndex += 1
return theLabel, theColour, theCount
else:
raise StopIteration
# END Class NWStatus
+4 -4
View File
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="0.1.5" fileVersion="1.0" timeStamp="2019-06-27 20:31:37">
<novelWriterXML appVersion="0.4.5" fileVersion="1.0" saveCount="4" autoCount="0" timeStamp="2020-05-07 22:03:17">
<project>
<name></name>
<title></title>
@@ -59,7 +59,7 @@
<status>New</status>
<expanded>True</expanded>
</item>
<item handle="2fca346db6561" order="0" parent="44cb730c42048">
<item handle="98010bd9270f9" order="0" parent="44cb730c42048">
<name>New File</name>
<type>FILE</type>
<class>CHARACTER</class>
@@ -78,7 +78,7 @@
<status>New</status>
<expanded>True</expanded>
</item>
<item handle="02d20bbd7e394" order="0" parent="71ee45a3c0db9">
<item handle="0e17daca5f3e1" order="0" parent="71ee45a3c0db9">
<name>New File</name>
<type>FILE</type>
<class>PLOT</class>
@@ -97,7 +97,7 @@
<status>New</status>
<expanded>True</expanded>
</item>
<item handle="7688b6ef52555" order="0" parent="811786ad1ae74">
<item handle="1a6562590ef19" order="0" parent="811786ad1ae74">
<name>New File</name>
<type>FILE</type>
<class>WORLD</class>
+5 -5
View File
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="0.4.5" fileVersion="1.0" timeStamp="2020-02-26 22:05:41">
<novelWriterXML appVersion="0.4.5" fileVersion="1.0" saveCount="4" autoCount="0" timeStamp="2020-05-07 22:52:18">
<project>
<name></name>
<title></title>
@@ -73,28 +73,28 @@
<paraCount>0</paraCount>
<cursorPos>0</cursorPos>
</item>
<item handle="8722616204217" order="None" parent="None">
<item handle="98010bd9270f9" order="None" parent="None">
<name>Timeline</name>
<type>ROOT</type>
<class>TIMELINE</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="96061e92f58e4" order="None" parent="None">
<item handle="0e17daca5f3e1" order="None" parent="None">
<name>Object</name>
<type>ROOT</type>
<class>OBJECT</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="eb624dbe56eb6" order="None" parent="None">
<item handle="1a6562590ef19" order="None" parent="None">
<name>Custom1</name>
<type>ROOT</type>
<class>CUSTOM</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="f369cb89fc627" order="None" parent="None">
<item handle="031b4af5197ec" order="None" parent="None">
<name>Custom2</name>
<type>ROOT</type>
<class>CUSTOM</class>
+15 -15
View File
@@ -25,15 +25,15 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
qtbot.wait(stepDelay)
# Create new, save, close project
nwGUI.theProject.handleSeed = 42
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject(nwTempGUI, True)
assert nwGUI.saveProject()
assert nwGUI.closeProject()
assert len(nwGUI.theProject.projTree) == 0
assert len(nwGUI.theProject.treeOrder) == 0
assert len(nwGUI.theProject.treeRoots) == 0
assert nwGUI.theProject.trashRoot is None
assert len(nwGUI.theProject.projTree._treeOrder) == 0
assert len(nwGUI.theProject.projTree._treeRoots) == 0
assert nwGUI.theProject.projTree.trashRoot() is None
assert nwGUI.theProject.projPath is None
assert nwGUI.theProject.projMeta is None
assert nwGUI.theProject.projFile == "nwProject.nwx"
@@ -55,9 +55,9 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
# Check that we loaded the data
assert len(nwGUI.theProject.projTree) == 6
assert len(nwGUI.theProject.treeOrder) == 6
assert len(nwGUI.theProject.treeRoots) == 4
assert nwGUI.theProject.trashRoot is None
assert len(nwGUI.theProject.projTree._treeOrder) == 6
assert len(nwGUI.theProject.projTree._treeRoots) == 4
assert nwGUI.theProject.projTree.trashRoot() is None
assert nwGUI.theProject.projPath == nwTempGUI
assert nwGUI.theProject.projMeta == path.join(nwTempGUI,"meta")
assert nwGUI.theProject.projFile == "nwProject.nwx"
@@ -237,14 +237,14 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
# Check the files
refFile = path.join(nwTempGUI,"nwProject.nwx")
assert cmpFiles(refFile, path.join(nwRef,"gui","1_nwProject.nwx"), [2])
refFile = path.join(nwTempGUI,"data_0","2d20bbd7e394_main.nwd")
assert cmpFiles(refFile, path.join(nwRef,"gui","1_2d20bbd7e394_main.nwd"))
refFile = path.join(nwTempGUI,"data_2","fca346db6561_main.nwd")
assert cmpFiles(refFile, path.join(nwRef,"gui","1_fca346db6561_main.nwd"))
refFile = path.join(nwTempGUI,"data_0","e17daca5f3e1_main.nwd")
assert cmpFiles(refFile, path.join(nwRef,"gui","1_e17daca5f3e1_main.nwd"))
refFile = path.join(nwTempGUI,"data_9","8010bd9270f9_main.nwd")
assert cmpFiles(refFile, path.join(nwRef,"gui","1_8010bd9270f9_main.nwd"))
refFile = path.join(nwTempGUI,"data_3","1489056e0916_main.nwd")
assert cmpFiles(refFile, path.join(nwRef,"gui","1_1489056e0916_main.nwd"))
refFile = path.join(nwTempGUI,"data_7","688b6ef52555_main.nwd")
assert cmpFiles(refFile, path.join(nwRef,"gui","1_688b6ef52555_main.nwd"))
refFile = path.join(nwTempGUI,"data_1","a6562590ef19_main.nwd")
assert cmpFiles(refFile, path.join(nwRef,"gui","1_a6562590ef19_main.nwd"))
nwGUI.closeMain()
# qtbot.stopForInteraction()
@@ -258,7 +258,7 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
qtbot.wait(stepDelay)
# Create new, save, open project
nwGUI.theProject.handleSeed = 42
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject(nwTempGUI, True)
nwGUI.mainConf.backupPath = nwTempGUI
@@ -345,7 +345,7 @@ def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
qtbot.wait(stepDelay)
# Create new, save, open project
nwGUI.theProject.handleSeed = 42
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject(nwTempGUI, True)
itemEdit = GuiItemEditor(nwGUI, nwGUI.theProject, "31489056e0916")
+217
View File
@@ -0,0 +1,217 @@
# -*- coding: utf-8 -*-
"""novelWriter NWItem Class Tester
"""
import nw
import pytest
from lxml import etree
from nwdummy import DummyMain
from nw.config import Config
from nw.project.project import NWProject, NWItem
from nw.constants import nwItemClass, nwItemType, nwItemLayout
theConf = Config()
theMain = DummyMain()
theMain.mainConf = theConf
theProject = NWProject(theMain)
theItem = NWItem(theProject)
nwXML = etree.Element("novelWriterXML")
@pytest.mark.project
def testItemSettersSimple():
# Name
theItem.setName("A Name")
assert theItem.itemName == "A Name"
theItem.setName("\t A Name ")
assert theItem.itemName == "A Name"
# Handle
theItem.setHandle(123)
assert theItem.itemHandle is None
theItem.setHandle("0123456789abcdef")
assert theItem.itemHandle is None
theItem.setHandle("0123456789abc")
assert theItem.itemHandle == "0123456789abc"
# Parent
theItem.setParent(None)
assert theItem.parHandle is None
theItem.setParent(123)
assert theItem.parHandle is None
theItem.setParent("0123456789abcdef")
assert theItem.parHandle is None
theItem.setParent("0123456789abc")
assert theItem.parHandle == "0123456789abc"
# Order
theItem.setOrder(None)
assert theItem.itemOrder == 0
theItem.setOrder("1")
assert theItem.itemOrder == 1
theItem.setOrder(1)
assert theItem.itemOrder == 1
# Status
theItem.setStatus("Nonsense")
assert theItem.itemStatus == "New"
theItem.setStatus("New")
assert theItem.itemStatus == "New"
theItem.setStatus("Minor")
assert theItem.itemStatus == "Minor"
theItem.setStatus("Major")
assert theItem.itemStatus == "Major"
theItem.setStatus("Main")
assert theItem.itemStatus == "Main"
# Expanded
theItem.setExpanded(8)
assert not theItem.isExpanded
theItem.setExpanded(None)
assert not theItem.isExpanded
theItem.setExpanded("None")
assert not theItem.isExpanded
theItem.setExpanded("What?")
assert not theItem.isExpanded
theItem.setExpanded("True")
assert theItem.isExpanded
theItem.setExpanded(True)
assert theItem.isExpanded
# CharCount
theItem.setCharCount(None)
assert theItem.charCount == 0
theItem.setCharCount("1")
assert theItem.charCount == 1
theItem.setCharCount(1)
assert theItem.charCount == 1
# WordCount
theItem.setWordCount(None)
assert theItem.wordCount == 0
theItem.setWordCount("1")
assert theItem.wordCount == 1
theItem.setWordCount(1)
assert theItem.wordCount == 1
# ParaCount
theItem.setParaCount(None)
assert theItem.paraCount == 0
theItem.setParaCount("1")
assert theItem.paraCount == 1
theItem.setParaCount(1)
assert theItem.paraCount == 1
# CursorPos
theItem.setCursorPos(None)
assert theItem.cursorPos == 0
theItem.setCursorPos("1")
assert theItem.cursorPos == 1
theItem.setCursorPos(1)
assert theItem.cursorPos == 1
@pytest.mark.project
def testItemClassSetter():
# Class
theItem.setClass(None)
assert theItem.itemClass == nwItemClass.NO_CLASS
theItem.setClass("NONSENSE")
assert theItem.itemClass == nwItemClass.NO_CLASS
theItem.setClass("NO_CLASS")
assert theItem.itemClass == nwItemClass.NO_CLASS
theItem.setClass("NOVEL")
assert theItem.itemClass == nwItemClass.NOVEL
theItem.setClass("PLOT")
assert theItem.itemClass == nwItemClass.PLOT
theItem.setClass("CHARACTER")
assert theItem.itemClass == nwItemClass.CHARACTER
theItem.setClass("WORLD")
assert theItem.itemClass == nwItemClass.WORLD
theItem.setClass("TIMELINE")
assert theItem.itemClass == nwItemClass.TIMELINE
theItem.setClass("OBJECT")
assert theItem.itemClass == nwItemClass.OBJECT
theItem.setClass("ENTITY")
assert theItem.itemClass == nwItemClass.ENTITY
theItem.setClass("CUSTOM")
assert theItem.itemClass == nwItemClass.CUSTOM
theItem.setClass("TRASH")
assert theItem.itemClass == nwItemClass.TRASH
@pytest.mark.project
def testItemTypeSetter():
# Class
theItem.setType(None)
assert theItem.itemType == nwItemType.NO_TYPE
theItem.setType("NONSENSE")
assert theItem.itemType == nwItemType.NO_TYPE
theItem.setType("NO_TYPE")
assert theItem.itemType == nwItemType.NO_TYPE
theItem.setType("ROOT")
assert theItem.itemType == nwItemType.ROOT
theItem.setType("FOLDER")
assert theItem.itemType == nwItemType.FOLDER
theItem.setType("FILE")
assert theItem.itemType == nwItemType.FILE
theItem.setType("TRASH")
assert theItem.itemType == nwItemType.TRASH
@pytest.mark.project
def testItemLayoutSetter():
# Class
theItem.setLayout(None)
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
theItem.setLayout("NONSENSE")
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
theItem.setLayout("NO_LAYOUT")
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
theItem.setLayout("TITLE")
assert theItem.itemLayout == nwItemLayout.TITLE
theItem.setLayout("BOOK")
assert theItem.itemLayout == nwItemLayout.BOOK
theItem.setLayout("PAGE")
assert theItem.itemLayout == nwItemLayout.PAGE
theItem.setLayout("PARTITION")
assert theItem.itemLayout == nwItemLayout.PARTITION
theItem.setLayout("UNNUMBERED")
assert theItem.itemLayout == nwItemLayout.UNNUMBERED
theItem.setLayout("CHAPTER")
assert theItem.itemLayout == nwItemLayout.CHAPTER
theItem.setLayout("SCENE")
assert theItem.itemLayout == nwItemLayout.SCENE
theItem.setLayout("NOTE")
assert theItem.itemLayout == nwItemLayout.NOTE
@pytest.mark.project
def testItemXMLPackUnpack():
# Pack
xContent = etree.SubElement(nwXML, "content")
theItem.packXML(xContent)
assert etree.tostring(xContent, pretty_print=False, encoding="utf-8") == (
b"<content>"
b"<item handle=\"0123456789abc\" order=\"1\" parent=\"0123456789abc\">"
b"<name>A Name</name><type>TRASH</type><class>TRASH</class><status>Main</status><expanded>True</expanded>"
b"</item>"
b"</content>"
)
# Unpack
assert theItem.unpackXML(xContent[0])
assert theItem.itemHandle == "0123456789abc"
assert theItem.parHandle == "0123456789abc"
assert theItem.itemOrder == 1
assert theItem.isExpanded
assert theItem.charCount == 1
assert theItem.wordCount == 1
assert theItem.paraCount == 1
assert theItem.cursorPos == 1
assert theItem.itemClass == nwItemClass.TRASH
assert theItem.itemType == nwItemType.TRASH
assert theItem.itemLayout == nwItemLayout.NOTE
+6 -6
View File
@@ -2,24 +2,24 @@
"""novelWriter Project Class Tester
"""
import nw, pytest, types
import nw
import pytest
from os import path
from nwtools import *
from nwdummy import DummyMain
from nw.config import Config
from nw.config import Config
from nw.project.project import NWProject
from nw.project.item import NWItem
from nw.project.index import NWIndex
from nw.constants import nwItemClass
from nw.project.index import NWIndex
from nw.constants import nwItemClass
theConf = Config()
theMain = DummyMain()
theMain.mainConf = theConf
theProject = NWProject(theMain)
theProject.handleSeed = 42
theProject.projTree.setSeed(42)
@pytest.mark.project
def testProjectNew(nwTempProj,nwRef,nwTemp):