Update new project creation (#1067)

This commit is contained in:
Veronica Berglyd Olsen
2022-05-21 22:22:07 +02:00
committed by GitHub
26 changed files with 504 additions and 366 deletions
+58 -52
View File
@@ -120,32 +120,34 @@ class NWProject():
# Item Methods
##
def newRoot(self, rootName, rootClass):
"""Add a new root item.
def newRoot(self, itemClass, label=None):
"""Add a new root item. If label is None, use the class label.
"""
if label is None:
label = trConst(nwLabels.CLASS_NAME[itemClass])
newItem = NWItem(self)
newItem.setName(rootName)
newItem.setName(label)
newItem.setType(nwItemType.ROOT)
newItem.setClass(rootClass)
newItem.setClass(itemClass)
self.projTree.append(None, None, newItem)
self.projTree.updateItemData(newItem.itemHandle)
return newItem.itemHandle
def newFolder(self, folderName, pHandle):
"""Add a new folder with a given name and parent item.
def newFolder(self, label, pHandle):
"""Add a new folder with a given label and parent item.
"""
newItem = NWItem(self)
newItem.setName(folderName)
newItem.setName(label)
newItem.setType(nwItemType.FOLDER)
self.projTree.append(None, pHandle, newItem)
self.projTree.updateItemData(newItem.itemHandle)
return newItem.itemHandle
def newFile(self, fileName, pHandle):
"""Add a new file with a given name and parent item.
def newFile(self, label, pHandle):
"""Add a new file with a given label and parent item.
"""
newItem = NWItem(self)
newItem.setName(fileName)
newItem.setName(label)
newItem.setType(nwItemType.FILE)
self.projTree.append(None, pHandle, newItem)
self.projTree.updateItemData(newItem.itemHandle)
@@ -264,84 +266,88 @@ class NWProject():
self.setBookTitle(projTitle)
self.setBookAuthors(projAuthors)
hNovelRoot = self.newRoot(nwItemClass.NOVEL)
hTitlePage = self.newFile(self.tr("Title Page"), hNovelRoot)
titlePage = "#! %s\n\n" % (self.bookTitle if self.bookTitle else self.projName)
if self.bookAuthors:
titlePage = "%s>> %s %s <<\n" % (titlePage, self.tr("By"), self.getAuthors())
aDoc = NWDoc(self, hTitlePage)
aDoc.writeDocument(titlePage)
if popMinimal:
# Creating a minimal project with a few root folders and a
# single chapter folder with a single file.
xHandle = {}
xHandle[1] = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
xHandle[2] = self.newRoot(self.tr("Plot"), nwItemClass.PLOT)
xHandle[3] = self.newRoot(self.tr("Characters"), nwItemClass.CHARACTER)
xHandle[4] = self.newRoot(self.tr("World"), nwItemClass.WORLD)
xHandle[5] = self.newFile(self.tr("Title Page"), xHandle[1])
xHandle[6] = self.newFolder(self.tr("New Chapter"), xHandle[1])
xHandle[7] = self.newFile(self.tr("New Chapter"), xHandle[6])
xHandle[8] = self.newFile(self.tr("New Scene"), xHandle[6])
aDoc = NWDoc(self, xHandle[5])
aDoc.writeDocument(titlePage)
aDoc = NWDoc(self, xHandle[7])
# single chapter with a single scene.
hChapter = self.newFile(self.tr("New Chapter"), hNovelRoot)
aDoc = NWDoc(self, hChapter)
aDoc.writeDocument("## %s\n\n" % self.tr("New Chapter"))
aDoc = NWDoc(self, xHandle[8])
hScene = self.newFile(self.tr("New Scene"), hChapter)
aDoc = NWDoc(self, hScene)
aDoc.writeDocument("### %s\n\n" % self.tr("New Scene"))
self.newRoot(nwItemClass.PLOT)
self.newRoot(nwItemClass.CHARACTER)
self.newRoot(nwItemClass.WORLD)
self.newRoot(nwItemClass.ARCHIVE)
elif popCustom:
# Create a project structure based on selected root folders
# and a number of chapters and scenes selected in the
# wizard's custom page.
# Create root folders
nHandle = self.newRoot(self.tr("Novel"), nwItemClass.NOVEL)
for newRoot in projData.get("addRoots", []):
if newRoot in nwItemClass:
self.newRoot(trConst(nwLabels.CLASS_NAME[newRoot]), newRoot)
# Create a title page
tHandle = self.newFile(self.tr("Title Page"), nHandle)
aDoc = NWDoc(self, tHandle)
aDoc.writeDocument(titlePage)
# Create chapters and scenes
numChapters = projData.get("numChapters", 0)
numScenes = projData.get("numScenes", 0)
chFolders = projData.get("chFolders", False)
chSynop = self.tr("Summary of the chapter.")
scSynop = self.tr("Summary of the scene.")
# Create chapters
if numChapters > 0:
for ch in range(numChapters):
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
pHandle = nHandle
if chFolders:
pHandle = self.newFolder(chTitle, nHandle)
cHandle = self.newFile(chTitle, pHandle)
cHandle = self.newFile(chTitle, hNovelRoot)
aDoc = NWDoc(self, cHandle)
aDoc.writeDocument("## %s\n\n" % chTitle)
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
# Create chapter scenes
if numScenes > 0:
for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
sHandle = self.newFile(scTitle, pHandle)
sHandle = self.newFile(scTitle, cHandle)
aDoc = NWDoc(self, sHandle)
aDoc.writeDocument("### %s\n\n" % scTitle)
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
# Create scenes (no chapters)
elif numScenes > 0:
for sc in range(numScenes):
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
sHandle = self.newFile(scTitle, nHandle)
sHandle = self.newFile(scTitle, hNovelRoot)
aDoc = NWDoc(self, sHandle)
aDoc.writeDocument("### %s\n\n" % scTitle)
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
# Create notes folders
noteTitles = {
nwItemClass.PLOT: self.tr("Main Plot"),
nwItemClass.CHARACTER: self.tr("Protagonist"),
nwItemClass.WORLD: self.tr("Main Location"),
}
addNotes = projData.get("addNotes", False)
for newRoot in projData.get("addRoots", []):
if newRoot in nwItemClass:
rHandle = self.newRoot(newRoot)
if addNotes:
aHandle = self.newFile(noteTitles[newRoot], rHandle)
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
aDoc = NWDoc(self, aHandle)
aDoc.writeDocument(f"# {noteTitles[newRoot]}\n\n@tag: {ntTag}\n\n")
# Also add the archive and trash folders
self.newRoot(nwItemClass.ARCHIVE)
self.trashFolder()
# Finalise
if popCustom or popMinimal:
+1 -4
View File
@@ -37,7 +37,6 @@ from PyQt5.QtWidgets import (
from novelwriter.core import NWDoc
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout, nwAlert
from novelwriter.constants import trConst, nwLabels
logger = logging.getLogger(__name__)
@@ -176,9 +175,7 @@ class GuiProjectTree(QTreeWidget):
if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
tHandle = self.theProject.newRoot(
trConst(nwLabels.CLASS_NAME[itemClass]), itemClass
)
tHandle = self.theProject.newRoot(itemClass)
elif itemType in (nwItemType.FILE, nwItemType.FOLDER):
+2 -8
View File
@@ -1440,9 +1440,9 @@ class GuiMain(QMainWindow):
"popMinimal": newProj.field("popMinimal"),
"popCustom": newProj.field("popCustom"),
"addRoots": [],
"addNotes": False,
"numChapters": 0,
"numScenes": 0,
"chFolders": False,
}
if newProj.field("popCustom"):
addRoots = []
@@ -1452,16 +1452,10 @@ class GuiMain(QMainWindow):
addRoots.append(nwItemClass.CHARACTER)
if newProj.field("addWorld"):
addRoots.append(nwItemClass.WORLD)
if newProj.field("addTime"):
addRoots.append(nwItemClass.TIMELINE)
if newProj.field("addObject"):
addRoots.append(nwItemClass.OBJECT)
if newProj.field("addEntity"):
addRoots.append(nwItemClass.ENTITY)
projData["addRoots"] = addRoots
projData["addNotes"] = newProj.field("addNotes")
projData["numChapters"] = newProj.field("numChapters")
projData["numScenes"] = newProj.field("numScenes")
projData["chFolders"] = newProj.field("chFolders")
return projData
+91 -90
View File
@@ -31,12 +31,10 @@ from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QWizard, QWizardPage, QLabel, QVBoxLayout, QLineEdit, QPlainTextEdit,
QPushButton, QFileDialog, QHBoxLayout, QRadioButton, QFormLayout,
QGroupBox, QGridLayout, QSpinBox
QGridLayout, QSpinBox
)
from novelwriter.enum import nwItemClass
from novelwriter.common import makeFileNameSafe
from novelwriter.constants import trConst, nwLabels
from novelwriter.gui.custom import QSwitch
logger = logging.getLogger(__name__)
@@ -98,10 +96,10 @@ class ProjWizardIntroPage(QWizardPage):
self.setTitle(self.tr("Create New Project"))
self.theText = QLabel(self.tr(
"Provide at least a working title. The working title should not "
"be change beyond this point as it is used by the application for "
"generating file names for for instance backups. The other fields "
"are optional and can be changed at any time in Project Settings."
"Provide at least a project name. The project name should not "
"be changed beyond this point as it is used for generating file "
"names for for instance backups. The other fields are optional "
"and can be changed at any time in Project Settings."
))
self.theText.setWordWrap(True)
@@ -134,7 +132,7 @@ class ProjWizardIntroPage(QWizardPage):
self.projAuthors.setPlaceholderText(self.tr("Optional. One name per line."))
self.mainForm = QFormLayout()
self.mainForm.addRow(self.tr("Working Title"), self.projName)
self.mainForm.addRow(self.tr("Project Name"), self.projName)
self.mainForm.addRow(self.tr("Novel Title"), self.projTitle)
self.mainForm.addRow(self.tr("Author(s)"), self.projAuthors)
self.mainForm.setVerticalSpacing(fS)
@@ -324,68 +322,28 @@ class ProjWizardCustomPage(QWizardPage):
self.setTitle(self.tr("Custom Project Options"))
self.theText = QLabel(self.tr(
"Select which additional root folders to make, and how to populate "
"the Novel folder. If you don't want to add chapters or scenes, set "
"the values to 0. You can add scenes without chapters."
"Select which additional elements to populate the project with. "
"You can skip making chapters and add only scenes by setting the "
"number of chapters to 0."
))
self.theText.setWordWrap(True)
vS = self.mainConf.pxInt(12)
cM = self.mainConf.pxInt(12)
mH = self.mainConf.pxInt(26)
fS = self.mainConf.pxInt(4)
# Root Folders
self.rootGroup = QGroupBox(self.tr("Additional Root Folders"))
self.rootForm = QGridLayout()
self.rootGroup.setLayout(self.rootForm)
self.lblPlot = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.PLOT]))
)
self.lblChar = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.CHARACTER]))
)
self.lblWorld = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.WORLD]))
)
self.lblTime = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.TIMELINE]))
)
self.lblObject = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.OBJECT]))
)
self.lblEntity = QLabel(self.tr("{0} folder").format(
trConst(nwLabels.CLASS_NAME[nwItemClass.ENTITY]))
)
self.addPlot = QSwitch()
self.addChar = QSwitch()
self.addWorld = QSwitch()
self.addTime = QSwitch()
self.addObject = QSwitch()
self.addEntity = QSwitch()
self.addPlot = QSwitch()
self.addChar = QSwitch()
self.addWorld = QSwitch()
self.addNotes = QSwitch()
self.addPlot.setChecked(True)
self.addChar.setChecked(True)
self.addWorld.setChecked(True)
self.rootForm.addWidget(self.lblPlot, 0, 0)
self.rootForm.addWidget(self.lblChar, 1, 0)
self.rootForm.addWidget(self.lblWorld, 2, 0)
self.rootForm.addWidget(self.lblTime, 3, 0)
self.rootForm.addWidget(self.lblObject, 4, 0)
self.rootForm.addWidget(self.lblEntity, 5, 0)
self.rootForm.addWidget(self.addPlot, 0, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addChar, 1, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addWorld, 2, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addTime, 3, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addObject, 4, 1, 1, 1, Qt.AlignRight)
self.rootForm.addWidget(self.addEntity, 5, 1, 1, 1, Qt.AlignRight)
self.rootForm.setRowStretch(6, 1)
# Novel Options
self.novelGroup = QGroupBox(self.tr("Populate Novel Folder"))
self.novelForm = QGridLayout()
self.novelGroup.setLayout(self.novelForm)
self.addWorld.setChecked(False)
self.addNotes.setChecked(False)
# Generate Content
self.numChapters = QSpinBox()
self.numChapters.setRange(0, 100)
self.numChapters.setValue(5)
@@ -394,37 +352,40 @@ class ProjWizardCustomPage(QWizardPage):
self.numScenes.setRange(0, 200)
self.numScenes.setValue(5)
self.chFolders = QSwitch()
self.chFolders.setChecked(True)
self.novelForm.addWidget(QLabel(self.tr("Add chapters")), 0, 0)
self.novelForm.addWidget(QLabel(self.tr("Scenes (per chapter)")), 1, 0)
self.novelForm.addWidget(QLabel(self.tr("Add chapter folders")), 2, 0)
self.novelForm.addWidget(self.numChapters, 0, 1, 1, 1, Qt.AlignRight)
self.novelForm.addWidget(self.numScenes, 1, 1, 1, 1, Qt.AlignRight)
self.novelForm.addWidget(self.chFolders, 2, 1, 1, 1, Qt.AlignRight)
self.novelForm.setRowStretch(3, 1)
# Grid Form
self.addBox = QGridLayout()
self.addBox.addWidget(QLabel(self.tr("Add a folder for plot notes")), 0, 0)
self.addBox.addWidget(QLabel(self.tr("Add a folder for character notes")), 1, 0)
self.addBox.addWidget(QLabel(self.tr("Add a folder for location notes")), 2, 0)
self.addBox.addWidget(QLabel(self.tr("Add example notes to the above")), 3, 0)
self.addBox.addWidget(QLabel(self.tr("Add chapters to the novel folder")), 4, 0)
self.addBox.addWidget(QLabel(self.tr("Add scenes to each chapter")), 5, 0)
self.addBox.addWidget(self.addPlot, 0, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.addChar, 1, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.addWorld, 2, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.addNotes, 3, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.numChapters, 4, 1, 1, 1, Qt.AlignRight)
self.addBox.addWidget(self.numScenes, 5, 1, 1, 1, Qt.AlignRight)
self.addBox.setVerticalSpacing(fS)
self.addBox.setHorizontalSpacing(cM)
self.addBox.setContentsMargins(cM, 0, cM, 0)
self.addBox.setColumnStretch(2, 1)
for i in range(6):
self.addBox.setRowMinimumHeight(i, mH)
# Wizard Fields
self.registerField("addPlot", self.addPlot)
self.registerField("addChar", self.addChar)
self.registerField("addWorld", self.addWorld)
self.registerField("addTime", self.addTime)
self.registerField("addObject", self.addObject)
self.registerField("addEntity", self.addEntity)
self.registerField("addNotes", self.addNotes)
self.registerField("numChapters", self.numChapters)
self.registerField("numScenes", self.numScenes)
self.registerField("chFolders", self.chFolders)
# Assemble
self.innerBox = QHBoxLayout()
self.innerBox.addWidget(self.rootGroup)
self.innerBox.addWidget(self.novelGroup)
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(vS)
self.outerBox.setSpacing(cM)
self.outerBox.addWidget(self.theText)
self.outerBox.addLayout(self.innerBox)
self.outerBox.addLayout(self.addBox)
self.outerBox.addStretch(1)
self.setLayout(self.outerBox)
@@ -441,15 +402,8 @@ class ProjWizardFinalPage(QWizardPage):
self.mainConf = novelwriter.CONFIG
self.theWizard = theWizard
self.setTitle(self.tr("Finished"))
self.theText = QLabel(
"<p>%s</p><p>%s</p>" % (
self.tr("All done."),
self.tr("Press '{0}' to create the new project.").format(
self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish")
)
)
)
self.setTitle(self.tr("Summary"))
self.theText = QLabel("")
self.theText.setWordWrap(True)
# Assemble
@@ -461,4 +415,51 @@ class ProjWizardFinalPage(QWizardPage):
return
def initializePage(self):
"""Update the summary information on the final page.
"""
QWizardPage.initializePage(self)
sumList = []
sumList.append(self.tr("Project Name: {0}").format(self.field("projName")))
sumList.append(self.tr("Project Path: {0}").format(self.field("projPath")))
if self.field("popMinimal"):
sumList.append(self.tr("Fill the project with a minimal set of items"))
elif self.field("popSample"):
sumList.append(self.tr("Fill the project with example files"))
elif self.field("popCustom"):
if self.field("addPlot"):
sumList.append(self.tr("Add a folder for plot notes"))
if self.field("addChar"):
sumList.append(self.tr("Add a folder for character notes"))
if self.field("addWorld"):
sumList.append(self.tr("Add a folder for location notes"))
if self.field("addNotes"):
sumList.append(self.tr("Add example notes to the above"))
if self.field("numChapters") > 0:
sumList.append(self.tr("Add {0} chapters to the novel folder").format(
self.field("numChapters")
))
if self.field("numScenes") > 0:
sumList.append(self.tr("Add {0} scenes to each chapter").format(
self.field("numScenes")
))
else:
if self.field("numScenes") > 0:
sumList.append(self.tr("Add {0} scenes").format(
self.field("numScenes")
))
self.theText.setText(
"<p>%s</p><p>&nbsp;&bull;&nbsp;%s</p><p>%s</p>" % (
self.tr("You have selected the following:"),
"<br>&nbsp;&bull;&nbsp;".join(sumList),
self.tr("Press '{0}' to create the new project.").format(
self.tr("Done") if self.mainConf.osDarwin else self.tr("Finish")
)
)
)
return
# END Class ProjWizardFinalPage
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:34:25">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:49:23">
<project>
<name>Test Custom</name>
<title>Test Novel</title>
@@ -29,110 +29,106 @@
<section></section>
</titleFormat>
<status>
<entry key="s000008" count="17" red="100" green="100" blue="100">New</entry>
<entry key="s000008" count="15" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="i00000c" count="6" red="100" green="100" blue="100">New</entry>
<entry key="i00000c" count="7" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="23">
<content count="22">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Timeline</name>
</item>
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Objects</name>
</item>
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="ENTITY">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Entities</name>
</item>
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Title Page</name>
</item>
<item handle="0000000000018" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Chapter 1</name>
</item>
<item handle="0000000000019" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Chapter 1</name>
</item>
<item handle="000000000001a" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000013" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 1.1</name>
</item>
<item handle="000000000001b" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000014" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 1.2</name>
</item>
<item handle="000000000001c" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000015" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 1.3</name>
</item>
<item handle="000000000001d" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Chapter 2</name>
</item>
<item handle="000000000001e" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Chapter 2</name>
</item>
<item handle="000000000001f" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000017" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 2.1</name>
</item>
<item handle="0000000000020" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000018" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 2.2</name>
</item>
<item handle="0000000000021" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000019" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 2.3</name>
</item>
<item handle="0000000000022" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Chapter 3</name>
</item>
<item handle="0000000000023" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001a" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Chapter 3</name>
</item>
<item handle="0000000000024" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001b" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 3.1</name>
</item>
<item handle="0000000000025" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001c" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 3.2</name>
</item>
<item handle="0000000000026" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001d" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 3.3</name>
</item>
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="000000000001f" parent="000000000001e" root="000000000001e" order="0" type="FILE" class="PLOT" layout="NOTE">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Main Plot</name>
</item>
<item handle="0000000000020" parent="None" root="0000000000020" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000021" parent="0000000000020" root="0000000000020" order="0" type="FILE" class="CHARACTER" layout="NOTE">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Protagonist</name>
</item>
<item handle="0000000000022" parent="None" root="0000000000022" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="0000000000023" parent="0000000000022" root="0000000000022" order="0" type="FILE" class="WORLD" layout="NOTE">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Main Location</name>
</item>
<item handle="0000000000024" parent="None" root="0000000000024" order="0" type="ROOT" class="ARCHIVE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Archive</name>
</item>
<item handle="0000000000025" parent="None" root="0000000000025" order="0" type="ROOT" class="TRASH">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Trash</name>
</item>
</content>
</novelWriterXML>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:34:25">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:48:40">
<project>
<name>Test Custom</name>
<title>Test Novel</title>
@@ -29,74 +29,82 @@
<section></section>
</titleFormat>
<status>
<entry key="s000008" count="8" red="100" green="100" blue="100">New</entry>
<entry key="s000008" count="9" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="i00000c" count="6" red="100" green="100" blue="100">New</entry>
<entry key="i00000c" count="7" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="14">
<content count="16">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Timeline</name>
</item>
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Objects</name>
</item>
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="ENTITY">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Entities</name>
</item>
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Title Page</name>
</item>
<item handle="0000000000018" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 1</name>
</item>
<item handle="0000000000019" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000013" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 2</name>
</item>
<item handle="000000000001a" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 3</name>
</item>
<item handle="000000000001b" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 4</name>
</item>
<item handle="000000000001c" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 5</name>
</item>
<item handle="000000000001d" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Scene 6</name>
</item>
<item handle="0000000000018" parent="None" root="0000000000018" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000019" parent="0000000000018" root="0000000000018" order="0" type="FILE" class="PLOT" layout="NOTE">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Main Plot</name>
</item>
<item handle="000000000001a" parent="None" root="000000000001a" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="000000000001b" parent="000000000001a" root="000000000001a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Protagonist</name>
</item>
<item handle="000000000001c" parent="None" root="000000000001c" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="000000000001d" parent="000000000001c" root="000000000001c" order="0" type="FILE" class="WORLD" layout="NOTE">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Main Location</name>
</item>
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="ARCHIVE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Archive</name>
</item>
<item handle="000000000001f" parent="None" root="000000000001f" order="0" type="ROOT" class="TRASH">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Trash</name>
</item>
</content>
</novelWriterXML>
@@ -1,8 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:34:25">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:08:21">
<project>
<name>New Project</name>
<title></title>
<title>New Novel</title>
<author>Jane Doe</author>
<saveCount>2</saveCount>
<autoCount>1</autoCount>
<editTime>0</editTime>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:34:25">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:50:26">
<project>
<name>New Project</name>
<title></title>
@@ -44,33 +44,33 @@
<meta expanded="False"/>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">World</name>
</item>
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">Title Page</name>
</item>
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="s000008" import="i00000c">New Chapter</name>
</item>
<item handle="0000000000016" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">New Chapter</name>
</item>
<item handle="0000000000017" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000013" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="s000008" import="i00000c" exported="True">New Scene</name>
</item>
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="0000000000017" parent="None" root="0000000000017" order="0" type="ROOT" class="ARCHIVE">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Archive</name>
</item>
</content>
</novelWriterXML>
@@ -1,8 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:34:25">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 22:07:28">
<project>
<name>New Project</name>
<title></title>
<title>New Novel</title>
<author>Jane Doe</author>
<saveCount>2</saveCount>
<autoCount>1</autoCount>
<editTime>0</editTime>
@@ -82,11 +83,11 @@
</item>
<item handle="000000000002a" parent="None" root="000000000002a" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Character</name>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="000000000002b" parent="None" root="000000000002b" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="s000008" import="i00000c">World</name>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="000000000002c" parent="None" root="000000000002c" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/>
@@ -94,15 +95,15 @@
</item>
<item handle="000000000002d" parent="None" root="000000000002d" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Object</name>
<name status="s000008" import="i00000c">Objects</name>
</item>
<item handle="000000000002e" parent="None" root="000000000002e" order="0" type="ROOT" class="CUSTOM">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Custom1</name>
<name status="s000008" import="i00000c">Custom</name>
</item>
<item handle="000000000002f" parent="None" root="000000000002f" order="0" type="ROOT" class="CUSTOM">
<meta expanded="False"/>
<name status="s000008" import="i00000c">Custom2</name>
<name status="s000008" import="i00000c">Custom</name>
</item>
</content>
</novelWriterXML>
@@ -1,9 +1,10 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 16:55:50">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:27:16">
<project>
<name>New Project</name>
<title></title>
<saveCount>5</saveCount>
<title>New Novel</title>
<author>Jane Doe</author>
<saveCount>4</saveCount>
<autoCount>2</autoCount>
<editTime>3</editTime>
</project>
@@ -15,8 +16,8 @@
<autoOutline>True</autoOutline>
<lastEdited>000000000000f</lastEdited>
<lastViewed>None</lastViewed>
<lastWordCount>126</lastWordCount>
<novelWordCount>99</novelWordCount>
<lastWordCount>129</lastWordCount>
<novelWordCount>102</novelWordCount>
<notesWordCount>27</notesWordCount>
<autoReplace/>
<titleFormat>
@@ -45,7 +46,7 @@
<name status="s000000" import="i000004">Novel</name>
</item>
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<meta expanded="False" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
<name status="s000000" import="i000004" exported="True">Title Page</name>
</item>
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
@@ -1,9 +1,10 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:28:44">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:25:41">
<project>
<name>New Project</name>
<title></title>
<saveCount>3</saveCount>
<title>New Novel</title>
<author>Jane Doe</author>
<saveCount>2</saveCount>
<autoCount>1</autoCount>
<editTime>0</editTime>
</project>
@@ -15,8 +16,8 @@
<autoOutline>True</autoOutline>
<lastEdited>None</lastEdited>
<lastViewed>None</lastViewed>
<lastWordCount>6</lastWordCount>
<novelWordCount>6</novelWordCount>
<lastWordCount>9</lastWordCount>
<novelWordCount>9</novelWordCount>
<notesWordCount>0</notesWordCount>
<autoReplace/>
<titleFormat>
@@ -45,7 +46,7 @@
<name status="s000000" import="i000004">Novel</name>
</item>
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<meta expanded="False" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
<name status="s000000" import="i000004" exported="True">Title Page</name>
</item>
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
@@ -1,11 +1,11 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-23 15:28:41">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-05-21 17:31:17">
<project>
<name>Project Name</name>
<title>Project Title</title>
<author>Jane Doe</author>
<author>John Doh</author>
<saveCount>2</saveCount>
<saveCount>1</saveCount>
<autoCount>1</autoCount>
<editTime>0</editTime>
</project>
@@ -17,8 +17,8 @@
<autoOutline>True</autoOutline>
<lastEdited>None</lastEdited>
<lastViewed>None</lastViewed>
<lastWordCount>6</lastWordCount>
<novelWordCount>6</novelWordCount>
<lastWordCount>9</lastWordCount>
<novelWordCount>9</novelWordCount>
<notesWordCount>0</notesWordCount>
<autoReplace>
<entry key="A">B</entry>
@@ -51,7 +51,7 @@
<name status="s000000" import="i000004">Novel</name>
</item>
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<meta expanded="False" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
<name status="s000000" import="i000004" exported="True">Title Page</name>
</item>
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
+1 -1
View File
@@ -280,7 +280,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
assert theIndex.scanText(xHandle, "Hello World!") is False
# Create the archive root
aHandle = theProject.newRoot("Archive", nwItemClass.ARCHIVE)
aHandle = theProject.newRoot(nwItemClass.ARCHIVE)
assert theProject.projTree[aHandle] is not None
xItem.setParent(aHandle)
theProject.projTree.updateItemData(xItem.itemHandle)
+23 -33
View File
@@ -26,7 +26,7 @@ from shutil import copyfile
from zipfile import ZipFile
from lxml import etree
from tools import cmpFiles, writeFile, readFile
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE
from mock import causeOSError
from novelwriter.core.project import NWProject
@@ -67,7 +67,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.saveProject() is True
assert theProject.closeProject() is True
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False
# Open a second time
@@ -77,7 +77,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.saveProject() is True
assert theProject.closeProject() is True
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# END Test testCoreProject_NewMinimal
@@ -103,13 +103,10 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
nwItemClass.PLOT,
nwItemClass.CHARACTER,
nwItemClass.WORLD,
nwItemClass.TIMELINE,
nwItemClass.OBJECT,
nwItemClass.ENTITY,
],
"addNotes": True,
"numChapters": 3,
"numScenes": 3,
"chFolders": True,
}
theProject = NWProject(mockGUI)
@@ -118,7 +115,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.closeProject() is True
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# END Test testCoreProject_NewCustomA
@@ -144,13 +141,10 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI, mockRnd):
nwItemClass.PLOT,
nwItemClass.CHARACTER,
nwItemClass.WORLD,
nwItemClass.TIMELINE,
nwItemClass.OBJECT,
nwItemClass.ENTITY,
],
"addNotes": True,
"numChapters": 0,
"numScenes": 6,
"chFolders": True,
}
theProject = NWProject(mockGUI)
@@ -159,7 +153,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.closeProject() is True
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# END Test testCoreProject_NewCustomB
@@ -258,28 +252,28 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
theProject = NWProject(mockGUI)
buildTestProject(theProject, fncDir)
assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.setProjectPath(fncDir) is True
assert theProject.saveProject() is True
assert theProject.closeProject() is True
assert theProject.openProject(projFile) is True
assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL), str)
assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), str)
assert isinstance(theProject.newRoot("Character", nwItemClass.CHARACTER), str)
assert isinstance(theProject.newRoot("World", nwItemClass.WORLD), str)
assert isinstance(theProject.newRoot("Timeline", nwItemClass.TIMELINE), str)
assert isinstance(theProject.newRoot("Object", nwItemClass.OBJECT), str)
assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str)
assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str)
assert isinstance(theProject.newRoot(nwItemClass.NOVEL), str)
assert isinstance(theProject.newRoot(nwItemClass.PLOT), str)
assert isinstance(theProject.newRoot(nwItemClass.CHARACTER), str)
assert isinstance(theProject.newRoot(nwItemClass.WORLD), str)
assert isinstance(theProject.newRoot(nwItemClass.TIMELINE), str)
assert isinstance(theProject.newRoot(nwItemClass.OBJECT), str)
assert isinstance(theProject.newRoot(nwItemClass.CUSTOM), str)
assert isinstance(theProject.newRoot(nwItemClass.CUSTOM), str)
assert theProject.projChanged is True
assert theProject.saveProject() is True
assert theProject.closeProject() is True
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False
# END Test testCoreProject_NewRoot
@@ -294,8 +288,8 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
theProject = NWProject(mockGUI)
buildTestProject(theProject, fncDir)
assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.setProjectPath(fncDir) is True
assert theProject.saveProject() is True
assert theProject.closeProject() is True
@@ -308,7 +302,7 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
assert theProject.closeProject() is True
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False
# END Test testCoreProject_NewFile
@@ -489,7 +483,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
assert theProject.saveProject() is True
assert theProject.saveCount == saveCount + 1
assert theProject.autoCount == autoCount
assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# Check that a second save creates a .bak file
assert os.path.isfile(backFile) is True
@@ -500,7 +494,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
assert theProject.saveProject(autoSave=True) is True
assert theProject.saveCount == saveCount
assert theProject.autoCount == autoCount + 1
assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
# Close test project
assert theProject.closeProject()
@@ -677,7 +671,7 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
"""Test the status and importance flag handling.
"""
theProject = NWProject(mockGUI)
assert theProject.newProject({"projPath": fncDir}) is True
buildTestProject(theProject, fncDir)
statusKeys = ["s000008", "s000009", "s00000a", "s00000b"]
importKeys = ["i00000c", "i00000d", "i00000e", "i00000f"]
@@ -798,7 +792,7 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
"""Test other project class methods and functions.
"""
theProject = NWProject(mockGUI)
assert theProject.newProject({"projPath": fncDir}) is True
buildTestProject(theProject, fncDir)
# Setting project path
assert theProject.setProjectPath(None)
@@ -1178,10 +1172,6 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
theProject = NWProject(mockGUI)
theProject.setProjectPath(fncDir)
# assert theProject.newProject({"projPath": fncDir})
# assert theProject.saveProject()
# assert theProject.closeProject()
# Check behaviour of deprecated files function on OSError
tstFile = os.path.join(fncDir, "ToC.json")
writeFile(tstFile, "stuff")
+2 -2
View File
@@ -23,7 +23,7 @@ import os
import pytest
from mock import causeOSError
from tools import getGuiItem, readFile, writeFile
from tools import getGuiItem, readFile, writeFile, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
@@ -41,7 +41,7 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
# Create a new project
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
# Handles for new objects
hNovelRoot = "0000000000008"
+2 -2
View File
@@ -23,7 +23,7 @@ import os
import pytest
from mock import causeOSError
from tools import getGuiItem, readFile, writeFile
from tools import getGuiItem, readFile, writeFile, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
@@ -42,7 +42,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
# Create a new project
assert nwGUI.newProject({"projPath": fncProj}) is True
buildTestProject(nwGUI, fncProj)
# Handles for new objects
hNovelRoot = "0000000000008"
+5 -5
View File
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest
from tools import getGuiItem
from tools import getGuiItem, buildTestProject
from PyQt5.QtWidgets import QAction, QDialog, QMessageBox
@@ -48,7 +48,7 @@ def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
assert nwGUI.editItem() is False
# Create and Open Project
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
tHandle = "000000000000f"
# No Selection
@@ -99,7 +99,7 @@ def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
tHandle = "000000000000f"
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
@@ -156,7 +156,7 @@ def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
assert nwGUI.theProject.importItems.name(importKeys[0]) == "New"
@@ -209,7 +209,7 @@ def testDlgItemEditor_Folder(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
# Edit a Folder
itemEdit = GuiItemEditor(nwGUI, "000000000000d")
+4 -3
View File
@@ -23,7 +23,7 @@ import os
import pytest
from shutil import copyfile
from tools import cmpFiles, getGuiItem
from tools import cmpFiles, getGuiItem, buildTestProject
from PyQt5.QtGui import QColor
from PyQt5.QtCore import Qt
@@ -57,7 +57,7 @@ def testDlgProjSettings_Dialog(
assert getGuiItem("GuiProjectSettings") is None
# Create new project
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
nwGUI.mainConf.backupPath = fncDir
nwGUI.theProject.setSpellLang("en")
@@ -81,7 +81,7 @@ def testDlgProjSettings_Dialog(
# ============
assert projEdit.tabMain.editName.text() == "New Project"
assert projEdit.tabMain.editTitle.text() == ""
assert projEdit.tabMain.editTitle.text() == "New Novel"
assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Smith\nJohn Smith"
assert projEdit.tabMain.spellLang.currentData() == "en"
assert projEdit.tabMain.doBackup.isChecked() is False
@@ -90,6 +90,7 @@ def testDlgProjSettings_Dialog(
projEdit.tabMain.editName.setText("")
for c in "Project Name":
qtbot.keyClick(projEdit.tabMain.editName, c, delay=typeDelay)
projEdit.tabMain.editTitle.setText("")
for c in "Project Title":
qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=typeDelay)
+97 -10
View File
@@ -23,7 +23,7 @@ import os
import pytest
from shutil import copyfile
from tools import cmpFiles
from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox, QDialog
@@ -32,6 +32,7 @@ from novelwriter.gui import (
GuiDocEditor, GuiProjectTree, GuiNovelTree, GuiOutline
)
from novelwriter.enum import nwItemType, nwWidget
from novelwriter.tools import GuiProjectWizard
from novelwriter.dialogs.itemeditor import GuiItemEditor
keyDelay = 2
@@ -70,15 +71,52 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
# END Test testGuiMain_NoProject
@pytest.mark.gui
def testGuiMain_NewProject(monkeypatch, nwGUI, fncProj):
"""Test creating a new project.
"""
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# No data
with monkeypatch.context() as mp:
mp.setattr(GuiProjectWizard, "exec_", lambda *a: None)
assert nwGUI.newProject(projData=None) is False
# Close project
with monkeypatch.context() as mp:
nwGUI.hasProject = True
mp.setattr(QMessageBox, "question", lambda *a: QMessageBox.No)
assert nwGUI.newProject(projData={"projPath": fncProj}) is False
# No project path
assert nwGUI.newProject(projData={}) is False
# Project file already exists
projFile = os.path.join(fncProj, nwGUI.theProject.projFile)
writeFile(projFile, "Stuff")
assert nwGUI.newProject(projData={"projPath": fncProj}) is False
os.unlink(projFile)
# An unreachable path should also fail
projPath = os.path.join(fncProj, "stuff", "stuff", "stuff")
assert nwGUI.newProject(projData={"projPath": projPath}) is False
# This one should work just fine
assert nwGUI.newProject(projData={"projPath": fncProj}) is True
assert os.path.isfile(os.path.join(fncProj, nwGUI.theProject.projFile))
assert os.path.isdir(os.path.join(fncProj, "content"))
# END Test testGuiMain_NewProject
@pytest.mark.gui
def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test handling of project tree items based on GUI focus states.
"""
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
assert nwGUI.newProject({"projPath": fncProj}) is True
assert nwGUI.saveProject() is True
# assert False
buildTestProject(nwGUI, fncProj)
sHandle = "000000000000f"
assert nwGUI.openSelectedItem() is False
@@ -139,7 +177,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
# Create new, save, close project
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
assert nwGUI.saveProject()
assert nwGUI.closeProject()
@@ -160,7 +198,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
testFile = os.path.join(outDir, "guiEditor_Main_Initial_nwProject.nwx")
compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx")
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
qtbot.wait(stepDelay)
# qtbot.stopForInteraction()
@@ -178,9 +216,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta")
assert nwGUI.theProject.projFile == "nwProject.nwx"
assert nwGUI.theProject.projName == "New Project"
assert nwGUI.theProject.bookTitle == ""
assert len(nwGUI.theProject.bookAuthors) == 0
assert not nwGUI.theProject.spellCheck
assert nwGUI.theProject.bookTitle == "New Novel"
assert len(nwGUI.theProject.bookAuthors) == 1
assert nwGUI.theProject.spellCheck is False
# Check that tree items have been created
assert nwGUI.treeView._getTreeItem("0000000000008") is not None
@@ -438,7 +476,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
testFile = os.path.join(outDir, "guiEditor_Main_Final_nwProject.nwx")
compFile = os.path.join(refDir, "guiEditor_Main_Final_nwProject.nwx")
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
projFile = os.path.join(fncProj, "content", "000000000000f.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_000000000000f.nwd")
@@ -467,3 +505,52 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# qtbot.stopForInteraction()
# END Test testGuiMain_Editing
@pytest.mark.gui
def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test toggling focus mode in main window.
"""
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
buildTestProject(nwGUI, fncProj)
assert nwGUI.isFocusMode is False
# Focus Mode
# ==========
# No document open, so not allowing focus mode
assert nwGUI.toggleFocusMode() is False
# Open a file in editor and viewer
assert nwGUI.openDocument("000000000000f")
assert nwGUI.viewDocument("000000000000f")
# Enable focus mode
assert nwGUI.toggleFocusMode() is True
assert nwGUI.treePane.isVisible() is False
assert nwGUI.statusBar.isVisible() is False
assert nwGUI.mainMenu.isVisible() is False
assert nwGUI.viewsBar.isVisible() is False
assert nwGUI.splitView.isVisible() is False
# Disable focus mode
assert nwGUI.toggleFocusMode() is True
assert nwGUI.treePane.isVisible() is True
assert nwGUI.statusBar.isVisible() is True
assert nwGUI.mainMenu.isVisible() is True
assert nwGUI.viewsBar.isVisible() is True
assert nwGUI.splitView.isVisible() is True
# Full Screen Mode
# ================
assert nwGUI.mainConf.isFullScreen is False
nwGUI.toggleFullScreenMode()
assert nwGUI.mainConf.isFullScreen is True
nwGUI.toggleFullScreenMode()
assert nwGUI.mainConf.isFullScreen is False
# qtbot.stopForInteraction()
# END Test testGuiMain_FocusFullMode
+2 -2
View File
@@ -26,7 +26,7 @@ from PyQt5.QtCore import Qt
from PyQt5.QtGui import QTextCursor, QTextBlock
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
from tools import writeFile
from tools import writeFile, buildTestProject
from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.enum import nwDocAction, nwDocInsert
@@ -465,7 +465,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
assert nwGUI.treeView._getTreeItem("000000000000f") is not None
assert nwGUI.openDocument("000000000000f") is True
+5 -3
View File
@@ -22,6 +22,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest
import os
from tools import buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox
from novelwriter.guimain import GuiMain
@@ -47,7 +49,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
# Create a project
prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True
buildTestProject(nwGUI, prjDir)
# No itemType set
nwTree.clearSelection()
@@ -155,7 +157,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# Create a project
prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True
buildTestProject(nwGUI, prjDir)
# Move Documents
# ==============
@@ -283,7 +285,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
# Create a project
prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True
buildTestProject(nwGUI, prjDir)
# Try emptying the trash already now, when there is no trash folder
assert nwTree.emptyTrash() is False
+5 -3
View File
@@ -22,6 +22,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import time
import pytest
from tools import buildTestProject
from PyQt5.QtWidgets import QMessageBox
from novelwriter.core import NWDoc
@@ -34,7 +36,7 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
assert nwGUI.newProject({"projPath": fncProj}) is True
buildTestProject(nwGUI, fncProj)
cHandle = nwGUI.theProject.newFile("A Note", "000000000000a")
newDoc = NWDoc(nwGUI.theProject, cHandle)
newDoc.writeDocument("# A Note\n\n")
@@ -89,10 +91,10 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Project Stats
nwGUI.statusBar.mainConf.incNotesWCount = False
nwGUI._updateStatusWordCount()
assert nwGUI.statusBar.statsText.text() == "Words: 6 (+6)"
assert nwGUI.statusBar.statsText.text() == "Words: 9 (+9)"
nwGUI.statusBar.mainConf.incNotesWCount = True
nwGUI._updateStatusWordCount()
assert nwGUI.statusBar.statsText.text() == "Words: 8 (+8)"
assert nwGUI.statusBar.statsText.text() == "Words: 11 (+11)"
# qtbot.stopForInteraction()
+2 -2
View File
@@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import pytest
from tools import getGuiItem
from tools import getGuiItem, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox
@@ -40,7 +40,7 @@ def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
assert getGuiItem("GuiLipsum") is None
# Create a new project
assert nwGUI.newProject({"projPath": fncProj}) is True
buildTestProject(nwGUI, fncProj)
assert nwGUI.openDocument("000000000000f") is True
assert len(nwGUI.docEditor.getText()) == 15
+4 -10
View File
@@ -205,14 +205,11 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
customPage.addPlot.setChecked(True)
customPage.addChar.setChecked(True)
customPage.addWorld.setChecked(True)
customPage.addTime.setChecked(True)
customPage.addObject.setChecked(True)
customPage.addEntity.setChecked(True)
customPage.addNotes.setChecked(True)
if prjType == "custom2":
customPage.numChapters.setValue(0)
customPage.numScenes.setValue(10)
customPage.chFolders.setChecked(False)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton)
@@ -240,23 +237,20 @@ def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType):
nwItemClass.PLOT,
nwItemClass.CHARACTER,
nwItemClass.WORLD,
nwItemClass.TIMELINE,
nwItemClass.OBJECT,
nwItemClass.ENTITY,
]
if prjType == "custom1":
assert projData["numChapters"] == 5
assert projData["numScenes"] == 5
assert projData["chFolders"]
assert projData["addNotes"] is True
else:
assert projData["numChapters"] == 0
assert projData["numScenes"] == 10
assert not projData["chFolders"]
assert projData["addNotes"] is True
else:
assert projData["addRoots"] == []
assert projData["numChapters"] == 0
assert projData["numScenes"] == 0
assert not projData["chFolders"]
assert projData["addNotes"] is False
# Cleanup
nwWiz.reject()
+2 -2
View File
@@ -23,8 +23,8 @@ import pytest
import json
import os
from tools import getGuiItem, writeFile
from mock import causeOSError
from tools import getGuiItem, writeFile, buildTestProject
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
@@ -48,7 +48,7 @@ def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
# Create a project to work on
assert nwGUI.newProject({"projPath": fncProj})
buildTestProject(nwGUI, fncProj)
qtbot.wait(100)
assert nwGUI.saveProject()
sessFile = os.path.join(fncProj, "meta", nwFiles.SESS_STATS)
+56
View File
@@ -20,10 +20,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import os
import time
import shutil
from PyQt5.QtWidgets import qApp
XML_IGNORE = ("<novelWriterXML", "<saveCount", "<autoCount", "<editTime")
def cmpFiles(fileOne, fileTwo, ignoreLines=None, ignoreStart=None):
"""Compare two files, but optionally ignore lines given by a list.
@@ -119,3 +122,56 @@ def cleanProject(projPath):
os.unlink(tocFile)
return
def buildTestProject(theObject, projPath):
"""Build a standard test project in projPath using theProject
object as the parent.
"""
from novelwriter.enum import nwItemClass
from novelwriter.core import NWProject, NWDoc
if isinstance(theObject, NWProject):
theGUI = None
theProject = theObject
else:
theGUI = theObject
theProject = theObject.theProject
theProject.clearProject()
theProject.setProjectPath(projPath, newProject=True)
theProject.setProjectName("New Project")
theProject.setBookTitle("New Novel")
theProject.setBookAuthors("Jane Doe")
# Creating a minimal project with a few root folders and a
# single chapter folder with a single file.
xHandle = {}
xHandle[1] = theProject.newRoot(nwItemClass.NOVEL, "Novel")
xHandle[2] = theProject.newRoot(nwItemClass.PLOT, "Plot")
xHandle[3] = theProject.newRoot(nwItemClass.CHARACTER, "Characters")
xHandle[4] = theProject.newRoot(nwItemClass.WORLD, "World")
xHandle[5] = theProject.newFile("Title Page", xHandle[1])
xHandle[6] = theProject.newFolder("New Chapter", xHandle[1])
xHandle[7] = theProject.newFile("New Chapter", xHandle[6])
xHandle[8] = theProject.newFile("New Scene", xHandle[6])
aDoc = NWDoc(theProject, xHandle[5])
aDoc.writeDocument("#! New Novel\n\n>> By Jane DOe <<\n")
aDoc = NWDoc(theProject, xHandle[7])
aDoc.writeDocument("## %s\n\n" % theProject.tr("New Chapter"))
aDoc = NWDoc(theProject, xHandle[8])
aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene"))
theProject.projOpened = time.time()
theProject.setProjectChanged(True)
theProject.saveProject(autoSave=True)
if theGUI is not None:
theGUI.hasProject = True
theGUI.rebuildTrees()
theGUI.rebuildIndex(beQuiet=True)
return