Refactor project XML handling (#1221)
This commit is contained in:
@@ -75,7 +75,7 @@ class NWIndex:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<NWIndex project='{self.theProject.projName}'>"
|
return f"<NWIndex project='{self.theProject.data.name}'>"
|
||||||
|
|
||||||
##
|
##
|
||||||
# Properties
|
# Properties
|
||||||
|
|||||||
+82
-122
@@ -25,8 +25,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
|
|
||||||
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified
|
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified
|
||||||
@@ -148,107 +146,70 @@ class NWItem:
|
|||||||
return self._cursorPos
|
return self._cursorPos
|
||||||
|
|
||||||
##
|
##
|
||||||
# XML Pack/Unpack
|
# Pack/Unpack Data
|
||||||
##
|
##
|
||||||
|
|
||||||
def packXML(self, xParent):
|
def pack(self):
|
||||||
"""Pack all the data in the class instance into an XML object.
|
"""Pack all the data in the class instance into a dictionary.
|
||||||
"""
|
"""
|
||||||
itemAttrib = {}
|
item = {}
|
||||||
itemAttrib["handle"] = str(self._handle)
|
meta = {}
|
||||||
itemAttrib["parent"] = str(self._parent)
|
name = {}
|
||||||
itemAttrib["root"] = str(self._root)
|
|
||||||
itemAttrib["order"] = str(self._order)
|
item["handle"] = str(self._handle)
|
||||||
itemAttrib["type"] = str(self._type.name)
|
item["parent"] = str(self._parent)
|
||||||
itemAttrib["class"] = str(self._class.name)
|
item["root"] = str(self._root)
|
||||||
|
item["order"] = str(self._order)
|
||||||
|
item["type"] = str(self._type.name)
|
||||||
|
item["class"] = str(self._class.name)
|
||||||
|
meta["expanded"] = str(self._expanded)
|
||||||
|
name["status"] = str(self._status)
|
||||||
|
name["import"] = str(self._import)
|
||||||
|
|
||||||
if self._type == nwItemType.FILE:
|
if self._type == nwItemType.FILE:
|
||||||
itemAttrib["layout"] = str(self._layout.name)
|
item["layout"] = str(self._layout.name)
|
||||||
|
meta["heading"] = str(self._heading)
|
||||||
|
meta["charCount"] = str(self._charCount)
|
||||||
|
meta["wordCount"] = str(self._wordCount)
|
||||||
|
meta["paraCount"] = str(self._paraCount)
|
||||||
|
meta["cursorPos"] = str(self._cursorPos)
|
||||||
|
name["active"] = str(self._active)
|
||||||
|
|
||||||
metaAttrib = {}
|
data = {
|
||||||
metaAttrib["expanded"] = str(self._expanded)
|
"name": str(self._name),
|
||||||
if self._type == nwItemType.FILE:
|
"itemAttr": item,
|
||||||
metaAttrib["mainHeading"] = str(self._heading)
|
"metaAttr": meta,
|
||||||
metaAttrib["charCount"] = str(self._charCount)
|
"nameAttr": name,
|
||||||
metaAttrib["wordCount"] = str(self._wordCount)
|
}
|
||||||
metaAttrib["paraCount"] = str(self._paraCount)
|
|
||||||
metaAttrib["cursorPos"] = str(self._cursorPos)
|
|
||||||
|
|
||||||
nameAttrib = {}
|
return data
|
||||||
nameAttrib["status"] = str(self._status)
|
|
||||||
nameAttrib["import"] = str(self._import)
|
|
||||||
if self._type == nwItemType.FILE:
|
|
||||||
nameAttrib["active"] = str(self._active)
|
|
||||||
|
|
||||||
xPack = etree.SubElement(xParent, "item", attrib=itemAttrib)
|
def unpack(self, data):
|
||||||
self._subPack(xPack, "meta", attrib=metaAttrib)
|
"""Set the values from a data dictionary.
|
||||||
self._subPack(xPack, "name", text=str(self._name), attrib=nameAttrib)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def unpackXML(self, xItem):
|
|
||||||
"""Set the values from an XML entry of type 'item'.
|
|
||||||
"""
|
"""
|
||||||
if xItem.tag != "item":
|
if "handle" in data:
|
||||||
logger.error("XML entry is not an NWItem")
|
self.setHandle(data["handle"])
|
||||||
return False
|
|
||||||
|
|
||||||
if "handle" in xItem.attrib:
|
|
||||||
self.setHandle(xItem.attrib["handle"])
|
|
||||||
else:
|
else:
|
||||||
logger.error("XML item entry does not have a handle")
|
logger.error("Item does not have a handle")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.setParent(xItem.attrib.get("parent", None))
|
self.setName(data.get("label", ""))
|
||||||
self.setRoot(xItem.attrib.get("root", None))
|
self.setParent(data.get("parent", None))
|
||||||
self.setOrder(xItem.attrib.get("order", 0))
|
self.setRoot(data.get("root", None))
|
||||||
self.setType(xItem.attrib.get("type", nwItemType.NO_TYPE))
|
self.setOrder(data.get("order", 0))
|
||||||
self.setClass(xItem.attrib.get("class", nwItemClass.NO_CLASS))
|
self.setType(data.get("type", nwItemType.NO_TYPE))
|
||||||
self.setLayout(xItem.attrib.get("layout", nwItemLayout.NO_LAYOUT))
|
self.setClass(data.get("class", nwItemClass.NO_CLASS))
|
||||||
|
self.setLayout(data.get("layout", nwItemLayout.NO_LAYOUT))
|
||||||
|
|
||||||
for xValue in xItem:
|
self.setExpanded(data.get("expanded", False))
|
||||||
if xValue.tag == "meta":
|
self.setStatus(data.get("status", None))
|
||||||
self.setExpanded(xValue.attrib.get("expanded", False))
|
self.setImport(data.get("import", None))
|
||||||
self.setMainHeading(xValue.attrib.get("mainHeading", "H0"))
|
self.setMainHeading(data.get("heading", "H0"))
|
||||||
self.setCharCount(xValue.attrib.get("charCount", 0))
|
self.setCharCount(data.get("charCount", 0))
|
||||||
self.setWordCount(xValue.attrib.get("wordCount", 0))
|
self.setWordCount(data.get("wordCount", 0))
|
||||||
self.setParaCount(xValue.attrib.get("paraCount", 0))
|
self.setParaCount(data.get("paraCount", 0))
|
||||||
self.setCursorPos(xValue.attrib.get("cursorPos", 0))
|
self.setCursorPos(data.get("cursorPos", 0))
|
||||||
elif xValue.tag == "name":
|
self.setActive(data.get("active", True))
|
||||||
self.setName(xValue.text)
|
|
||||||
self.setStatus(xValue.attrib.get("status", None))
|
|
||||||
self.setImport(xValue.attrib.get("import", None))
|
|
||||||
self.setActive(xValue.attrib.get("active", True))
|
|
||||||
|
|
||||||
# ToDo: Remove before 2.0 release. Only needed for 2.0 pre-releases.
|
|
||||||
if "exported" in xValue.attrib:
|
|
||||||
self.setActive(xValue.attrib.get("exported", True))
|
|
||||||
|
|
||||||
# Legacy Format (1.3 and earlier)
|
|
||||||
elif xValue.tag == "status":
|
|
||||||
self.setImportStatus(xValue.text)
|
|
||||||
elif xValue.tag == "type":
|
|
||||||
self.setType(xValue.text)
|
|
||||||
elif xValue.tag == "class":
|
|
||||||
self.setClass(xValue.text)
|
|
||||||
elif xValue.tag == "layout":
|
|
||||||
self.setLayout(xValue.text)
|
|
||||||
elif xValue.tag == "expanded":
|
|
||||||
self.setExpanded(xValue.text)
|
|
||||||
elif xValue.tag == "exported":
|
|
||||||
self.setActive(xValue.text)
|
|
||||||
elif xValue.tag == "charCount":
|
|
||||||
self.setCharCount(xValue.text)
|
|
||||||
elif xValue.tag == "wordCount":
|
|
||||||
self.setWordCount(xValue.text)
|
|
||||||
elif xValue.tag == "paraCount":
|
|
||||||
self.setParaCount(xValue.text)
|
|
||||||
elif xValue.tag == "cursorPos":
|
|
||||||
self.setCursorPos(xValue.text)
|
|
||||||
else:
|
|
||||||
# Sliently skip as we may otherwise cause orphaned
|
|
||||||
# items if an otherwise valid file is opened by a
|
|
||||||
# version of novelWriter that doesn't know the tag
|
|
||||||
logger.error("Unknown tag '%s'", xValue.tag)
|
|
||||||
|
|
||||||
# Make some checks to ensure consistency
|
# Make some checks to ensure consistency
|
||||||
if self._type == nwItemType.ROOT:
|
if self._type == nwItemType.ROOT:
|
||||||
@@ -256,26 +217,17 @@ class NWItem:
|
|||||||
self._parent = None # Root items cannot have a parent
|
self._parent = None # Root items cannot have a parent
|
||||||
|
|
||||||
if self._type != nwItemType.FILE:
|
if self._type != nwItemType.FILE:
|
||||||
self._charCount = 0 # Only set for files
|
# Reset values that should only be set for files
|
||||||
self._wordCount = 0 # Only set for files
|
self._layout = nwItemLayout.NO_LAYOUT
|
||||||
self._paraCount = 0 # Only set for files
|
self._heading = "H0"
|
||||||
self._cursorPos = 0 # Only set for files
|
self._active = False
|
||||||
|
self._charCount = 0
|
||||||
|
self._wordCount = 0
|
||||||
|
self._paraCount = 0
|
||||||
|
self._cursorPos = 0
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _subPack(xParent, name, attrib=None, text=None, none=True):
|
|
||||||
"""Pack the values into an XML element.
|
|
||||||
"""
|
|
||||||
if not none and (text is None or text == "None"):
|
|
||||||
return None
|
|
||||||
xAttr = {} if attrib is None else attrib
|
|
||||||
xSub = etree.SubElement(xParent, name, attrib=xAttr)
|
|
||||||
if text is not None:
|
|
||||||
xSub.text = text
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Lookup Methods
|
# Lookup Methods
|
||||||
##
|
##
|
||||||
@@ -310,11 +262,11 @@ class NWItem:
|
|||||||
the current item based on its class.
|
the current item based on its class.
|
||||||
"""
|
"""
|
||||||
if self.isNovelLike():
|
if self.isNovelLike():
|
||||||
stName = self.theProject.statusItems.name(self._status)
|
stName = self.theProject.data.itemStatus.name(self._status)
|
||||||
stIcon = self.theProject.statusItems.icon(self._status) if incIcon else None
|
stIcon = self.theProject.data.itemStatus.icon(self._status) if incIcon else None
|
||||||
else:
|
else:
|
||||||
stName = self.theProject.importItems.name(self._import)
|
stName = self.theProject.data.itemImport.name(self._import)
|
||||||
stIcon = self.theProject.importItems.icon(self._import) if incIcon else None
|
stIcon = self.theProject.data.itemImport.icon(self._import) if incIcon else None
|
||||||
return stName, stIcon
|
return stName, stIcon
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -451,8 +403,6 @@ class NWItem:
|
|||||||
self._type = value
|
self._type = value
|
||||||
elif isItemType(value):
|
elif isItemType(value):
|
||||||
self._type = nwItemType[value]
|
self._type = nwItemType[value]
|
||||||
elif value == "TRASH":
|
|
||||||
self._type = nwItemType.ROOT
|
|
||||||
else:
|
else:
|
||||||
logger.error("Unrecognised item type '%s'", value)
|
logger.error("Unrecognised item type '%s'", value)
|
||||||
self._type = nwItemType.NO_TYPE
|
self._type = nwItemType.NO_TYPE
|
||||||
@@ -479,8 +429,6 @@ class NWItem:
|
|||||||
self._layout = value
|
self._layout = value
|
||||||
elif isItemLayout(value):
|
elif isItemLayout(value):
|
||||||
self._layout = nwItemLayout[value]
|
self._layout = nwItemLayout[value]
|
||||||
elif value in ("TITLE", "PAGE", "BOOK", "PARTITION", "UNNUMBERED", "CHAPTER", "SCENE"):
|
|
||||||
self._layout = nwItemLayout.DOCUMENT
|
|
||||||
else:
|
else:
|
||||||
logger.error("Unrecognised item layout '%s'", value)
|
logger.error("Unrecognised item layout '%s'", value)
|
||||||
self._layout = nwItemLayout.NO_LAYOUT
|
self._layout = nwItemLayout.NO_LAYOUT
|
||||||
@@ -490,14 +438,14 @@ class NWItem:
|
|||||||
"""Set the item status by looking it up in the valid status
|
"""Set the item status by looking it up in the valid status
|
||||||
items of the current project.
|
items of the current project.
|
||||||
"""
|
"""
|
||||||
self._status = self.theProject.statusItems.check(value)
|
self._status = self.theProject.data.itemStatus.check(value)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setImport(self, value):
|
def setImport(self, value):
|
||||||
"""Set the item importance by looking it up in the valid import
|
"""Set the item importance by looking it up in the valid import
|
||||||
items of the current project.
|
items of the current project.
|
||||||
"""
|
"""
|
||||||
self._import = self.theProject.importItems.check(value)
|
self._import = self.theProject.data.itemImport.check(value)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setActive(self, state):
|
def setActive(self, state):
|
||||||
@@ -532,25 +480,37 @@ class NWItem:
|
|||||||
def setCharCount(self, count):
|
def setCharCount(self, count):
|
||||||
"""Set the character count, and ensure that it is an integer.
|
"""Set the character count, and ensure that it is an integer.
|
||||||
"""
|
"""
|
||||||
self._charCount = max(0, checkInt(count, 0))
|
if isinstance(count, int):
|
||||||
|
self._charCount = max(0, count)
|
||||||
|
else:
|
||||||
|
self._charCount = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def setWordCount(self, count):
|
def setWordCount(self, count):
|
||||||
"""Set the word count, and ensure that it is an integer.
|
"""Set the word count, and ensure that it is an integer.
|
||||||
"""
|
"""
|
||||||
self._wordCount = max(0, checkInt(count, 0))
|
if isinstance(count, int):
|
||||||
|
self._wordCount = max(0, count)
|
||||||
|
else:
|
||||||
|
self._wordCount = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def setParaCount(self, count):
|
def setParaCount(self, count):
|
||||||
"""Set the paragraph count, and ensure that it is an integer.
|
"""Set the paragraph count, and ensure that it is an integer.
|
||||||
"""
|
"""
|
||||||
self._paraCount = max(0, checkInt(count, 0))
|
if isinstance(count, int):
|
||||||
|
self._paraCount = max(0, count)
|
||||||
|
else:
|
||||||
|
self._paraCount = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def setCursorPos(self, position):
|
def setCursorPos(self, position):
|
||||||
"""Set the cursor position, and ensure that it is an integer.
|
"""Set the cursor position, and ensure that it is an integer.
|
||||||
"""
|
"""
|
||||||
self._cursorPos = max(0, checkInt(position, 0))
|
if isinstance(position, int):
|
||||||
|
self._cursorPos = max(0, position)
|
||||||
|
else:
|
||||||
|
self._cursorPos = 0
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveInitialCount(self):
|
def saveInitialCount(self):
|
||||||
|
|||||||
+451
-493
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,557 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Project XML Read/Write
|
||||||
|
====================================
|
||||||
|
Classes for reading and writing the project XML file
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2022-09-28 [2.0rc1] ProjectXMLReader
|
||||||
|
Created: 2022-09-28 [2.0rc1] XMLReadState
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2022, 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 os
|
||||||
|
import logging
|
||||||
|
import novelwriter
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from lxml import etree
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
from novelwriter.common import (
|
||||||
|
checkBool, checkInt, checkStringNone, formatTimeStamp, simplified, checkString
|
||||||
|
)
|
||||||
|
from novelwriter.constants import nwFiles
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
FILE_VERSION = "1.4" # The current project file format version
|
||||||
|
|
||||||
|
NUM_VERSION = {
|
||||||
|
"1.0": 0x0100,
|
||||||
|
"1.1": 0x0101,
|
||||||
|
"1.2": 0x0102,
|
||||||
|
"1.3": 0x0103,
|
||||||
|
"1.4": 0x0104,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class XMLReadState(Enum):
|
||||||
|
|
||||||
|
NO_ACTION = 0
|
||||||
|
NO_ERROR = 1
|
||||||
|
PARSED_BACKUP = 2
|
||||||
|
CANNOT_PARSE = 3
|
||||||
|
NOT_NWX_FILE = 4
|
||||||
|
UNKNOWN_VERSION = 5
|
||||||
|
PARSED_OK = 6
|
||||||
|
WAS_LEGACY = 7
|
||||||
|
|
||||||
|
# END Class XMLReadState
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectXMLReader:
|
||||||
|
"""The main project XML file reader class. All data is read into a
|
||||||
|
NWProjectData instance, which must be provided.
|
||||||
|
|
||||||
|
Version Change History
|
||||||
|
======================
|
||||||
|
1.0 Original file format.
|
||||||
|
|
||||||
|
1.1 Changes the way documents are structured in the project folder
|
||||||
|
from data_X, where X is the first hex value of the handle, to a
|
||||||
|
single content folder. Introduced in version 0.7.
|
||||||
|
|
||||||
|
1.2 Changes the way autoReplace entries are stored. The 1.1 parser
|
||||||
|
will lose the autoReplace settings if allowed to read the file.
|
||||||
|
Introduced in version 0.10.
|
||||||
|
|
||||||
|
1.3 Reduces the number of layouts to only two. One for novel
|
||||||
|
documents and one for project notes. Introduced in version 1.5.
|
||||||
|
|
||||||
|
1.4 Introduces a more compact format for storing items. All settings
|
||||||
|
aside from name are now attributes. This format also changes the
|
||||||
|
way satus and importance labels are stored and handled.
|
||||||
|
Introduced in version 2.0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
|
||||||
|
self._path = path
|
||||||
|
self._state = XMLReadState.NO_ACTION
|
||||||
|
|
||||||
|
self._root = ""
|
||||||
|
self._version = 0x0000
|
||||||
|
self._appVersion = ""
|
||||||
|
self._hexVersion = ""
|
||||||
|
self._timeStamp = ""
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Properties
|
||||||
|
##
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""The state of the parsing as an XMLReadState enum value.
|
||||||
|
"""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def xmlRoot(self):
|
||||||
|
"""The root tag name of the XNL file,
|
||||||
|
"""
|
||||||
|
return self._root
|
||||||
|
|
||||||
|
@property
|
||||||
|
def xmlVersion(self):
|
||||||
|
"""The project XML version number.
|
||||||
|
"""
|
||||||
|
return self._version
|
||||||
|
|
||||||
|
@property
|
||||||
|
def appVersion(self):
|
||||||
|
"""The novelWriter version number who wrote the file.
|
||||||
|
"""
|
||||||
|
return self._appVersion
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hexVersion(self):
|
||||||
|
"""The novelWriter version number who wrote the file as hex.
|
||||||
|
"""
|
||||||
|
return self._hexVersion
|
||||||
|
|
||||||
|
@property
|
||||||
|
def timeStamp(self):
|
||||||
|
"""The date and time when the file was written.
|
||||||
|
"""
|
||||||
|
return self._timeStamp
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def read(self, projData, projContent):
|
||||||
|
"""Read and parse the project XML file.
|
||||||
|
"""
|
||||||
|
tStart = time()
|
||||||
|
logger.debug("Reading project XML")
|
||||||
|
|
||||||
|
try:
|
||||||
|
xml = etree.parse(self._path)
|
||||||
|
self._state = XMLReadState.NO_ERROR
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
# Trying to open backup file instead
|
||||||
|
logger.error("Failed to parse project xml", exc_info=exc)
|
||||||
|
self._state = XMLReadState.CANNOT_PARSE
|
||||||
|
|
||||||
|
backFile = self._path[:-3]+"bak"
|
||||||
|
if os.path.isfile(backFile):
|
||||||
|
try:
|
||||||
|
xml = etree.parse(backFile)
|
||||||
|
self._state = XMLReadState.PARSED_BACKUP
|
||||||
|
logger.info("Backup project file parsed")
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error("Failed to parse backup project xml", exc_info=exc)
|
||||||
|
self._state = XMLReadState.CANNOT_PARSE
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self._state = XMLReadState.CANNOT_PARSE
|
||||||
|
return False
|
||||||
|
|
||||||
|
xRoot = xml.getroot()
|
||||||
|
self._root = str(xRoot.tag)
|
||||||
|
if self._root != "novelWriterXML":
|
||||||
|
self._state = XMLReadState.NOT_NWX_FILE
|
||||||
|
return False
|
||||||
|
|
||||||
|
fileVersion = str(xRoot.attrib.get("fileVersion", ""))
|
||||||
|
if fileVersion in NUM_VERSION:
|
||||||
|
self._version = NUM_VERSION[fileVersion]
|
||||||
|
else:
|
||||||
|
self._state = XMLReadState.UNKNOWN_VERSION
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.debug("XML is '%s' version '%s'", self._root, fileVersion)
|
||||||
|
|
||||||
|
self._appVersion = str(xRoot.attrib.get("appVersion", ""))
|
||||||
|
self._hexVersion = str(xRoot.attrib.get("hexVersion", ""))
|
||||||
|
self._timeStamp = str(xRoot.attrib.get("timeStamp", ""))
|
||||||
|
|
||||||
|
for xSection in xRoot:
|
||||||
|
if xSection.tag == "project":
|
||||||
|
self._parseProjectMeta(xSection, projData)
|
||||||
|
elif xSection.tag == "settings":
|
||||||
|
self._parseProjectSettings(xSection, projData)
|
||||||
|
elif xSection.tag == "content":
|
||||||
|
if self._version >= 0x0104:
|
||||||
|
self._parseProjectContent(xSection, projContent)
|
||||||
|
else:
|
||||||
|
self._parseProjectContentLegacy(xSection, projContent, projData)
|
||||||
|
else:
|
||||||
|
logger.warning("Ignored <root/%s> in xml", xSection.tag)
|
||||||
|
|
||||||
|
if self._version == 0x0104:
|
||||||
|
self._state = XMLReadState.PARSED_OK
|
||||||
|
else:
|
||||||
|
self._state = XMLReadState.WAS_LEGACY
|
||||||
|
|
||||||
|
logger.debug("Project XML loaded in %.3f ms", (time() - tStart)*1000)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _parseProjectMeta(self, xSection, projData):
|
||||||
|
"""Parse the project section of the XML file.
|
||||||
|
"""
|
||||||
|
logger.debug("Parsing <project> section")
|
||||||
|
for xItem in xSection:
|
||||||
|
if xItem.tag == "name":
|
||||||
|
projData.setName(xItem.text)
|
||||||
|
elif xItem.tag == "title":
|
||||||
|
projData.setTitle(xItem.text)
|
||||||
|
elif xItem.tag == "author":
|
||||||
|
projData.addAuthor(xItem.text)
|
||||||
|
elif xItem.tag == "saveCount":
|
||||||
|
projData.setSaveCount(xItem.text)
|
||||||
|
elif xItem.tag == "autoCount":
|
||||||
|
projData.setAutoCount(xItem.text)
|
||||||
|
elif xItem.tag == "editTime":
|
||||||
|
projData.setEditTime(xItem.text)
|
||||||
|
else:
|
||||||
|
logger.warning("Ignored <root/project/%s> in xml", xItem.tag)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _parseProjectSettings(self, xSection, projData):
|
||||||
|
"""Parse the settings section of the XML file.
|
||||||
|
"""
|
||||||
|
logger.debug("Parsing <settings> section")
|
||||||
|
|
||||||
|
for xItem in xSection:
|
||||||
|
if xItem.tag == "doBackup":
|
||||||
|
projData.setDoBackup(xItem.text)
|
||||||
|
elif xItem.tag == "language":
|
||||||
|
projData.setLanguage(xItem.text)
|
||||||
|
elif xItem.tag == "spellCheck":
|
||||||
|
projData.setSpellCheck(xItem.text)
|
||||||
|
elif xItem.tag == "spellLang":
|
||||||
|
projData.setSpellLang(xItem.text)
|
||||||
|
elif xItem.tag == "novelWordCount":
|
||||||
|
projData.setInitCounts(novel=xItem.text)
|
||||||
|
elif xItem.tag == "notesWordCount":
|
||||||
|
projData.setInitCounts(notes=xItem.text)
|
||||||
|
elif xItem.tag == "status":
|
||||||
|
self._parseStatusImport(xItem, projData.itemStatus)
|
||||||
|
elif xItem.tag in ("import", "importance"):
|
||||||
|
self._parseStatusImport(xItem, projData.itemImport)
|
||||||
|
elif xItem.tag == "lastHandle":
|
||||||
|
projData.setLastHandle(self._parseDictKeyText(xItem))
|
||||||
|
elif xItem.tag == "autoReplace":
|
||||||
|
if self._version >= 0x0102:
|
||||||
|
projData.setAutoReplace(self._parseDictKeyText(xItem))
|
||||||
|
else: # Pre 1.2 format
|
||||||
|
projData.setAutoReplace(self._parseDictTagText(xItem))
|
||||||
|
elif xItem.tag == "titleFormat":
|
||||||
|
if self._version >= 0x0104:
|
||||||
|
projData.setTitleFormat(self._parseDictKeyText(xItem))
|
||||||
|
else: # Pre 1.4 format
|
||||||
|
projData.setTitleFormat(self._parseDictTagText(xItem))
|
||||||
|
else:
|
||||||
|
logger.warning("Ignored <root/settings/%s> in xml", xItem.tag)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _parseProjectContent(self, xSection, projContent):
|
||||||
|
"""Parse the content section of the XML file.
|
||||||
|
"""
|
||||||
|
logger.debug("Parsing <content> section")
|
||||||
|
|
||||||
|
for xItem in xSection:
|
||||||
|
if xItem.tag == "item":
|
||||||
|
item = {}
|
||||||
|
item["handle"] = checkStringNone(xItem.attrib.get("handle"), None)
|
||||||
|
item["parent"] = checkStringNone(xItem.attrib.get("parent"), None)
|
||||||
|
item["root"] = checkStringNone(xItem.attrib.get("root"), None)
|
||||||
|
item["order"] = checkInt(xItem.attrib.get("order"), 0)
|
||||||
|
item["type"] = checkString(xItem.attrib.get("type"), "NO_TYPE")
|
||||||
|
item["class"] = checkString(xItem.attrib.get("class"), "NO_CLASS")
|
||||||
|
item["layout"] = checkString(xItem.attrib.get("layout"), "NO_LAYOUT")
|
||||||
|
for xVal in xItem:
|
||||||
|
if xVal.tag == "meta":
|
||||||
|
item["expanded"] = checkBool(xVal.attrib.get("expanded"), False)
|
||||||
|
item["heading"] = checkString(xVal.attrib.get("heading"), "H0")
|
||||||
|
item["charCount"] = checkInt(xVal.attrib.get("charCount"), 0)
|
||||||
|
item["wordCount"] = checkInt(xVal.attrib.get("wordCount"), 0)
|
||||||
|
item["paraCount"] = checkInt(xVal.attrib.get("paraCount"), 0)
|
||||||
|
item["cursorPos"] = checkInt(xVal.attrib.get("cursorPos"), 0)
|
||||||
|
elif xVal.tag == "name":
|
||||||
|
item["label"] = simplified(checkString(xVal.text, ""))
|
||||||
|
item["status"] = checkStringNone(xVal.attrib.get("status"), None)
|
||||||
|
item["import"] = checkStringNone(xVal.attrib.get("import"), None)
|
||||||
|
item["active"] = checkBool(xVal.attrib.get("active"), False)
|
||||||
|
|
||||||
|
# ToDo: Remove before 2.0 release. Only needed for 2.0 pre-releases.
|
||||||
|
if "exported" in xVal.attrib:
|
||||||
|
item["active"] = checkBool(xVal.attrib.get("exported"), False)
|
||||||
|
else:
|
||||||
|
logger.warning("Ignored <root/content/item/%s> in xml", xVal.tag)
|
||||||
|
|
||||||
|
projContent.append(item)
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.warning("Ignored item <root/content/%s> in xml", xItem.tag)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _parseProjectContentLegacy(self, xSection, projContent, projData):
|
||||||
|
"""Parse the content section of the XML file for older versions.
|
||||||
|
"""
|
||||||
|
logger.debug("Parsing <content> section (legacy format)")
|
||||||
|
|
||||||
|
# Create maps to look up name -> key for status and importance
|
||||||
|
statusMap = {entry["name"]: key for key, entry in projData.itemStatus.items()}
|
||||||
|
importMap = {entry["name"]: key for key, entry in projData.itemImport.items()}
|
||||||
|
|
||||||
|
for xItem in xSection:
|
||||||
|
item = {}
|
||||||
|
if xItem.tag == "item":
|
||||||
|
item["handle"] = checkStringNone(xItem.attrib.get("handle", None), None)
|
||||||
|
item["parent"] = checkStringNone(xItem.attrib.get("parent", None), None)
|
||||||
|
item["root"] = None # Value was added in 1.4
|
||||||
|
item["order"] = checkInt(xItem.attrib.get("order", 0), 0)
|
||||||
|
item["heading"] = "H0" # Value was added in 1.4
|
||||||
|
|
||||||
|
tmpStatus = ""
|
||||||
|
for xVal in xItem:
|
||||||
|
if xVal.tag == "name":
|
||||||
|
item["label"] = simplified(checkString(xVal.text, ""))
|
||||||
|
elif xVal.tag == "status":
|
||||||
|
tmpStatus = checkStringNone(xVal.text, None)
|
||||||
|
elif xVal.tag == "type":
|
||||||
|
item["type"] = checkString(xVal.text, "")
|
||||||
|
elif xVal.tag == "class":
|
||||||
|
item["class"] = checkString(xVal.text, "")
|
||||||
|
elif xVal.tag == "layout":
|
||||||
|
item["layout"] = checkString(xVal.text, "")
|
||||||
|
elif xVal.tag == "expanded":
|
||||||
|
item["expanded"] = checkBool(xVal.text, False)
|
||||||
|
elif xVal.tag == "exported": # Renamed to active in 1.4
|
||||||
|
item["active"] = checkBool(xVal.text, False)
|
||||||
|
elif xVal.tag == "charCount":
|
||||||
|
item["charCount"] = checkInt(xVal.text, 0)
|
||||||
|
elif xVal.tag == "wordCount":
|
||||||
|
item["wordCount"] = checkInt(xVal.text, 0)
|
||||||
|
elif xVal.tag == "paraCount":
|
||||||
|
item["paraCount"] = checkInt(xVal.text, 0)
|
||||||
|
elif xVal.tag == "cursorPos":
|
||||||
|
item["cursorPos"] = checkInt(xVal.text, 0)
|
||||||
|
else:
|
||||||
|
logger.warning("Ignored <root/content/item/%s> in xml", xVal.tag)
|
||||||
|
|
||||||
|
# Status was split into separate status/import with a key in 1.4
|
||||||
|
if item.get("class", "") in ("NOVEL", "ARCHIVE"):
|
||||||
|
item["status"] = statusMap.get(tmpStatus, None)
|
||||||
|
else:
|
||||||
|
item["import"] = importMap.get(tmpStatus, None)
|
||||||
|
|
||||||
|
# A number of layouts were removed in 1.3
|
||||||
|
if item.get("layout", "") in (
|
||||||
|
"TITLE", "PAGE", "BOOK", "PARTITION", "UNNUMBERED", "CHAPTER", "SCENE"
|
||||||
|
):
|
||||||
|
item["layout"] = "DOCUMENT"
|
||||||
|
|
||||||
|
# The trast type was removed in 1.4
|
||||||
|
if item.get("type", "") == "TRASH":
|
||||||
|
item["type"] = "ROOT"
|
||||||
|
|
||||||
|
projContent.append(item)
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.warning("Ignored <root/content/%s> in xml", xItem.tag)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _parseStatusImport(self, xItem, sObject):
|
||||||
|
"""Parse a status or importance entry.
|
||||||
|
"""
|
||||||
|
for xEntry in xItem:
|
||||||
|
if xEntry.tag == "entry":
|
||||||
|
key = xEntry.attrib.get("key", None)
|
||||||
|
red = checkInt(xEntry.attrib.get("red", 0), 0)
|
||||||
|
green = checkInt(xEntry.attrib.get("green", 0), 0)
|
||||||
|
blue = checkInt(xEntry.attrib.get("blue", 0), 0)
|
||||||
|
count = checkInt(xEntry.attrib.get("count", 0), 0)
|
||||||
|
sObject.write(key, xEntry.text, (red, green, blue), count)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _parseDictKeyText(self, xItem):
|
||||||
|
"""Parse a dictionary stored with key as an attribute and the
|
||||||
|
value as the text porperty.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for xEntry in xItem:
|
||||||
|
if xEntry.tag == "entry" and "key" in xEntry.attrib:
|
||||||
|
result[xEntry.attrib["key"]] = checkString(xEntry.text, "")
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _parseDictTagText(self, xItem):
|
||||||
|
"""Parse a dictionary stored with key as the tag and the value
|
||||||
|
as the text porperty.
|
||||||
|
"""
|
||||||
|
return {n.tag: checkString(n.text, "") for n in xItem}
|
||||||
|
|
||||||
|
# END Class ProjectXMLReader
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectXMLWriter:
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
|
||||||
|
self._path = path
|
||||||
|
self._error = None
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Properties
|
||||||
|
##
|
||||||
|
|
||||||
|
@property
|
||||||
|
def error(self):
|
||||||
|
return self._error
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def write(self, projData, projContent, saveTime, editTime):
|
||||||
|
"""Write the project data and content to the XML files.
|
||||||
|
"""
|
||||||
|
tStart = time()
|
||||||
|
logger.debug("Writing project XML")
|
||||||
|
|
||||||
|
xRoot = etree.Element("novelWriterXML", attrib={
|
||||||
|
"appVersion": str(novelwriter.__version__),
|
||||||
|
"hexVersion": str(novelwriter.__hexversion__),
|
||||||
|
"fileVersion": FILE_VERSION,
|
||||||
|
"timeStamp": formatTimeStamp(saveTime),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Save Project Meta
|
||||||
|
xProject = etree.SubElement(xRoot, "project")
|
||||||
|
self._packSingleValue(xProject, "name", projData.name)
|
||||||
|
self._packSingleValue(xProject, "title", projData.title)
|
||||||
|
self._packListValue(xProject, "author", projData.authors)
|
||||||
|
self._packSingleValue(xProject, "saveCount", projData.saveCount)
|
||||||
|
self._packSingleValue(xProject, "autoCount", projData.autoCount)
|
||||||
|
self._packSingleValue(xProject, "editTime", editTime)
|
||||||
|
|
||||||
|
# Save Project Settings
|
||||||
|
xSettings = etree.SubElement(xRoot, "settings")
|
||||||
|
self._packSingleValue(xSettings, "doBackup", projData.doBackup)
|
||||||
|
self._packSingleValue(xSettings, "language", projData.language)
|
||||||
|
self._packSingleValue(xSettings, "spellCheck", projData.spellCheck)
|
||||||
|
self._packSingleValue(xSettings, "spellLang", projData.spellLang)
|
||||||
|
self._packSingleValue(xSettings, "novelWordCount", projData.currCounts[0])
|
||||||
|
self._packSingleValue(xSettings, "notesWordCount", projData.currCounts[1])
|
||||||
|
self._packDictKeyValue(xSettings, "lastHandle", projData.lastHandle)
|
||||||
|
self._packDictKeyValue(xSettings, "autoReplace", projData.autoReplace)
|
||||||
|
self._packDictKeyValue(xSettings, "titleFormat", projData.titleFormat)
|
||||||
|
|
||||||
|
# Save Status/Importance
|
||||||
|
xStatus = etree.SubElement(xSettings, "status")
|
||||||
|
for label, attrib in projData.itemStatus.pack():
|
||||||
|
self._packSingleValue(xStatus, "entry", label, attrib=attrib)
|
||||||
|
|
||||||
|
xImport = etree.SubElement(xSettings, "importance")
|
||||||
|
for label, attrib in projData.itemImport.pack():
|
||||||
|
self._packSingleValue(xImport, "entry", label, attrib=attrib)
|
||||||
|
|
||||||
|
# Save Tree Content
|
||||||
|
xContent = etree.SubElement(xRoot, "content", attrib={"count": str(len(projContent))})
|
||||||
|
for item in projContent:
|
||||||
|
xItem = etree.SubElement(xContent, "item", attrib=item.get("itemAttr", {}))
|
||||||
|
etree.SubElement(xItem, "meta", attrib=item.get("metaAttr", {}))
|
||||||
|
xName = etree.SubElement(xItem, "name", attrib=item.get("nameAttr", {}))
|
||||||
|
xName.text = item["name"]
|
||||||
|
|
||||||
|
# Write the xml tree to file
|
||||||
|
saveFile = os.path.join(self._path, nwFiles.PROJ_FILE)
|
||||||
|
tempFile = os.path.join(self._path, nwFiles.PROJ_FILE+"~")
|
||||||
|
backFile = os.path.join(self._path, nwFiles.PROJ_FILE[:-3]+"bak")
|
||||||
|
try:
|
||||||
|
with open(tempFile, mode="wb") as outFile:
|
||||||
|
outFile.write(etree.tostring(
|
||||||
|
xRoot,
|
||||||
|
pretty_print=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
xml_declaration=True
|
||||||
|
))
|
||||||
|
except Exception as exc:
|
||||||
|
self._error = exc
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If we're here, the file was successfully saved,
|
||||||
|
# so let's sort out the temps and backups
|
||||||
|
try:
|
||||||
|
if os.path.isfile(saveFile):
|
||||||
|
os.replace(saveFile, backFile)
|
||||||
|
os.replace(tempFile, saveFile)
|
||||||
|
except OSError as exc:
|
||||||
|
self._error = exc
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.debug("Project XML saved in %.3f ms", (time() - tStart)*1000)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _packSingleValue(self, xParent, name, value, attrib=None):
|
||||||
|
"""Pack a single value into an xml element.
|
||||||
|
"""
|
||||||
|
xItem = etree.SubElement(xParent, name, attrib=attrib)
|
||||||
|
xItem.text = str(value) or ""
|
||||||
|
return
|
||||||
|
|
||||||
|
def _packListValue(self, xParent, name, data):
|
||||||
|
"""Pack a list of values into an xml element.
|
||||||
|
"""
|
||||||
|
for value in data:
|
||||||
|
xItem = etree.SubElement(xParent, name)
|
||||||
|
xItem.text = str(value) or ""
|
||||||
|
return
|
||||||
|
|
||||||
|
def _packDictKeyValue(self, xParent, name, data):
|
||||||
|
"""Pack the entries of a dictionary into an xml element.
|
||||||
|
"""
|
||||||
|
xItem = etree.SubElement(xParent, name)
|
||||||
|
for key, value in data.items():
|
||||||
|
if len(key) > 0:
|
||||||
|
xEntry = etree.SubElement(xItem, "entry", attrib={"key": key})
|
||||||
|
xEntry.text = str(value) or ""
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class ProjectXMLWriter
|
||||||
+21
-32
@@ -28,12 +28,10 @@ import random
|
|||||||
import logging
|
import logging
|
||||||
import novelwriter
|
import novelwriter
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
|
|
||||||
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
|
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
|
||||||
from PyQt5.QtCore import QRectF, Qt
|
from PyQt5.QtCore import QRectF, Qt
|
||||||
|
|
||||||
from novelwriter.common import checkInt, minmax, simplified
|
from novelwriter.common import minmax, simplified
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -47,7 +45,6 @@ class NWStatus:
|
|||||||
|
|
||||||
self._type = type
|
self._type = type
|
||||||
self._store = {}
|
self._store = {}
|
||||||
self._reverse = {}
|
|
||||||
self._default = None
|
self._default = None
|
||||||
|
|
||||||
self._iPX = novelwriter.CONFIG.pxInt(24)
|
self._iPX = novelwriter.CONFIG.pxInt(24)
|
||||||
@@ -58,7 +55,7 @@ class NWStatus:
|
|||||||
self._iconPath = QPainterPath()
|
self._iconPath = QPainterPath()
|
||||||
self._iconPath.addRoundedRect(QRectF(pA, pA, pB, pB), pR, pR)
|
self._iconPath.addRoundedRect(QRectF(pA, pA, pB, pB), pR, pR)
|
||||||
|
|
||||||
self._defaultIcon = self._createIcon([100, 100, 100])
|
self._defaultIcon = self._createIcon(100, 100, 100)
|
||||||
|
|
||||||
if self._type == self.STATUS:
|
if self._type == self.STATUS:
|
||||||
self._prefix = "s"
|
self._prefix = "s"
|
||||||
@@ -80,17 +77,19 @@ class NWStatus:
|
|||||||
if len(col) != 3:
|
if len(col) != 3:
|
||||||
col = (100, 100, 100)
|
col = (100, 100, 100)
|
||||||
|
|
||||||
|
cR = minmax(col[0], 0, 255)
|
||||||
|
cG = minmax(col[1], 0, 255)
|
||||||
|
cB = minmax(col[2], 0, 255)
|
||||||
name = simplified(name)
|
name = simplified(name)
|
||||||
if count is None:
|
if count is None:
|
||||||
count = self._store[key]["count"] if key in self._store else 0
|
count = self._store.get(key, {}).get("count", 0)
|
||||||
|
|
||||||
self._store[key] = {
|
self._store[key] = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"icon": self._createIcon(col),
|
"icon": self._createIcon(cR, cG, cB),
|
||||||
"cols": col,
|
"cols": (cR, cG, cB),
|
||||||
"count": count,
|
"count": count,
|
||||||
}
|
}
|
||||||
self._reverse[name] = key
|
|
||||||
|
|
||||||
if self._default is None:
|
if self._default is None:
|
||||||
self._default = key
|
self._default = key
|
||||||
@@ -106,7 +105,6 @@ class NWStatus:
|
|||||||
if self._store[key]["count"] > 0:
|
if self._store[key]["count"] > 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
del self._reverse[self._store[key]["name"]]
|
|
||||||
del self._store[key]
|
del self._store[key]
|
||||||
|
|
||||||
keys = list(self._store.keys())
|
keys = list(self._store.keys())
|
||||||
@@ -123,8 +121,6 @@ class NWStatus:
|
|||||||
"""
|
"""
|
||||||
if self._isKey(value) and value in self._store:
|
if self._isKey(value) and value in self._store:
|
||||||
return value
|
return value
|
||||||
elif value in self._reverse:
|
|
||||||
return self._reverse[value]
|
|
||||||
elif self._default is not None:
|
elif self._default is not None:
|
||||||
return self._default
|
return self._default
|
||||||
else:
|
else:
|
||||||
@@ -206,37 +202,30 @@ class NWStatus:
|
|||||||
self._store[key]["count"] += 1
|
self._store[key]["count"] += 1
|
||||||
return
|
return
|
||||||
|
|
||||||
def packXML(self, xParent):
|
def pack(self):
|
||||||
"""Pack the status entries into an XML object for saving to the
|
"""Pack the status entries into a dictionary.
|
||||||
main project file.
|
|
||||||
"""
|
"""
|
||||||
for key, data in self._store.items():
|
for key, data in self._store.items():
|
||||||
xSub = etree.SubElement(xParent, "entry", attrib={
|
yield (data["name"], {
|
||||||
"key": key,
|
"key": key,
|
||||||
"count": str(data["count"]),
|
"count": str(data["count"]),
|
||||||
"red": str(data["cols"][0]),
|
"red": str(data["cols"][0]),
|
||||||
"green": str(data["cols"][1]),
|
"green": str(data["cols"][1]),
|
||||||
"blue": str(data["cols"][2]),
|
"blue": str(data["cols"][2]),
|
||||||
})
|
})
|
||||||
xSub.text = data["name"]
|
return
|
||||||
|
|
||||||
return True
|
def unpack(self, data):
|
||||||
|
"""Unpack a data dictionary and set the class values.
|
||||||
def unpackXML(self, xParent):
|
|
||||||
"""Unpack an XML tree and set the class values.
|
|
||||||
"""
|
"""
|
||||||
self._store = {}
|
self._store = {}
|
||||||
self._reverse = {}
|
|
||||||
self._default = None
|
self._default = None
|
||||||
|
|
||||||
for xChild in xParent:
|
for key, entry in data.items():
|
||||||
key = xChild.attrib.get("key", None)
|
label = entry.get("label", "")
|
||||||
name = xChild.text.strip()
|
colour = entry.get("colour", (100, 100, 100))
|
||||||
count = max(checkInt(xChild.attrib.get("count", 0), 0), 0)
|
count = entry.get("count", 0)
|
||||||
red = minmax(checkInt(xChild.attrib.get("red", 100), 100), 0, 255)
|
self.write(key, label, colour, count)
|
||||||
green = minmax(checkInt(xChild.attrib.get("green", 100), 100), 0, 255)
|
|
||||||
blue = minmax(checkInt(xChild.attrib.get("blue", 100), 100), 0, 255)
|
|
||||||
self.write(key, name, (red, green, blue), count)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -270,7 +259,7 @@ class NWStatus:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _createIcon(self, col):
|
def _createIcon(self, red, green, blue):
|
||||||
"""Generate an icon for a status label.
|
"""Generate an icon for a status label.
|
||||||
"""
|
"""
|
||||||
pixmap = QPixmap(self._iPX, self._iPX)
|
pixmap = QPixmap(self._iPX, self._iPX)
|
||||||
@@ -278,7 +267,7 @@ class NWStatus:
|
|||||||
|
|
||||||
painter = QPainter(pixmap)
|
painter = QPainter(pixmap)
|
||||||
painter.setRenderHint(QPainter.Antialiasing)
|
painter.setRenderHint(QPainter.Antialiasing)
|
||||||
painter.fillPath(self._iconPath, QColor(*col))
|
painter.fillPath(self._iconPath, QColor(red, green, blue))
|
||||||
painter.end()
|
painter.end()
|
||||||
|
|
||||||
return QIcon(pixmap)
|
return QIcon(pixmap)
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ class ToHtml(Tokenizer):
|
|||||||
"</body>\n"
|
"</body>\n"
|
||||||
"</html>\n"
|
"</html>\n"
|
||||||
).format(
|
).format(
|
||||||
projTitle=self.theProject.projName,
|
projTitle=self.theProject.data.name,
|
||||||
htmlStyle="\n".join(theStyle),
|
htmlStyle="\n".join(theStyle),
|
||||||
bodyText=bodyText,
|
bodyText=bodyText,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -327,9 +327,10 @@ class Tokenizer(ABC):
|
|||||||
"""Run trough the various replace doctionaries.
|
"""Run trough the various replace doctionaries.
|
||||||
"""
|
"""
|
||||||
# Process the user's auto-replace dictionary
|
# Process the user's auto-replace dictionary
|
||||||
if len(self.theProject.autoReplace) > 0:
|
autoReplace = self.theProject.data.autoReplace
|
||||||
|
if len(autoReplace) > 0:
|
||||||
repDict = {}
|
repDict = {}
|
||||||
for aKey, aVal in self.theProject.autoReplace.items():
|
for aKey, aVal in autoReplace.items():
|
||||||
repDict[f"<{aKey}>"] = aVal
|
repDict[f"<{aKey}>"] = aVal
|
||||||
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
|
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
|
||||||
self._theText = xRep.sub(lambda x: repDict[x.group(0)], self._theText)
|
self._theText = xRep.sub(lambda x: repDict[x.group(0)], self._theText)
|
||||||
|
|||||||
@@ -261,8 +261,8 @@ class ToOdt(Tokenizer):
|
|||||||
# ===============
|
# ===============
|
||||||
|
|
||||||
if self._headerText == "":
|
if self._headerText == "":
|
||||||
theTitle = self.theProject.bookTitle
|
theTitle = self.theProject.data.title
|
||||||
theAuth = self.theProject.getAuthors()
|
theAuth = self.theProject.getFormattedAuthors()
|
||||||
self._headerText = f"{theTitle} / {theAuth} /"
|
self._headerText = f"{theTitle} / {theAuth} /"
|
||||||
|
|
||||||
# Create Roots
|
# Create Roots
|
||||||
|
|||||||
+10
-17
@@ -27,8 +27,6 @@ import os
|
|||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
from novelwriter.common import checkHandle
|
from novelwriter.common import checkHandle
|
||||||
@@ -114,30 +112,25 @@ class NWTree:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def packXML(self, xParent):
|
def pack(self):
|
||||||
"""Pack the content of the tree into the provided XML object. In
|
"""Pack the content of the tree into the provided XML object. In
|
||||||
the order defined by the _treeOrder list.
|
the order defined by the _treeOrder list.
|
||||||
"""
|
"""
|
||||||
xContent = etree.SubElement(xParent, "content", attrib={
|
tree = []
|
||||||
"count": str(len(self._treeOrder))}
|
|
||||||
)
|
|
||||||
for tHandle in self._treeOrder:
|
for tHandle in self._treeOrder:
|
||||||
tItem = self.__getitem__(tHandle)
|
tItem = self.__getitem__(tHandle)
|
||||||
tItem.packXML(xContent)
|
if tItem:
|
||||||
return
|
tree.append(tItem.pack())
|
||||||
|
return tree
|
||||||
|
|
||||||
def unpackXML(self, xContent):
|
def unpack(self, data):
|
||||||
"""Iterate through all items of a content XML object and add
|
"""Iterate through all items of a list and add them to the
|
||||||
them to the project tree.
|
project tree.
|
||||||
"""
|
"""
|
||||||
if xContent.tag != "content":
|
|
||||||
logger.error("XML entry is not a NWTree")
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.clear()
|
self.clear()
|
||||||
for xItem in xContent:
|
for item in data:
|
||||||
nwItem = NWItem(self.theProject)
|
nwItem = NWItem(self.theProject)
|
||||||
if nwItem.unpackXML(xItem):
|
if nwItem.unpack(item):
|
||||||
self.append(nwItem.itemHandle, nwItem.itemParent, nwItem)
|
self.append(nwItem.itemHandle, nwItem.itemParent, nwItem)
|
||||||
nwItem.saveInitialCount()
|
nwItem.saveInitialCount()
|
||||||
|
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ class GuiProjectDetailsMain(QWidget):
|
|||||||
# Header
|
# Header
|
||||||
# ======
|
# ======
|
||||||
|
|
||||||
self.bookTitle = QLabel(self.theProject.bookTitle)
|
self.bookTitle = QLabel(self.theProject.data.title)
|
||||||
bookFont = self.bookTitle.font()
|
bookFont = self.bookTitle.font()
|
||||||
bookFont.setPointSizeF(2.2*fPt)
|
bookFont.setPointSizeF(2.2*fPt)
|
||||||
bookFont.setWeight(QFont.Bold)
|
bookFont.setWeight(QFont.Bold)
|
||||||
@@ -166,7 +166,7 @@ class GuiProjectDetailsMain(QWidget):
|
|||||||
self.bookTitle.setWordWrap(True)
|
self.bookTitle.setWordWrap(True)
|
||||||
|
|
||||||
self.projName = QLabel(
|
self.projName = QLabel(
|
||||||
self.tr("Working Title: {0}").format(self.theProject.projName)
|
self.tr("Working Title: {0}").format(self.theProject.data.name)
|
||||||
)
|
)
|
||||||
workFont = self.projName.font()
|
workFont = self.projName.font()
|
||||||
workFont.setPointSizeF(0.8*fPt)
|
workFont.setPointSizeF(0.8*fPt)
|
||||||
@@ -175,7 +175,9 @@ class GuiProjectDetailsMain(QWidget):
|
|||||||
self.projName.setAlignment(Qt.AlignHCenter)
|
self.projName.setAlignment(Qt.AlignHCenter)
|
||||||
self.projName.setWordWrap(True)
|
self.projName.setWordWrap(True)
|
||||||
|
|
||||||
self.bookAuthors = QLabel(self.tr("By {0}").format(self.theProject.getAuthors()))
|
self.bookAuthors = QLabel(self.tr("By {0}").format(
|
||||||
|
self.theProject.getFormattedAuthors()
|
||||||
|
))
|
||||||
authFont = self.bookAuthors.font()
|
authFont = self.bookAuthors.font()
|
||||||
authFont.setPointSizeF(1.2*fPt)
|
authFont.setPointSizeF(1.2*fPt)
|
||||||
self.bookAuthors.setFont(authFont)
|
self.bookAuthors.setFont(authFont)
|
||||||
@@ -255,7 +257,7 @@ class GuiProjectDetailsMain(QWidget):
|
|||||||
self.wordCountVal.setText(f"{nwCount:n}")
|
self.wordCountVal.setText(f"{nwCount:n}")
|
||||||
self.chapCountVal.setText(f"{hCounts[2]:n}")
|
self.chapCountVal.setText(f"{hCounts[2]:n}")
|
||||||
self.sceneCountVal.setText(f"{hCounts[3]:n}")
|
self.sceneCountVal.setText(f"{hCounts[3]:n}")
|
||||||
self.revCountVal.setText(f"{self.theProject.saveCount:n}")
|
self.revCountVal.setText(f"{self.theProject.data.saveCount:n}")
|
||||||
self.editTimeVal.setText(f"{edTime//3600:02d}:{edTime%3600//60:02d}")
|
self.editTimeVal.setText(f"{edTime//3600:02d}:{edTime%3600//60:02d}")
|
||||||
|
|
||||||
self.projPathVal.setText(self.theProject.projPath)
|
self.projPathVal.setText(self.theProject.projPath)
|
||||||
|
|||||||
@@ -114,13 +114,13 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
spellLang = self.tabMain.spellLang.currentData()
|
spellLang = self.tabMain.spellLang.currentData()
|
||||||
doBackup = not self.tabMain.doBackup.isChecked()
|
doBackup = not self.tabMain.doBackup.isChecked()
|
||||||
|
|
||||||
self.theProject.setProjectName(projName)
|
self.theProject.data.setName(projName)
|
||||||
self.theProject.setBookTitle(bookTitle)
|
self.theProject.data.setTitle(bookTitle)
|
||||||
self.theProject.setBookAuthors(bookAuthors)
|
self.theProject.data.setAuthors(bookAuthors)
|
||||||
self.theProject.setProjBackup(doBackup)
|
self.theProject.data.setDoBackup(doBackup)
|
||||||
|
|
||||||
# Remember this as updating spell dictionary can be expensive
|
# Remember this as updating spell dictionary can be expensive
|
||||||
self._spellChanged = self.theProject.setSpellLang(spellLang)
|
self._spellChanged = self.theProject.data.setSpellLang(spellLang)
|
||||||
|
|
||||||
if self.tabStatus.colChanged:
|
if self.tabStatus.colChanged:
|
||||||
newList, delList = self.tabStatus.getNewList()
|
newList, delList = self.tabStatus.getNewList()
|
||||||
@@ -135,7 +135,7 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
|
|
||||||
if self.tabReplace.arChanged:
|
if self.tabReplace.arChanged:
|
||||||
newList = self.tabReplace.getNewList()
|
newList = self.tabReplace.getNewList()
|
||||||
self.theProject.setAutoReplace(newList)
|
self.theProject.data.setAutoReplace(newList)
|
||||||
|
|
||||||
self._saveGuiSettings()
|
self._saveGuiSettings()
|
||||||
self.accept()
|
self.accept()
|
||||||
@@ -209,7 +209,7 @@ class GuiProjectEditMain(QWidget):
|
|||||||
self.editName = QLineEdit()
|
self.editName = QLineEdit()
|
||||||
self.editName.setMaxLength(200)
|
self.editName.setMaxLength(200)
|
||||||
self.editName.setMaximumWidth(xW)
|
self.editName.setMaximumWidth(xW)
|
||||||
self.editName.setText(self.theProject.projName)
|
self.editName.setText(self.theProject.data.name)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Project name"),
|
self.tr("Project name"),
|
||||||
self.editName,
|
self.editName,
|
||||||
@@ -219,7 +219,7 @@ class GuiProjectEditMain(QWidget):
|
|||||||
self.editTitle = QLineEdit()
|
self.editTitle = QLineEdit()
|
||||||
self.editTitle.setMaxLength(200)
|
self.editTitle.setMaxLength(200)
|
||||||
self.editTitle.setMaximumWidth(xW)
|
self.editTitle.setMaximumWidth(xW)
|
||||||
self.editTitle.setText(self.theProject.bookTitle)
|
self.editTitle.setText(self.theProject.data.title)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Novel title"),
|
self.tr("Novel title"),
|
||||||
self.editTitle,
|
self.editTitle,
|
||||||
@@ -229,7 +229,7 @@ class GuiProjectEditMain(QWidget):
|
|||||||
self.editAuthors = QPlainTextEdit()
|
self.editAuthors = QPlainTextEdit()
|
||||||
self.editAuthors.setMaximumHeight(xH)
|
self.editAuthors.setMaximumHeight(xH)
|
||||||
self.editAuthors.setMaximumWidth(xW)
|
self.editAuthors.setMaximumWidth(xW)
|
||||||
self.editAuthors.setPlainText("\n".join(self.theProject.bookAuthors))
|
self.editAuthors.setPlainText("\n".join(self.theProject.data.authors))
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Author(s)"),
|
self.tr("Author(s)"),
|
||||||
self.editAuthors,
|
self.editAuthors,
|
||||||
@@ -252,13 +252,13 @@ class GuiProjectEditMain(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
spellIdx = 0
|
spellIdx = 0
|
||||||
if self.theProject.projSpell is not None:
|
if self.theProject.data.spellLang is not None:
|
||||||
spellIdx = self.spellLang.findData(self.theProject.projSpell)
|
spellIdx = self.spellLang.findData(self.theProject.data.spellLang)
|
||||||
if spellIdx != -1:
|
if spellIdx != -1:
|
||||||
self.spellLang.setCurrentIndex(spellIdx)
|
self.spellLang.setCurrentIndex(spellIdx)
|
||||||
|
|
||||||
self.doBackup = QSwitch(self)
|
self.doBackup = QSwitch(self)
|
||||||
self.doBackup.setChecked(not self.theProject.doBackup)
|
self.doBackup.setChecked(not self.theProject.data.doBackup)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("No backup on close"),
|
self.tr("No backup on close"),
|
||||||
self.doBackup,
|
self.doBackup,
|
||||||
@@ -288,11 +288,11 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
self.mainTheme = projGui.mainGui.mainTheme
|
self.mainTheme = projGui.mainGui.mainTheme
|
||||||
|
|
||||||
if isStatus:
|
if isStatus:
|
||||||
self.theStatus = self.theProject.statusItems
|
self.theStatus = self.theProject.data.itemStatus
|
||||||
pageLabel = self.tr("Novel File Status Levels")
|
pageLabel = self.tr("Novel File Status Levels")
|
||||||
colSetting = "statusColW"
|
colSetting = "statusColW"
|
||||||
else:
|
else:
|
||||||
self.theStatus = self.theProject.importItems
|
self.theStatus = self.theProject.data.itemImport
|
||||||
pageLabel = self.tr("Note File Importance Levels")
|
pageLabel = self.tr("Note File Importance Levels")
|
||||||
colSetting = "importColW"
|
colSetting = "importColW"
|
||||||
|
|
||||||
@@ -578,7 +578,7 @@ class GuiProjectEditReplace(QWidget):
|
|||||||
self.listBox.setColumnWidth(self.COL_KEY, wCol0)
|
self.listBox.setColumnWidth(self.COL_KEY, wCol0)
|
||||||
self.listBox.setIndentation(0)
|
self.listBox.setIndentation(0)
|
||||||
|
|
||||||
for aKey, aVal in self.theProject.autoReplace.items():
|
for aKey, aVal in self.theProject.data.autoReplace.items():
|
||||||
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
|
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
|
||||||
self.listBox.addTopLevelItem(newItem)
|
self.listBox.addTopLevelItem(newItem)
|
||||||
|
|
||||||
|
|||||||
@@ -684,10 +684,10 @@ class GuiDocEditor(QTextEdit):
|
|||||||
"""Set the spell checker dictionary language, and emit the
|
"""Set the spell checker dictionary language, and emit the
|
||||||
dictionary changed signal.
|
dictionary changed signal.
|
||||||
"""
|
"""
|
||||||
if self.theProject.projSpell is None:
|
if self.theProject.data.spellLang is None:
|
||||||
theLang = self.mainConf.spellLanguage
|
theLang = self.mainConf.spellLanguage
|
||||||
else:
|
else:
|
||||||
theLang = self.theProject.projSpell
|
theLang = self.theProject.data.spellLang
|
||||||
|
|
||||||
self.spEnchant.setLanguage(theLang, self.theProject.projDict)
|
self.spEnchant.setLanguage(theLang, self.theProject.projDict)
|
||||||
_, theProvider = self.spEnchant.describeDict()
|
_, theProvider = self.spEnchant.describeDict()
|
||||||
@@ -721,7 +721,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
self._spellCheck = theMode
|
self._spellCheck = theMode
|
||||||
self.mainGui.mainMenu.setSpellCheck(theMode)
|
self.mainGui.mainMenu.setSpellCheck(theMode)
|
||||||
self.theProject.setSpellCheck(theMode)
|
self.theProject.data.setSpellCheck(theMode)
|
||||||
self.highLight.setSpellCheck(theMode)
|
self.highLight.setSpellCheck(theMode)
|
||||||
if not self._bigDoc:
|
if not self._bigDoc:
|
||||||
self.spellCheckDocument()
|
self.spellCheckDocument()
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
self.verticalScrollBar().setValue(sPos)
|
self.verticalScrollBar().setValue(sPos)
|
||||||
|
|
||||||
self._docHandle = tHandle
|
self._docHandle = tHandle
|
||||||
self.theProject.setLastViewed(tHandle)
|
self.theProject._data.setLastHandle(tHandle, "viewer")
|
||||||
self.docHeader.setTitleFromHandle(self._docHandle)
|
self.docHeader.setTitleFromHandle(self._docHandle)
|
||||||
self.updateDocMargins()
|
self.updateDocMargins()
|
||||||
|
|
||||||
|
|||||||
@@ -794,7 +794,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Tools > Check Spelling
|
# Tools > Check Spelling
|
||||||
self.aSpellCheck = QAction(self.tr("Check Spelling"), self)
|
self.aSpellCheck = QAction(self.tr("Check Spelling"), self)
|
||||||
self.aSpellCheck.setCheckable(True)
|
self.aSpellCheck.setCheckable(True)
|
||||||
self.aSpellCheck.setChecked(self.theProject.spellCheck)
|
self.aSpellCheck.setChecked(self.theProject.data.spellCheck)
|
||||||
self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled!
|
self.aSpellCheck.triggered.connect(self._toggleSpellCheck) # triggered, not toggled!
|
||||||
self.aSpellCheck.setShortcut("Ctrl+F7")
|
self.aSpellCheck.setShortcut("Ctrl+F7")
|
||||||
self.toolsMenu.addAction(self.aSpellCheck)
|
self.toolsMenu.addAction(self.aSpellCheck)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class GuiNovelView(QWidget):
|
|||||||
def refreshTree(self):
|
def refreshTree(self):
|
||||||
"""Refresh the current tree.
|
"""Refresh the current tree.
|
||||||
"""
|
"""
|
||||||
self.novelTree.refreshTree(rootHandle=self.theProject.lastNovel)
|
self.novelTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("novelTree"))
|
||||||
return
|
return
|
||||||
|
|
||||||
def clearProject(self):
|
def clearProject(self):
|
||||||
@@ -122,7 +122,7 @@ class GuiNovelView(QWidget):
|
|||||||
def openProjectTasks(self):
|
def openProjectTasks(self):
|
||||||
"""Run open project tasks.
|
"""Run open project tasks.
|
||||||
"""
|
"""
|
||||||
lastNovel = self.theProject.lastNovel
|
lastNovel = self.theProject.data.getLastHandle("novelTree")
|
||||||
if lastNovel not in self.theProject.tree:
|
if lastNovel not in self.theProject.tree:
|
||||||
lastNovel = self.theProject.tree.findRoot(nwItemClass.NOVEL)
|
lastNovel = self.theProject.tree.findRoot(nwItemClass.NOVEL)
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
def _refreshNovelTree(self):
|
def _refreshNovelTree(self):
|
||||||
"""Rebuild the current tree.
|
"""Rebuild the current tree.
|
||||||
"""
|
"""
|
||||||
rootHandle = self.theProject.lastNovel
|
rootHandle = self.theProject.data.getLastHandle("novelTree")
|
||||||
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
|
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -485,7 +485,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
titleKey = selItem[0].data(self.C_TITLE, self.D_KEY)
|
titleKey = selItem[0].data(self.C_TITLE, self.D_KEY)
|
||||||
|
|
||||||
self._populateTree(rootHandle)
|
self._populateTree(rootHandle)
|
||||||
self.theProject.setLastNovelViewed(rootHandle)
|
self.theProject.data.setLastHandle(rootHandle, "novelTree")
|
||||||
|
|
||||||
if titleKey is not None and titleKey in self._treeMap:
|
if titleKey is not None and titleKey in self._treeMap:
|
||||||
self._treeMap[titleKey].setSelected(True)
|
self._treeMap[titleKey].setSelected(True)
|
||||||
@@ -523,7 +523,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
self._lastCol = colType
|
self._lastCol = colType
|
||||||
self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN)
|
self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN)
|
||||||
if doRefresh:
|
if doRefresh:
|
||||||
self.refreshTree(rootHandle=self.theProject.lastNovel, overRide=True)
|
lastNovel = self.theProject.data.getLastHandle("novelTree")
|
||||||
|
self.refreshTree(rootHandle=lastNovel, overRide=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setActiveHandle(self, tHandle):
|
def setActiveHandle(self, tHandle):
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class GuiOutlineView(QWidget):
|
|||||||
def refreshTree(self):
|
def refreshTree(self):
|
||||||
"""Refresh the current tree.
|
"""Refresh the current tree.
|
||||||
"""
|
"""
|
||||||
self.outlineTree.refreshTree(rootHandle=self.theProject.lastOutline)
|
self.outlineTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("outline"))
|
||||||
return
|
return
|
||||||
|
|
||||||
def clearProject(self):
|
def clearProject(self):
|
||||||
@@ -126,7 +126,7 @@ class GuiOutlineView(QWidget):
|
|||||||
def openProjectTasks(self):
|
def openProjectTasks(self):
|
||||||
"""Run open project tasks.
|
"""Run open project tasks.
|
||||||
"""
|
"""
|
||||||
lastOutline = self.theProject.lastOutline
|
lastOutline = self.theProject.data.getLastHandle("outline")
|
||||||
if not (lastOutline in self.theProject.tree or lastOutline is None):
|
if not (lastOutline in self.theProject.tree or lastOutline is None):
|
||||||
lastOutline = self.theProject.tree.findRoot(nwItemClass.NOVEL)
|
lastOutline = self.theProject.tree.findRoot(nwItemClass.NOVEL)
|
||||||
|
|
||||||
@@ -504,7 +504,7 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._populateTree(rootHandle)
|
self._populateTree(rootHandle)
|
||||||
self.theProject.setLastOutlineViewed(rootHandle or None)
|
self.theProject.data.setLastHandle(rootHandle or None, "outline")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -1208,7 +1208,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
checkMark = f" ({nwUnicode.U_CHECK})"
|
checkMark = f" ({nwUnicode.U_CHECK})"
|
||||||
if tItem.isNovelLike():
|
if tItem.isNovelLike():
|
||||||
mStatus = ctxMenu.addMenu(self.tr("Set Status to ..."))
|
mStatus = ctxMenu.addMenu(self.tr("Set Status to ..."))
|
||||||
for n, (key, entry) in enumerate(self.theProject.statusItems.items()):
|
for n, (key, entry) in enumerate(self.theProject.data.itemStatus.items()):
|
||||||
entryName = entry["name"] + (checkMark if tItem.itemStatus == key else "")
|
entryName = entry["name"] + (checkMark if tItem.itemStatus == key else "")
|
||||||
aStatus = mStatus.addAction(entry["icon"], entryName)
|
aStatus = mStatus.addAction(entry["icon"], entryName)
|
||||||
aStatus.triggered.connect(
|
aStatus.triggered.connect(
|
||||||
@@ -1221,7 +1221,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
mImport = ctxMenu.addMenu(self.tr("Set Importance to ..."))
|
mImport = ctxMenu.addMenu(self.tr("Set Importance to ..."))
|
||||||
for n, (key, entry) in enumerate(self.theProject.importItems.items()):
|
for n, (key, entry) in enumerate(self.theProject.data.itemImport.items()):
|
||||||
entryName = entry["name"] + (checkMark if tItem.itemImport == key else "")
|
entryName = entry["name"] + (checkMark if tItem.itemImport == key else "")
|
||||||
aImport = mImport.addAction(entry["icon"], entryName)
|
aImport = mImport.addAction(entry["icon"], entryName)
|
||||||
aImport.triggered.connect(
|
aImport.triggered.connect(
|
||||||
|
|||||||
+23
-19
@@ -197,6 +197,8 @@ class GuiMain(QMainWindow):
|
|||||||
# Connect Signals
|
# Connect Signals
|
||||||
# ===============
|
# ===============
|
||||||
|
|
||||||
|
self.theProject.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus)
|
||||||
|
|
||||||
self.viewsBar.viewChangeRequested.connect(self._changeView)
|
self.viewsBar.viewChangeRequested.connect(self._changeView)
|
||||||
|
|
||||||
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||||
@@ -383,7 +385,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.mainStatus.setDocumentStatus(nwState.NONE)
|
self.mainStatus.setDocumentStatus(nwState.NONE)
|
||||||
self.mainStatus.setStatus(self.tr("New project created ..."))
|
self.mainStatus.setStatus(self.tr("New project created ..."))
|
||||||
|
|
||||||
self._updateWindowTitle(self.theProject.projName)
|
self._updateWindowTitle(self.theProject.data.name)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.theProject.clearProject()
|
self.theProject.clearProject()
|
||||||
@@ -417,7 +419,7 @@ class GuiMain(QMainWindow):
|
|||||||
if self.theProject.projAltered:
|
if self.theProject.projAltered:
|
||||||
saveOK = self.saveProject()
|
saveOK = self.saveProject()
|
||||||
doBackup = False
|
doBackup = False
|
||||||
if self.theProject.doBackup and self.mainConf.backupOnClose:
|
if self.theProject.data.doBackup and self.mainConf.backupOnClose:
|
||||||
doBackup = True
|
doBackup = True
|
||||||
if self.mainConf.askBeforeBackup:
|
if self.mainConf.askBeforeBackup:
|
||||||
msgYes = self.askQuestion(
|
msgYes = self.askQuestion(
|
||||||
@@ -521,10 +523,10 @@ class GuiMain(QMainWindow):
|
|||||||
self.theProject.index.loadIndex()
|
self.theProject.index.loadIndex()
|
||||||
|
|
||||||
# Update GUI
|
# Update GUI
|
||||||
self._updateWindowTitle(self.theProject.projName)
|
self._updateWindowTitle(self.theProject.data.name)
|
||||||
self.rebuildTrees()
|
self.rebuildTrees()
|
||||||
self.docEditor.setDictionaries()
|
self.docEditor.setDictionaries()
|
||||||
self.docEditor.toggleSpellCheck(self.theProject.spellCheck)
|
self.docEditor.toggleSpellCheck(self.theProject.data.spellCheck)
|
||||||
self.mainStatus.setRefTime(self.theProject.projOpened)
|
self.mainStatus.setRefTime(self.theProject.projOpened)
|
||||||
self.projView.openProjectTasks()
|
self.projView.openProjectTasks()
|
||||||
self.novelView.openProjectTasks()
|
self.novelView.openProjectTasks()
|
||||||
@@ -532,11 +534,13 @@ class GuiMain(QMainWindow):
|
|||||||
self._updateStatusWordCount()
|
self._updateStatusWordCount()
|
||||||
|
|
||||||
# Restore previously open documents, if any
|
# Restore previously open documents, if any
|
||||||
if self.theProject.lastEdited is not None:
|
lastEdited = self.theProject.data.getLastHandle("editor")
|
||||||
self.openDocument(self.theProject.lastEdited, doScroll=True)
|
if lastEdited is not None:
|
||||||
|
self.openDocument(lastEdited, doScroll=True)
|
||||||
|
|
||||||
if self.theProject.lastViewed is not None:
|
lastViewed = self.theProject.data.getLastHandle("viewer")
|
||||||
self.viewDocument(self.theProject.lastViewed)
|
if lastViewed is not None:
|
||||||
|
self.viewDocument(lastViewed)
|
||||||
|
|
||||||
# Check if we need to rebuild the index
|
# Check if we need to rebuild the index
|
||||||
if self.theProject.index.indexBroken:
|
if self.theProject.index.indexBroken:
|
||||||
@@ -607,7 +611,7 @@ class GuiMain(QMainWindow):
|
|||||||
if self.docEditor.loadText(tHandle, tLine):
|
if self.docEditor.loadText(tHandle, tLine):
|
||||||
if changeFocus:
|
if changeFocus:
|
||||||
self.docEditor.setFocus()
|
self.docEditor.setFocus()
|
||||||
self.theProject.setLastEdited(tHandle)
|
self.theProject.data.setLastHandle(tHandle, "editor")
|
||||||
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
||||||
self.novelView.setActiveHandle(tHandle)
|
self.novelView.setActiveHandle(tHandle)
|
||||||
else:
|
else:
|
||||||
@@ -676,7 +680,7 @@ class GuiMain(QMainWindow):
|
|||||||
tHandle = self.projView.getSelectedHandle()
|
tHandle = self.projView.getSelectedHandle()
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
tHandle = self.theProject.lastViewed
|
tHandle = self.theProject.data.getLastHandle("viewer")
|
||||||
|
|
||||||
if tHandle is None:
|
if tHandle is None:
|
||||||
logger.debug("No document to view, giving up")
|
logger.debug("No document to view, giving up")
|
||||||
@@ -960,7 +964,7 @@ class GuiMain(QMainWindow):
|
|||||||
if dlgProj.spellChanged:
|
if dlgProj.spellChanged:
|
||||||
self.docEditor.setDictionaries()
|
self.docEditor.setDictionaries()
|
||||||
self.itemDetails.refreshDetails()
|
self.itemDetails.refreshDetails()
|
||||||
self._updateWindowTitle(self.theProject.projName)
|
self._updateWindowTitle(self.theProject.data.name)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1223,14 +1227,14 @@ class GuiMain(QMainWindow):
|
|||||||
"""Close the document edit panel. This does not hide the editor.
|
"""Close the document edit panel. This does not hide the editor.
|
||||||
"""
|
"""
|
||||||
self.closeDocument()
|
self.closeDocument()
|
||||||
self.theProject.setLastEdited(None)
|
self.theProject.data.setLastHandle(None, "editor")
|
||||||
return
|
return
|
||||||
|
|
||||||
def closeDocViewer(self):
|
def closeDocViewer(self):
|
||||||
"""Close the document view panel.
|
"""Close the document view panel.
|
||||||
"""
|
"""
|
||||||
self.docViewer.clearViewer()
|
self.docViewer.clearViewer()
|
||||||
self.theProject.setLastViewed(None)
|
self.theProject.data.setLastHandle(None, "viewer")
|
||||||
bPos = self.splitMain.sizes()
|
bPos = self.splitMain.sizes()
|
||||||
self.splitView.setVisible(False)
|
self.splitView.setVisible(False)
|
||||||
self.splitDocs.setSizes([bPos[1], 0])
|
self.splitDocs.setSizes([bPos[1], 0])
|
||||||
@@ -1555,13 +1559,13 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
self.theProject.updateWordCounts()
|
self.theProject.updateWordCounts()
|
||||||
if self.mainConf.incNotesWCount:
|
if self.mainConf.incNotesWCount:
|
||||||
currWords = self.theProject.currWCount
|
iTotal = sum(self.theProject.data.initCounts)
|
||||||
diffWords = currWords - self.theProject.lastWCount
|
cTotal = sum(self.theProject.data.currCounts)
|
||||||
|
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
||||||
else:
|
else:
|
||||||
currWords = self.theProject.currNovelWC
|
iNovel, _ = self.theProject.data.initCounts
|
||||||
diffWords = currWords - self.theProject.lastNovelWC
|
cNovel, _ = self.theProject.data.currCounts
|
||||||
|
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
||||||
self.mainStatus.setProjectStats(currWords, diffWords)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -126,7 +126,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.fmtTitle.setMinimumWidth(xFmt)
|
self.fmtTitle.setMinimumWidth(xFmt)
|
||||||
self.fmtTitle.setToolTip(fmtHelp)
|
self.fmtTitle.setToolTip(fmtHelp)
|
||||||
self.fmtTitle.setText(
|
self.fmtTitle.setText(
|
||||||
self._reFmtCodes(self.theProject.titleFormat["title"])
|
self._reFmtCodes(self.theProject.data.getTitleFormat("title"))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.fmtChapter = QLineEdit()
|
self.fmtChapter = QLineEdit()
|
||||||
@@ -134,7 +134,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.fmtChapter.setMinimumWidth(xFmt)
|
self.fmtChapter.setMinimumWidth(xFmt)
|
||||||
self.fmtChapter.setToolTip(fmtHelp)
|
self.fmtChapter.setToolTip(fmtHelp)
|
||||||
self.fmtChapter.setText(
|
self.fmtChapter.setText(
|
||||||
self._reFmtCodes(self.theProject.titleFormat["chapter"])
|
self._reFmtCodes(self.theProject.data.getTitleFormat("chapter"))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.fmtUnnumbered = QLineEdit()
|
self.fmtUnnumbered = QLineEdit()
|
||||||
@@ -142,7 +142,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.fmtUnnumbered.setMinimumWidth(xFmt)
|
self.fmtUnnumbered.setMinimumWidth(xFmt)
|
||||||
self.fmtUnnumbered.setToolTip(fmtHelp)
|
self.fmtUnnumbered.setToolTip(fmtHelp)
|
||||||
self.fmtUnnumbered.setText(
|
self.fmtUnnumbered.setText(
|
||||||
self._reFmtCodes(self.theProject.titleFormat["unnumbered"])
|
self._reFmtCodes(self.theProject.data.getTitleFormat("unnumbered"))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.fmtScene = QLineEdit()
|
self.fmtScene = QLineEdit()
|
||||||
@@ -150,7 +150,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.fmtScene.setMinimumWidth(xFmt)
|
self.fmtScene.setMinimumWidth(xFmt)
|
||||||
self.fmtScene.setToolTip(fmtHelp + fmtScHelp)
|
self.fmtScene.setToolTip(fmtHelp + fmtScHelp)
|
||||||
self.fmtScene.setText(
|
self.fmtScene.setText(
|
||||||
self._reFmtCodes(self.theProject.titleFormat["scene"])
|
self._reFmtCodes(self.theProject.data.getTitleFormat("scene"))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.fmtSection = QLineEdit()
|
self.fmtSection = QLineEdit()
|
||||||
@@ -158,7 +158,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.fmtSection.setMinimumWidth(xFmt)
|
self.fmtSection.setMinimumWidth(xFmt)
|
||||||
self.fmtSection.setToolTip(fmtHelp + fmtScHelp)
|
self.fmtSection.setToolTip(fmtHelp + fmtScHelp)
|
||||||
self.fmtSection.setText(
|
self.fmtSection.setText(
|
||||||
self._reFmtCodes(self.theProject.titleFormat["section"])
|
self._reFmtCodes(self.theProject.data.getTitleFormat("section"))
|
||||||
)
|
)
|
||||||
|
|
||||||
self.buildLang = QComboBox()
|
self.buildLang = QComboBox()
|
||||||
@@ -168,7 +168,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
for langID, langName in theLangs:
|
for langID, langName in theLangs:
|
||||||
self.buildLang.addItem(langName, langID)
|
self.buildLang.addItem(langName, langID)
|
||||||
|
|
||||||
langIdx = self.buildLang.findData(self.theProject.projLang)
|
langIdx = self.buildLang.findData(self.theProject.data.language)
|
||||||
if langIdx != -1:
|
if langIdx != -1:
|
||||||
self.buildLang.setCurrentIndex(langIdx)
|
self.buildLang.setCurrentIndex(langIdx)
|
||||||
|
|
||||||
@@ -888,7 +888,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
# Generate File Name
|
# Generate File Name
|
||||||
# ==================
|
# ==================
|
||||||
|
|
||||||
cleanName = makeFileNameSafe(self.theProject.projName)
|
cleanName = makeFileNameSafe(self.theProject.data.name)
|
||||||
fileName = "%s.%s" % (cleanName, fileExt)
|
fileName = "%s.%s" % (cleanName, fileExt)
|
||||||
saveDir = self.mainConf.lastPath
|
saveDir = self.mainConf.lastPath
|
||||||
if not os.path.isdir(saveDir):
|
if not os.path.isdir(saveDir):
|
||||||
@@ -972,9 +972,9 @@ class GuiBuildNovel(QDialog):
|
|||||||
elif theFmt == self.FMT_JSON_H or theFmt == self.FMT_JSON_M:
|
elif theFmt == self.FMT_JSON_H or theFmt == self.FMT_JSON_M:
|
||||||
jsonData = {
|
jsonData = {
|
||||||
"meta": {
|
"meta": {
|
||||||
"workingTitle": self.theProject.projName,
|
"workingTitle": self.theProject.data.name,
|
||||||
"novelTitle": self.theProject.bookTitle,
|
"novelTitle": self.theProject.data.title,
|
||||||
"authors": self.theProject.bookAuthors,
|
"authors": self.theProject.data.authors,
|
||||||
"buildTime": self.buildTime,
|
"buildTime": self.buildTime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1159,7 +1159,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
logger.debug("Saving GuiBuildNovel settings")
|
logger.debug("Saving GuiBuildNovel settings")
|
||||||
|
|
||||||
# Formatting
|
# Formatting
|
||||||
self.theProject.setTitleFormat({
|
self.theProject.data.setTitleFormat({
|
||||||
"title": self.fmtTitle.text().strip(),
|
"title": self.fmtTitle.text().strip(),
|
||||||
"chapter": self.fmtChapter.text().strip(),
|
"chapter": self.fmtChapter.text().strip(),
|
||||||
"unnumbered": self.fmtUnnumbered.text().strip(),
|
"unnumbered": self.fmtUnnumbered.text().strip(),
|
||||||
|
|||||||
+33
-32
@@ -1,37 +1,38 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-25 18:45:45">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 12:20:53">
|
||||||
<project>
|
<project>
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
<saveCount>1386</saveCount>
|
<saveCount>1409</saveCount>
|
||||||
<autoCount>236</autoCount>
|
<autoCount>236</autoCount>
|
||||||
<editTime>69352</editTime>
|
<editTime>69427</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>False</doBackup>
|
<doBackup>False</doBackup>
|
||||||
<language>en_GB</language>
|
<language>en_GB</language>
|
||||||
<spellCheck>True</spellCheck>
|
<spellCheck>True</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>636b6aa9b697b</lastEdited>
|
|
||||||
<lastViewed>636b6aa9b697b</lastViewed>
|
|
||||||
<lastNovel>7031beac91f75</lastNovel>
|
|
||||||
<lastOutline>7031beac91f75</lastOutline>
|
|
||||||
<lastWordCount>1363</lastWordCount>
|
|
||||||
<novelWordCount>954</novelWordCount>
|
<novelWordCount>954</novelWordCount>
|
||||||
<notesWordCount>409</notesWordCount>
|
<notesWordCount>409</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">636b6aa9b697b</entry>
|
||||||
|
<entry key="viewer">636b6aa9b697b</entry>
|
||||||
|
<entry key="novelTree">7031beac91f75</entry>
|
||||||
|
<entry key="outline">7031beac91f75</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace>
|
<autoReplace>
|
||||||
<entry key="A">B</entry>
|
<entry key="A">B</entry>
|
||||||
<entry key="B">E</entry>
|
<entry key="B">E</entry>
|
||||||
<entry key="C">D</entry>
|
<entry key="C">D</entry>
|
||||||
</autoReplace>
|
</autoReplace>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>Chapter %chw%: %title%</chapter>
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>Scene %ch%.%sc%: %title%</scene>
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
@@ -55,43 +56,43 @@
|
|||||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
<meta expanded="False" heading="H1" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
||||||
<name status="sc24b8f" import="ia857f0" active="True">Title Page</name>
|
<name status="sc24b8f" import="ia857f0" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
<meta expanded="False" heading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
||||||
<name status="sf12341" import="ia857f0" active="True">Page</name>
|
<name status="sf12341" import="ia857f0" active="True">Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
<meta expanded="False" heading="H1" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
<name status="s90e6c9" import="ia857f0" active="True">Part One</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Part One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="True" mainHeading="H2" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
<meta expanded="True" heading="H2" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
||||||
<name status="sf24ce6" import="ia857f0" active="True">Chapter One</name>
|
<name status="sf24ce6" import="ia857f0" active="True">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
<meta expanded="False" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
||||||
<name status="s90e6c9" import="ia857f0" active="True">Making a Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Making a Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
<meta expanded="False" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
||||||
<name status="s90e6c9" import="ia857f0" active="True">Another Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Another Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
<meta expanded="False" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
||||||
<name status="s78ea90" import="ia857f0" active="True">Interlude</name>
|
<name status="s78ea90" import="ia857f0" active="True">Interlude</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
<meta expanded="False" heading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
||||||
<name status="sf24ce6" import="ia857f0" active="False">A Note on Structure</name>
|
<name status="sf24ce6" import="ia857f0" active="False">A Note on Structure</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="True" mainHeading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
<meta expanded="True" heading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
||||||
<name status="s90e6c9" import="ia857f0" active="True">Chapter Two</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Chapter Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
<meta expanded="False" heading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s90e6c9" import="ia857f0" active="True">We Found John!</name>
|
<name status="s90e6c9" import="ia857f0" active="True">We Found John!</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||||
@@ -99,11 +100,11 @@
|
|||||||
<name status="sf12341" import="ia857f0">Sequel</name>
|
<name status="sf12341" import="ia857f0">Sequel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
<meta expanded="False" heading="H1" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
||||||
<name status="sc24b8f" import="ia857f0" active="True">Title Page</name>
|
<name status="sc24b8f" import="ia857f0" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
<meta expanded="False" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
||||||
<name status="s90e6c9" import="ia857f0" active="True">Chapter One</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||||
@@ -115,11 +116,11 @@
|
|||||||
<name status="sf12341" import="ia857f0">Main Characters</name>
|
<name status="sf12341" import="ia857f0">Main Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
<meta expanded="False" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||||
<name status="sf12341" import="icfb3a5" active="True">John Smith</name>
|
<name status="sf12341" import="icfb3a5" active="True">John Smith</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
<meta expanded="False" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||||
<name status="sf12341" import="i2d7a54" active="True">Jane Smith</name>
|
<name status="sf12341" import="i2d7a54" active="True">Jane Smith</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||||
@@ -127,15 +128,15 @@
|
|||||||
<name status="sf12341" import="ia857f0">Locations</name>
|
<name status="sf12341" import="ia857f0">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
<meta expanded="False" heading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||||
<name status="sf12341" import="i56be10" active="True">Earth</name>
|
<name status="sf12341" import="i56be10" active="True">Earth</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
<meta expanded="False" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||||
<name status="sf12341" import="icfb3a5" active="True">Space</name>
|
<name status="sf12341" import="icfb3a5" active="True">Space</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
<meta expanded="False" heading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||||
<name status="sf12341" import="i2d7a54" active="True">Mars</name>
|
<name status="sf12341" import="i2d7a54" active="True">Mars</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||||
@@ -147,7 +148,7 @@
|
|||||||
<name status="sf12341" import="ia857f0">Scenes</name>
|
<name status="sf12341" import="ia857f0">Scenes</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="8a5deb88c0e97" parent="ae9bf3c3ea159" root="6827118336ac1" order="0" type="FILE" class="ARCHIVE" layout="DOCUMENT">
|
<item handle="8a5deb88c0e97" parent="ae9bf3c3ea159" root="6827118336ac1" order="0" type="FILE" class="ARCHIVE" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
<meta expanded="False" heading="H3" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
||||||
<name status="s90e6c9" import="ia857f0" active="True">Old File</name>
|
<name status="s90e6c9" import="ia857f0" active="True">Old File</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
||||||
@@ -155,7 +156,7 @@
|
|||||||
<name status="sf12341" import="ia857f0">Trash</name>
|
<name status="sf12341" import="ia857f0">Trash</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="98acd8c76c93a" order="0" type="FILE" class="TRASH" layout="DOCUMENT">
|
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="98acd8c76c93a" order="0" type="FILE" class="TRASH" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
<meta expanded="False" heading="H3" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
<name status="sf12341" import="ia857f0" active="True">Delete Me!</name>
|
<name status="sf12341" import="ia857f0" active="True">Delete Me!</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
|
|||||||
@@ -214,27 +214,6 @@ def mockRnd(monkeypatch):
|
|||||||
# Temp Project Folders
|
# Temp Project Folders
|
||||||
##
|
##
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
|
||||||
def nwMinimal(tmpDir):
|
|
||||||
"""A minimal novelWriter example project.
|
|
||||||
"""
|
|
||||||
tstDir = os.path.dirname(__file__)
|
|
||||||
srcDir = os.path.join(tstDir, "minimal")
|
|
||||||
dstDir = os.path.join(tmpDir, "minimal")
|
|
||||||
if os.path.isdir(dstDir):
|
|
||||||
shutil.rmtree(dstDir)
|
|
||||||
|
|
||||||
shutil.copytree(srcDir, dstDir)
|
|
||||||
cleanProject(dstDir)
|
|
||||||
|
|
||||||
yield dstDir
|
|
||||||
|
|
||||||
if os.path.isdir(dstDir):
|
|
||||||
shutil.rmtree(dstDir)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def nwLipsum(tmpDir):
|
def nwLipsum(tmpDir):
|
||||||
"""A medium sized novelWriter example project with a lot of Lorem
|
"""A medium sized novelWriter example project with a lot of Lorem
|
||||||
|
|||||||
@@ -0,0 +1,299 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="0.6.1" hexVersion="0x000601f0" fileVersion="1.0" saveCount="186" autoCount="28" timeStamp="2020-05-28 09:59:15">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<backup>False</backup>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<autoOutline>True</autoOutline>
|
||||||
|
<lastEdited>636b6aa9b697b</lastEdited>
|
||||||
|
<lastViewed>636b6aa9b697b</lastViewed>
|
||||||
|
<lastWordCount>914</lastWordCount>
|
||||||
|
<autoReplace>
|
||||||
|
<A>B</A>
|
||||||
|
<B>E</B>
|
||||||
|
<C>D</C>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<title>%title%</title>
|
||||||
|
<chapter>Chapter %ch%: %title%</chapter>
|
||||||
|
<unnumbered>%title%</unnumbered>
|
||||||
|
<scene>Scene %ch%.%sc%: %title%</scene>
|
||||||
|
<section></section>
|
||||||
|
<withSynopsis>True</withSynopsis>
|
||||||
|
<withComments>True</withComments>
|
||||||
|
<withKeywords>False</withKeywords>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry blue="100" green="100" red="100">New</entry>
|
||||||
|
<entry blue="0" green="50" red="200">Notes</entry>
|
||||||
|
<entry blue="0" green="60" red="182">Started</entry>
|
||||||
|
<entry blue="0" green="129" red="193">1st Draft</entry>
|
||||||
|
<entry blue="0" green="129" red="193">2nd Draft</entry>
|
||||||
|
<entry blue="0" green="129" red="193">3rd Draft</entry>
|
||||||
|
<entry blue="58" green="180" red="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry blue="100" green="100" red="100">None</entry>
|
||||||
|
<entry blue="188" green="122" red="0">Minor</entry>
|
||||||
|
<entry blue="180" green="0" red="21">Major</entry>
|
||||||
|
<entry blue="175" green="0" red="117">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="22">
|
||||||
|
<item handle="7031beac91f75" order="0" parent="None">
|
||||||
|
<name>Novel</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Started</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" order="0" parent="7031beac91f75">
|
||||||
|
<name>Title Page</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Started</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>TITLE</layout>
|
||||||
|
<charCount>72</charCount>
|
||||||
|
<wordCount>15</wordCount>
|
||||||
|
<paraCount>2</paraCount>
|
||||||
|
<cursorPos>78</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" order="1" parent="7031beac91f75">
|
||||||
|
<name>Page</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>PAGE</layout>
|
||||||
|
<charCount>208</charCount>
|
||||||
|
<wordCount>40</wordCount>
|
||||||
|
<paraCount>2</paraCount>
|
||||||
|
<cursorPos>213</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" order="2" parent="7031beac91f75">
|
||||||
|
<name>Part One</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>PARTITION</layout>
|
||||||
|
<charCount>23</charCount>
|
||||||
|
<wordCount>5</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>0</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" order="3" parent="7031beac91f75">
|
||||||
|
<name>A Folder</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" order="0" parent="e7ded148d6e4a">
|
||||||
|
<name>Chapter One</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Notes</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>CHAPTER</layout>
|
||||||
|
<charCount>12</charCount>
|
||||||
|
<wordCount>3</wordCount>
|
||||||
|
<paraCount>0</paraCount>
|
||||||
|
<cursorPos>215</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" order="1" parent="e7ded148d6e4a">
|
||||||
|
<name>Making a Scene</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>1199</charCount>
|
||||||
|
<wordCount>216</wordCount>
|
||||||
|
<paraCount>7</paraCount>
|
||||||
|
<cursorPos>527</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
|
||||||
|
<name>Another Scene</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>476</charCount>
|
||||||
|
<wordCount>93</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>551</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" order="3" parent="e7ded148d6e4a">
|
||||||
|
<name>Interlude</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Finished</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>UNNUMBERED</layout>
|
||||||
|
<charCount>633</charCount>
|
||||||
|
<wordCount>101</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>1238</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" order="4" parent="e7ded148d6e4a">
|
||||||
|
<name>A Note on Structure</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>2nd Draft</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>False</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>1692</charCount>
|
||||||
|
<wordCount>313</wordCount>
|
||||||
|
<paraCount>6</paraCount>
|
||||||
|
<cursorPos>1721</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" order="5" parent="e7ded148d6e4a">
|
||||||
|
<name>Chapter Two</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>CHAPTER</layout>
|
||||||
|
<charCount>139</charCount>
|
||||||
|
<wordCount>28</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>343</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" order="6" parent="e7ded148d6e4a">
|
||||||
|
<name>We Found John!</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>189</charCount>
|
||||||
|
<wordCount>37</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>224</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" order="1" parent="None">
|
||||||
|
<name>Characters</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" order="0" parent="f6622b4617424">
|
||||||
|
<name>Main Characters</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" order="0" parent="f7e2d9f330615">
|
||||||
|
<name>John Smith</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>Minor</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>49</charCount>
|
||||||
|
<wordCount>9</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>24</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" order="1" parent="f7e2d9f330615">
|
||||||
|
<name>Jane Smith</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>Major</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>55</charCount>
|
||||||
|
<wordCount>9</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>25</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" order="2" parent="None">
|
||||||
|
<name>Locations</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" order="0" parent="15c4492bd5107">
|
||||||
|
<name>Earth</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Main</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>76</charCount>
|
||||||
|
<wordCount>15</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>20</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" order="1" parent="15c4492bd5107">
|
||||||
|
<name>Space</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Minor</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>115</charCount>
|
||||||
|
<wordCount>24</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>133</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" order="2" parent="15c4492bd5107">
|
||||||
|
<name>Mars</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Major</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>28</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>45</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" order="3" parent="None">
|
||||||
|
<name>Trash</name>
|
||||||
|
<type>TRASH</type>
|
||||||
|
<class>TRASH</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" order="0" parent="98acd8c76c93a">
|
||||||
|
<name>Delete Me!</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<expanded>False</expanded>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>0</charCount>
|
||||||
|
<wordCount>0</wordCount>
|
||||||
|
<paraCount>0</paraCount>
|
||||||
|
<cursorPos>36</cursorPos>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="0.9.2" hexVersion="0x000902f0" fileVersion="1.1" timeStamp="2020-06-26 21:20:24">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<saveCount>5</saveCount>
|
||||||
|
<autoCount>10</autoCount>
|
||||||
|
<editTime>1000</editTime>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<doBackup>True</doBackup>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<autoOutline>True</autoOutline>
|
||||||
|
<lastEdited>636b6aa9b697b</lastEdited>
|
||||||
|
<lastViewed>bb2c23b3c42cc</lastViewed>
|
||||||
|
<lastWordCount>967</lastWordCount>
|
||||||
|
<autoReplace>
|
||||||
|
<A>B</A>
|
||||||
|
<B>E</B>
|
||||||
|
<C>D</C>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<title>%title%</title>
|
||||||
|
<chapter>Chapter %ch%: %title%</chapter>
|
||||||
|
<unnumbered>%title%</unnumbered>
|
||||||
|
<scene>Scene %ch%.%sc%: %title%</scene>
|
||||||
|
<section></section>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry blue="100" green="100" red="100">New</entry>
|
||||||
|
<entry blue="0" green="50" red="200">Notes</entry>
|
||||||
|
<entry blue="0" green="60" red="182">Started</entry>
|
||||||
|
<entry blue="0" green="129" red="193">1st Draft</entry>
|
||||||
|
<entry blue="0" green="129" red="193">2nd Draft</entry>
|
||||||
|
<entry blue="0" green="129" red="193">3rd Draft</entry>
|
||||||
|
<entry blue="58" green="180" red="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry blue="100" green="100" red="100">None</entry>
|
||||||
|
<entry blue="188" green="122" red="0">Minor</entry>
|
||||||
|
<entry blue="180" green="0" red="21">Major</entry>
|
||||||
|
<entry blue="175" green="0" red="117">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="22">
|
||||||
|
<item handle="7031beac91f75" order="0" parent="None">
|
||||||
|
<name>Novel</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Started</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" order="0" parent="7031beac91f75">
|
||||||
|
<name>Title Page</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Started</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>TITLE</layout>
|
||||||
|
<charCount>72</charCount>
|
||||||
|
<wordCount>15</wordCount>
|
||||||
|
<paraCount>2</paraCount>
|
||||||
|
<cursorPos>78</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" order="1" parent="7031beac91f75">
|
||||||
|
<name>Page</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>PAGE</layout>
|
||||||
|
<charCount>210</charCount>
|
||||||
|
<wordCount>40</wordCount>
|
||||||
|
<paraCount>2</paraCount>
|
||||||
|
<cursorPos>213</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" order="2" parent="7031beac91f75">
|
||||||
|
<name>Part One</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>PARTITION</layout>
|
||||||
|
<charCount>23</charCount>
|
||||||
|
<wordCount>5</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>0</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" order="3" parent="7031beac91f75">
|
||||||
|
<name>A Folder</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" order="0" parent="e7ded148d6e4a">
|
||||||
|
<name>Chapter One</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Notes</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>CHAPTER</layout>
|
||||||
|
<charCount>12</charCount>
|
||||||
|
<wordCount>3</wordCount>
|
||||||
|
<paraCount>0</paraCount>
|
||||||
|
<cursorPos>215</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" order="1" parent="e7ded148d6e4a">
|
||||||
|
<name>Making a Scene</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>1483</charCount>
|
||||||
|
<wordCount>263</wordCount>
|
||||||
|
<paraCount>8</paraCount>
|
||||||
|
<cursorPos>1086</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
|
||||||
|
<name>Another Scene</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>476</charCount>
|
||||||
|
<wordCount>93</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>428</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" order="3" parent="e7ded148d6e4a">
|
||||||
|
<name>Interlude</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Finished</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>UNNUMBERED</layout>
|
||||||
|
<charCount>633</charCount>
|
||||||
|
<wordCount>101</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>1238</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" order="4" parent="e7ded148d6e4a">
|
||||||
|
<name>A Note on Structure</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>2nd Draft</status>
|
||||||
|
<exported>False</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>1692</charCount>
|
||||||
|
<wordCount>313</wordCount>
|
||||||
|
<paraCount>6</paraCount>
|
||||||
|
<cursorPos>1721</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" order="5" parent="e7ded148d6e4a">
|
||||||
|
<name>Chapter Two</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>CHAPTER</layout>
|
||||||
|
<charCount>139</charCount>
|
||||||
|
<wordCount>28</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>343</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" order="6" parent="e7ded148d6e4a">
|
||||||
|
<name>We Found John!</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>189</charCount>
|
||||||
|
<wordCount>37</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>224</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" order="1" parent="None">
|
||||||
|
<name>Characters</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" order="0" parent="f6622b4617424">
|
||||||
|
<name>Main Characters</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" order="0" parent="f7e2d9f330615">
|
||||||
|
<name>John Smith</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>Minor</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>49</charCount>
|
||||||
|
<wordCount>9</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>24</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" order="1" parent="f7e2d9f330615">
|
||||||
|
<name>Jane Smith</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>Major</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>55</charCount>
|
||||||
|
<wordCount>9</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>25</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" order="2" parent="None">
|
||||||
|
<name>Locations</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" order="0" parent="15c4492bd5107">
|
||||||
|
<name>Earth</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Main</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>76</charCount>
|
||||||
|
<wordCount>15</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>20</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" order="1" parent="15c4492bd5107">
|
||||||
|
<name>Space</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Minor</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>115</charCount>
|
||||||
|
<wordCount>24</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>133</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" order="2" parent="15c4492bd5107">
|
||||||
|
<name>Mars</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Major</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>28</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>45</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" order="3" parent="None">
|
||||||
|
<name>Trash</name>
|
||||||
|
<type>TRASH</type>
|
||||||
|
<class>TRASH</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" order="0" parent="98acd8c76c93a">
|
||||||
|
<name>Delete Me!</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>30</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>36</cursorPos>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="1.4.2" hexVersion="0x010402f0" fileVersion="1.2" timeStamp="2021-08-30 23:33:44">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<saveCount>5</saveCount>
|
||||||
|
<autoCount>10</autoCount>
|
||||||
|
<editTime>1000</editTime>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<doBackup>True</doBackup>
|
||||||
|
<language>en_GB</language>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<spellLang>en_GB</spellLang>
|
||||||
|
<autoOutline>True</autoOutline>
|
||||||
|
<lastEdited>636b6aa9b697b</lastEdited>
|
||||||
|
<lastViewed>636b6aa9b697b</lastViewed>
|
||||||
|
<lastWordCount>1216</lastWordCount>
|
||||||
|
<novelWordCount>840</novelWordCount>
|
||||||
|
<notesWordCount>376</notesWordCount>
|
||||||
|
<autoReplace>
|
||||||
|
<entry key="A">B</entry>
|
||||||
|
<entry key="B">E</entry>
|
||||||
|
<entry key="C">D</entry>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<title>%title%</title>
|
||||||
|
<chapter>Chapter %chw%: %title%</chapter>
|
||||||
|
<unnumbered>%title%</unnumbered>
|
||||||
|
<scene>Scene %ch%.%sc%: %title%</scene>
|
||||||
|
<section></section>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry blue="100" green="100" red="100">New</entry>
|
||||||
|
<entry blue="0" green="50" red="200">Notes</entry>
|
||||||
|
<entry blue="0" green="60" red="182">Started</entry>
|
||||||
|
<entry blue="0" green="129" red="193">1st Draft</entry>
|
||||||
|
<entry blue="0" green="129" red="193">2nd Draft</entry>
|
||||||
|
<entry blue="0" green="129" red="193">3rd Draft</entry>
|
||||||
|
<entry blue="58" green="180" red="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry blue="100" green="100" red="100">None</entry>
|
||||||
|
<entry blue="188" green="122" red="0">Minor</entry>
|
||||||
|
<entry blue="180" green="0" red="21">Major</entry>
|
||||||
|
<entry blue="175" green="0" red="117">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="25">
|
||||||
|
<item handle="7031beac91f75" order="0" parent="None">
|
||||||
|
<name>Novel</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Started</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" order="0" parent="7031beac91f75">
|
||||||
|
<name>Title Page</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Started</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>TITLE</layout>
|
||||||
|
<charCount>241</charCount>
|
||||||
|
<wordCount>42</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>252</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" order="1" parent="7031beac91f75">
|
||||||
|
<name>Page</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>PAGE</layout>
|
||||||
|
<charCount>125</charCount>
|
||||||
|
<wordCount>26</wordCount>
|
||||||
|
<paraCount>2</paraCount>
|
||||||
|
<cursorPos>127</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" order="2" parent="7031beac91f75">
|
||||||
|
<name>Part One</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>PARTITION</layout>
|
||||||
|
<charCount>26</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>30</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" order="3" parent="7031beac91f75">
|
||||||
|
<name>A Folder</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" order="0" parent="e7ded148d6e4a">
|
||||||
|
<name>Chapter One</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Notes</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>CHAPTER</layout>
|
||||||
|
<charCount>75</charCount>
|
||||||
|
<wordCount>14</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>279</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" order="1" parent="e7ded148d6e4a">
|
||||||
|
<name>Making a Scene</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>2429</charCount>
|
||||||
|
<wordCount>432</wordCount>
|
||||||
|
<paraCount>14</paraCount>
|
||||||
|
<cursorPos>61</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
|
||||||
|
<name>Another Scene</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>476</charCount>
|
||||||
|
<wordCount>93</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>577</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" order="3" parent="e7ded148d6e4a">
|
||||||
|
<name>Interlude</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>UNNUMBERED</layout>
|
||||||
|
<charCount>617</charCount>
|
||||||
|
<wordCount>101</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>1137</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" order="4" parent="e7ded148d6e4a">
|
||||||
|
<name>A Note on Structure</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>2nd Draft</status>
|
||||||
|
<exported>False</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>1692</charCount>
|
||||||
|
<wordCount>313</wordCount>
|
||||||
|
<paraCount>6</paraCount>
|
||||||
|
<cursorPos>1110</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" order="5" parent="e7ded148d6e4a">
|
||||||
|
<name>Chapter Two</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>CHAPTER</layout>
|
||||||
|
<charCount>139</charCount>
|
||||||
|
<wordCount>28</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>343</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" order="6" parent="e7ded148d6e4a">
|
||||||
|
<name>We Found John!</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>189</charCount>
|
||||||
|
<wordCount>37</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>224</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" order="1" parent="None">
|
||||||
|
<name>Characters</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" order="0" parent="f6622b4617424">
|
||||||
|
<name>Main Characters</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" order="0" parent="f7e2d9f330615">
|
||||||
|
<name>John Smith</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>Minor</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>49</charCount>
|
||||||
|
<wordCount>9</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>24</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" order="1" parent="f7e2d9f330615">
|
||||||
|
<name>Jane Smith</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>Major</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>55</charCount>
|
||||||
|
<wordCount>9</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>25</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" order="2" parent="None">
|
||||||
|
<name>Locations</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" order="0" parent="15c4492bd5107">
|
||||||
|
<name>Earth</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Main</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>76</charCount>
|
||||||
|
<wordCount>15</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>20</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" order="1" parent="15c4492bd5107">
|
||||||
|
<name>Space</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Minor</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>115</charCount>
|
||||||
|
<wordCount>24</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>133</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" order="2" parent="15c4492bd5107">
|
||||||
|
<name>Mars</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Major</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>28</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>45</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="6827118336ac1" order="3" parent="None">
|
||||||
|
<name>Outtakes</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>ARCHIVE</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="ae9bf3c3ea159" order="0" parent="6827118336ac1">
|
||||||
|
<name>Scenes</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>ARCHIVE</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="8a5deb88c0e97" order="0" parent="ae9bf3c3ea159">
|
||||||
|
<name>Old File</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>315</charCount>
|
||||||
|
<wordCount>55</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>322</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" order="4" parent="None">
|
||||||
|
<name>Trash</name>
|
||||||
|
<type>TRASH</type>
|
||||||
|
<class>TRASH</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" order="0" parent="98acd8c76c93a">
|
||||||
|
<name>Delete Me!</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>SCENE</layout>
|
||||||
|
<charCount>30</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>36</cursorPos>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="1.6.6" hexVersion="0x010606f0" fileVersion="1.3" timeStamp="2022-10-25 18:26:15">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<saveCount>5</saveCount>
|
||||||
|
<autoCount>10</autoCount>
|
||||||
|
<editTime>1000</editTime>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<doBackup>True</doBackup>
|
||||||
|
<language>en_GB</language>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<spellLang>en_GB</spellLang>
|
||||||
|
<autoOutline>True</autoOutline>
|
||||||
|
<lastEdited>636b6aa9b697b</lastEdited>
|
||||||
|
<lastViewed>636b6aa9b697b</lastViewed>
|
||||||
|
<lastWordCount>1206</lastWordCount>
|
||||||
|
<novelWordCount>830</novelWordCount>
|
||||||
|
<notesWordCount>376</notesWordCount>
|
||||||
|
<autoReplace>
|
||||||
|
<entry key="A">B</entry>
|
||||||
|
<entry key="B">E</entry>
|
||||||
|
<entry key="C">D</entry>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<title>%title%</title>
|
||||||
|
<chapter>Chapter %chw%: %title%</chapter>
|
||||||
|
<unnumbered>%title%</unnumbered>
|
||||||
|
<scene>Scene %ch%.%sc%: %title%</scene>
|
||||||
|
<section></section>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry blue="100" green="100" red="100">New</entry>
|
||||||
|
<entry blue="0" green="50" red="200">Notes</entry>
|
||||||
|
<entry blue="0" green="60" red="182">Started</entry>
|
||||||
|
<entry blue="0" green="129" red="193">1st Draft</entry>
|
||||||
|
<entry blue="0" green="129" red="193">2nd Draft</entry>
|
||||||
|
<entry blue="0" green="129" red="193">3rd Draft</entry>
|
||||||
|
<entry blue="58" green="180" red="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry blue="100" green="100" red="100">None</entry>
|
||||||
|
<entry blue="188" green="122" red="0">Minor</entry>
|
||||||
|
<entry blue="180" green="0" red="21">Major</entry>
|
||||||
|
<entry blue="175" green="0" red="117">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="25">
|
||||||
|
<item handle="7031beac91f75" order="0" parent="None">
|
||||||
|
<name>Novel</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Started</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" order="0" parent="7031beac91f75">
|
||||||
|
<name>Title Page</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Started</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>93</charCount>
|
||||||
|
<wordCount>19</wordCount>
|
||||||
|
<paraCount>2</paraCount>
|
||||||
|
<cursorPos>2</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" order="1" parent="7031beac91f75">
|
||||||
|
<name>Page</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>186</charCount>
|
||||||
|
<wordCount>39</wordCount>
|
||||||
|
<paraCount>2</paraCount>
|
||||||
|
<cursorPos>212</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" order="2" parent="7031beac91f75">
|
||||||
|
<name>Part One</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>26</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>33</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" order="3" parent="7031beac91f75">
|
||||||
|
<name>A Folder</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" order="0" parent="e7ded148d6e4a">
|
||||||
|
<name>Chapter One</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>Notes</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>75</charCount>
|
||||||
|
<wordCount>14</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>279</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" order="1" parent="e7ded148d6e4a">
|
||||||
|
<name>Making a Scene</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>2429</charCount>
|
||||||
|
<wordCount>432</wordCount>
|
||||||
|
<paraCount>14</paraCount>
|
||||||
|
<cursorPos>62</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" order="2" parent="e7ded148d6e4a">
|
||||||
|
<name>Another Scene</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>476</charCount>
|
||||||
|
<wordCount>93</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>577</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" order="3" parent="e7ded148d6e4a">
|
||||||
|
<name>Interlude</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>617</charCount>
|
||||||
|
<wordCount>101</wordCount>
|
||||||
|
<paraCount>3</paraCount>
|
||||||
|
<cursorPos>4</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" order="4" parent="e7ded148d6e4a">
|
||||||
|
<name>A Note on Structure</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>2nd Draft</status>
|
||||||
|
<exported>False</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>1692</charCount>
|
||||||
|
<wordCount>313</wordCount>
|
||||||
|
<paraCount>6</paraCount>
|
||||||
|
<cursorPos>1110</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" order="5" parent="e7ded148d6e4a">
|
||||||
|
<name>Chapter Two</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>139</charCount>
|
||||||
|
<wordCount>28</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>343</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" order="6" parent="e7ded148d6e4a">
|
||||||
|
<name>We Found John!</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>189</charCount>
|
||||||
|
<wordCount>37</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>224</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" order="1" parent="None">
|
||||||
|
<name>Characters</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" order="0" parent="f6622b4617424">
|
||||||
|
<name>Main Characters</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" order="0" parent="f7e2d9f330615">
|
||||||
|
<name>John Smith</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>Minor</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>49</charCount>
|
||||||
|
<wordCount>9</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>24</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" order="1" parent="f7e2d9f330615">
|
||||||
|
<name>Jane Smith</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>CHARACTER</class>
|
||||||
|
<status>Major</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>55</charCount>
|
||||||
|
<wordCount>9</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>25</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" order="2" parent="None">
|
||||||
|
<name>Locations</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" order="0" parent="15c4492bd5107">
|
||||||
|
<name>Earth</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Main</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>76</charCount>
|
||||||
|
<wordCount>15</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>20</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" order="1" parent="15c4492bd5107">
|
||||||
|
<name>Space</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Minor</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>115</charCount>
|
||||||
|
<wordCount>24</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>133</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" order="2" parent="15c4492bd5107">
|
||||||
|
<name>Mars</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>WORLD</class>
|
||||||
|
<status>Major</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>NOTE</layout>
|
||||||
|
<charCount>28</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>45</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="6827118336ac1" order="3" parent="None">
|
||||||
|
<name>Archive</name>
|
||||||
|
<type>ROOT</type>
|
||||||
|
<class>ARCHIVE</class>
|
||||||
|
<status>New</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="ae9bf3c3ea159" order="0" parent="6827118336ac1">
|
||||||
|
<name>Scenes</name>
|
||||||
|
<type>FOLDER</type>
|
||||||
|
<class>ARCHIVE</class>
|
||||||
|
<status>New</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="8a5deb88c0e97" order="0" parent="ae9bf3c3ea159">
|
||||||
|
<name>Old File</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>1st Draft</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>314</charCount>
|
||||||
|
<wordCount>55</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>322</cursorPos>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" order="4" parent="None">
|
||||||
|
<name>Trash</name>
|
||||||
|
<type>TRASH</type>
|
||||||
|
<class>TRASH</class>
|
||||||
|
<status>None</status>
|
||||||
|
<expanded>True</expanded>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" order="0" parent="98acd8c76c93a">
|
||||||
|
<name>Delete Me!</name>
|
||||||
|
<type>FILE</type>
|
||||||
|
<class>NOVEL</class>
|
||||||
|
<status>New</status>
|
||||||
|
<exported>True</exported>
|
||||||
|
<layout>DOCUMENT</layout>
|
||||||
|
<charCount>30</charCount>
|
||||||
|
<wordCount>6</wordCount>
|
||||||
|
<paraCount>1</paraCount>
|
||||||
|
<cursorPos>36</cursorPos>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 12:20:53">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<saveCount>5</saveCount>
|
||||||
|
<autoCount>10</autoCount>
|
||||||
|
<editTime>1000</editTime>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<doBackup>True</doBackup>
|
||||||
|
<language>en_GB</language>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<spellLang>en_GB</spellLang>
|
||||||
|
<novelWordCount>954</novelWordCount>
|
||||||
|
<notesWordCount>409</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">636b6aa9b697b</entry>
|
||||||
|
<entry key="viewer">636b6aa9b697b</entry>
|
||||||
|
<entry key="novelTree">7031beac91f75</entry>
|
||||||
|
<entry key="outline">7031beac91f75</entry>
|
||||||
|
</lastHandle>
|
||||||
|
<autoReplace>
|
||||||
|
<entry key="A">B</entry>
|
||||||
|
<entry key="B">E</entry>
|
||||||
|
<entry key="C">D</entry>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<entry key="title">%title%</entry>
|
||||||
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
|
<entry key="unnumbered">%title%</entry>
|
||||||
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
|
<entry key="section"></entry>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
|
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
||||||
|
<entry key="sc24b8f" count="3" red="182" green="60" blue="0">Started</entry>
|
||||||
|
<entry key="s90e6c9" count="7" red="193" green="129" blue="0">1st Draft</entry>
|
||||||
|
<entry key="sd51c5b" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||||
|
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||||
|
<entry key="s78ea90" count="1" red="58" green="180" blue="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry key="ia857f0" count="5" red="100" green="100" blue="100">None</entry>
|
||||||
|
<entry key="icfb3a5" count="2" red="0" green="122" blue="188">Minor</entry>
|
||||||
|
<entry key="i2d7a54" count="2" red="21" green="0" blue="180">Major</entry>
|
||||||
|
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="27">
|
||||||
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H1" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
||||||
|
<name status="sc24b8f" import="ia857f0" active="True">Title Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
||||||
|
<name status="sf12341" import="ia857f0" active="True">Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H1" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
|
<name status="s90e6c9" import="ia857f0" active="True">Part One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="True" heading="H2" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
||||||
|
<name status="sf24ce6" import="ia857f0" active="True">Chapter One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
||||||
|
<name status="s90e6c9" import="ia857f0" active="True">Making a Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
||||||
|
<name status="s90e6c9" import="ia857f0" active="True">Another Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
||||||
|
<name status="s78ea90" import="ia857f0" active="True">Interlude</name>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
||||||
|
<name status="sf24ce6" import="ia857f0" active="False">A Note on Structure</name>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="True" heading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
||||||
|
<name status="s90e6c9" import="ia857f0" active="True">Chapter Two</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
||||||
|
<name status="s90e6c9" import="ia857f0" active="True">We Found John!</name>
|
||||||
|
</item>
|
||||||
|
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="sf12341" import="ia857f0">Sequel</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H1" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
||||||
|
<name status="sc24b8f" import="ia857f0" active="True">Title Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
||||||
|
<name status="s90e6c9" import="ia857f0" active="True">Chapter One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="sf12341" import="ia857f0">Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" parent="f6622b4617424" root="f6622b4617424" order="0" type="FOLDER" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="sf12341" import="ia857f0">Main Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||||
|
<name status="sf12341" import="icfb3a5" active="True">John Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||||
|
<name status="sf12341" import="i2d7a54" active="True">Jane Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="sf12341" import="ia857f0">Locations</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||||
|
<name status="sf12341" import="i56be10" active="True">Earth</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||||
|
<name status="sf12341" import="icfb3a5" active="True">Space</name>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||||
|
<name status="sf12341" import="i2d7a54" active="True">Mars</name>
|
||||||
|
</item>
|
||||||
|
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="sf12341" import="ia857f0">Archive</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ae9bf3c3ea159" parent="6827118336ac1" root="6827118336ac1" order="0" type="FOLDER" class="ARCHIVE">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="sf12341" import="ia857f0">Scenes</name>
|
||||||
|
</item>
|
||||||
|
<item handle="8a5deb88c0e97" parent="ae9bf3c3ea159" root="6827118336ac1" order="0" type="FILE" class="ARCHIVE" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H3" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
||||||
|
<name status="s90e6c9" import="ia857f0" active="True">Old File</name>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="sf12341" import="ia857f0">Trash</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="98acd8c76c93a" order="0" type="FILE" class="TRASH" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H3" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
|
<name status="sf12341" import="ia857f0" active="True">Delete Me!</name>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
+30
-28
@@ -1,35 +1,37 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-17 21:17:31">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-31 22:17:38">
|
||||||
<project>
|
<project>
|
||||||
<name>Lorem Ipsum</name>
|
<name>Lorem Ipsum</name>
|
||||||
<title>Lorem Ipsum</title>
|
<title>Lorem Ipsum</title>
|
||||||
<author>lipsum.com</author>
|
<author>lipsum.com</author>
|
||||||
<saveCount>28</saveCount>
|
<saveCount>32</saveCount>
|
||||||
<autoCount>24</autoCount>
|
<autoCount>24</autoCount>
|
||||||
<editTime>1874</editTime>
|
<editTime>1889</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>False</doBackup>
|
<doBackup>False</doBackup>
|
||||||
<language>en_GB</language>
|
<language>en_GB</language>
|
||||||
<spellCheck>False</spellCheck>
|
<spellCheck>False</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>7a992350f3eb6</lastEdited>
|
<totalWordCount>3847</totalWordCount>
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>3847</lastWordCount>
|
|
||||||
<novelWordCount>3109</novelWordCount>
|
<novelWordCount>3109</novelWordCount>
|
||||||
<notesWordCount>738</notesWordCount>
|
<notesWordCount>738</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">7a992350f3eb6</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">b3643d0f92e32</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace>
|
<autoReplace>
|
||||||
<entry key="Rep1">Replace Text 1</entry>
|
<entry key="Rep1">Replace Text 1</entry>
|
||||||
<entry key="Rep2">Replace Text 2</entry>
|
<entry key="Rep2">Replace Text 2</entry>
|
||||||
</autoReplace>
|
</autoReplace>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>Chapter %ch%: %title%</chapter>
|
<entry key="chapter">Chapter %ch%: %title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>* * *</scene>
|
<entry key="scene">* * *</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="sbaa94f" count="3" red="100" green="100" blue="100">New</entry>
|
<entry key="sbaa94f" count="3" red="100" green="100" blue="100">New</entry>
|
||||||
@@ -50,19 +52,19 @@
|
|||||||
<name status="sbaa94f" import="i613591">Novel</name>
|
<name status="sbaa94f" import="i613591">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="7a992350f3eb6" parent="b3643d0f92e32" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="7a992350f3eb6" parent="b3643d0f92e32" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="230" wordCount="40" paraCount="3" cursorPos="148"/>
|
<meta expanded="False" heading="H1" charCount="230" wordCount="40" paraCount="3" cursorPos="148"/>
|
||||||
<name status="sedd043" import="i613591" active="True">Lorem Ipsum</name>
|
<name status="sedd043" import="i613591" active="True">Lorem Ipsum</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="8c58a65414c23" parent="b3643d0f92e32" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="8c58a65414c23" parent="b3643d0f92e32" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="1058" wordCount="176" paraCount="2" cursorPos="43"/>
|
<meta expanded="False" heading="H0" charCount="1058" wordCount="176" paraCount="2" cursorPos="43"/>
|
||||||
<name status="sedd043" import="i613591" active="True">Front Matter</name>
|
<name status="sedd043" import="i613591" active="True">Front Matter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="88d59a277361b" parent="b3643d0f92e32" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="88d59a277361b" parent="b3643d0f92e32" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="584" wordCount="92" paraCount="1" cursorPos="4"/>
|
<meta expanded="False" heading="H2" charCount="584" wordCount="92" paraCount="1" cursorPos="4"/>
|
||||||
<name status="s92a87b" import="i613591" active="True">Prologue</name>
|
<name status="s92a87b" import="i613591" active="True">Prologue</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="db7e733775d4d" parent="b3643d0f92e32" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="db7e733775d4d" parent="b3643d0f92e32" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="35" wordCount="6" paraCount="1" cursorPos="42"/>
|
<meta expanded="False" heading="H1" charCount="35" wordCount="6" paraCount="1" cursorPos="42"/>
|
||||||
<name status="sbaa94f" import="i613591" active="True">Act One</name>
|
<name status="sbaa94f" import="i613591" active="True">Act One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="45e6b01ca35c1" parent="b3643d0f92e32" root="b3643d0f92e32" order="4" type="FOLDER" class="NOVEL">
|
<item handle="45e6b01ca35c1" parent="b3643d0f92e32" root="b3643d0f92e32" order="4" type="FOLDER" class="NOVEL">
|
||||||
@@ -70,19 +72,19 @@
|
|||||||
<name status="s92a87b" import="i613591">Chapter One</name>
|
<name status="s92a87b" import="i613591">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="fb609cd8319dc" parent="45e6b01ca35c1" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="fb609cd8319dc" parent="45e6b01ca35c1" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="419" wordCount="67" paraCount="1" cursorPos="56"/>
|
<meta expanded="False" heading="H2" charCount="419" wordCount="67" paraCount="1" cursorPos="56"/>
|
||||||
<name status="s92a87b" import="i613591" active="True">Chapter One</name>
|
<name status="s92a87b" import="i613591" active="True">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="88243afbe5ed8" parent="45e6b01ca35c1" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="88243afbe5ed8" parent="45e6b01ca35c1" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="2758" wordCount="404" paraCount="4" cursorPos="1528"/>
|
<meta expanded="False" heading="H3" charCount="2758" wordCount="404" paraCount="4" cursorPos="1528"/>
|
||||||
<name status="sedd043" import="i613591" active="True">Scene One</name>
|
<name status="sedd043" import="i613591" active="True">Scene One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f96ec11c6a3da" parent="45e6b01ca35c1" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="f96ec11c6a3da" parent="45e6b01ca35c1" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="4043" wordCount="600" paraCount="6" cursorPos="2335"/>
|
<meta expanded="False" heading="H3" charCount="4043" wordCount="600" paraCount="6" cursorPos="2335"/>
|
||||||
<name status="sedd043" import="i613591" active="True">Scene Two</name>
|
<name status="sedd043" import="i613591" active="True">Scene Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="846352075de7d" parent="b3643d0f92e32" root="b3643d0f92e32" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="846352075de7d" parent="b3643d0f92e32" root="b3643d0f92e32" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="631" wordCount="109" paraCount="3" cursorPos="376"/>
|
<meta expanded="False" heading="H2" charCount="631" wordCount="109" paraCount="3" cursorPos="376"/>
|
||||||
<name status="sbaa94f" import="i613591" active="False">Interlude</name>
|
<name status="sbaa94f" import="i613591" active="False">Interlude</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6bd935d2490cd" parent="b3643d0f92e32" root="b3643d0f92e32" order="6" type="FOLDER" class="NOVEL">
|
<item handle="6bd935d2490cd" parent="b3643d0f92e32" root="b3643d0f92e32" order="6" type="FOLDER" class="NOVEL">
|
||||||
@@ -90,19 +92,19 @@
|
|||||||
<name status="s92a87b" import="i613591">Chapter Two</name>
|
<name status="s92a87b" import="i613591">Chapter Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="441420a886d82" parent="6bd935d2490cd" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="441420a886d82" parent="6bd935d2490cd" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="477" wordCount="70" paraCount="1" cursorPos="56"/>
|
<meta expanded="False" heading="H2" charCount="477" wordCount="70" paraCount="1" cursorPos="56"/>
|
||||||
<name status="s92a87b" import="i613591" active="True">Chapter Two</name>
|
<name status="s92a87b" import="i613591" active="True">Chapter Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="eb103bc70c90c" parent="6bd935d2490cd" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="eb103bc70c90c" parent="6bd935d2490cd" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="3006" wordCount="439" paraCount="4" cursorPos="57"/>
|
<meta expanded="False" heading="H3" charCount="3006" wordCount="439" paraCount="4" cursorPos="57"/>
|
||||||
<name status="sedd043" import="i613591" active="True">Scene Three</name>
|
<name status="sedd043" import="i613591" active="True">Scene Three</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f8c0562e50f1b" parent="6bd935d2490cd" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="f8c0562e50f1b" parent="6bd935d2490cd" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="3839" wordCount="563" paraCount="6" cursorPos="56"/>
|
<meta expanded="False" heading="H3" charCount="3839" wordCount="563" paraCount="6" cursorPos="56"/>
|
||||||
<name status="sedd043" import="i613591" active="True">Scene Four</name>
|
<name status="sedd043" import="i613591" active="True">Scene Four</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="47666c91c7ccf" parent="6bd935d2490cd" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="47666c91c7ccf" parent="6bd935d2490cd" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="3644" wordCount="543" paraCount="5" cursorPos="351"/>
|
<meta expanded="False" heading="H3" charCount="3644" wordCount="543" paraCount="5" cursorPos="351"/>
|
||||||
<name status="sedd043" import="i613591" active="True">Scene Five</name>
|
<name status="sedd043" import="i613591" active="True">Scene Five</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="67a8707f2f249" parent="None" root="67a8707f2f249" order="1" type="ROOT" class="CHARACTER">
|
<item handle="67a8707f2f249" parent="None" root="67a8707f2f249" order="1" type="ROOT" class="CHARACTER">
|
||||||
@@ -110,7 +112,7 @@
|
|||||||
<name status="sbaa94f" import="i613591">Characters</name>
|
<name status="sbaa94f" import="i613591">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="4c4f28287af27" parent="67a8707f2f249" root="67a8707f2f249" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="4c4f28287af27" parent="67a8707f2f249" root="67a8707f2f249" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="1864" wordCount="284" paraCount="3" cursorPos="1883"/>
|
<meta expanded="False" heading="H1" charCount="1864" wordCount="284" paraCount="3" cursorPos="1883"/>
|
||||||
<name status="sbaa94f" import="i613591" active="True">Mr. Nobody</name>
|
<name status="sbaa94f" import="i613591" active="True">Mr. Nobody</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6c6afb1247750" parent="None" root="6c6afb1247750" order="2" type="ROOT" class="PLOT">
|
<item handle="6c6afb1247750" parent="None" root="6c6afb1247750" order="2" type="ROOT" class="PLOT">
|
||||||
@@ -118,7 +120,7 @@
|
|||||||
<name status="sbaa94f" import="i613591">Plot</name>
|
<name status="sbaa94f" import="i613591">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="2426c6f0ca922" parent="6c6afb1247750" root="6c6afb1247750" order="0" type="FILE" class="PLOT" layout="NOTE">
|
<item handle="2426c6f0ca922" parent="6c6afb1247750" root="6c6afb1247750" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="1369" wordCount="195" paraCount="2" cursorPos="1387"/>
|
<meta expanded="False" heading="H1" charCount="1369" wordCount="195" paraCount="2" cursorPos="1387"/>
|
||||||
<name status="sbaa94f" import="i613591" active="True">Main</name>
|
<name status="sbaa94f" import="i613591" active="True">Main</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="60bdf227455cc" parent="None" root="60bdf227455cc" order="3" type="ROOT" class="WORLD">
|
<item handle="60bdf227455cc" parent="None" root="60bdf227455cc" order="3" type="ROOT" class="WORLD">
|
||||||
@@ -126,7 +128,7 @@
|
|||||||
<name status="sbaa94f" import="i613591">World</name>
|
<name status="sbaa94f" import="i613591">World</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="04468803b92e1" parent="60bdf227455cc" root="60bdf227455cc" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="04468803b92e1" parent="60bdf227455cc" root="60bdf227455cc" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="1792"/>
|
<meta expanded="False" heading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="1792"/>
|
||||||
<name status="sbaa94f" import="i613591" active="True">Ancient Europe</name>
|
<name status="sbaa94f" import="i613591" active="True">Ancient Europe</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
%%~name: New Scene
|
|
||||||
%%~path: a6d311a93600a/8c659a11cd429
|
|
||||||
%%~kind: NOVEL/DOCUMENT
|
|
||||||
### New Scene
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
%%~name: Title Page
|
|
||||||
%%~path: a508bb932959c/a35baf2e93843
|
|
||||||
%%~kind: NOVEL/DOCUMENT
|
|
||||||
#! Minimal
|
|
||||||
|
|
||||||
>> By Jane Doe, John Doh <<
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
%%~name: New Chapter
|
|
||||||
%%~path: a6d311a93600a/f5ab3e30151e1
|
|
||||||
%%~kind: NOVEL/DOCUMENT
|
|
||||||
## New Chapter
|
|
||||||
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-17 20:32:06">
|
|
||||||
<project>
|
|
||||||
<name>Test Minimal</name>
|
|
||||||
<title>Minimal</title>
|
|
||||||
<author>Jane Doe</author>
|
|
||||||
<author>John Doh</author>
|
|
||||||
<saveCount>19</saveCount>
|
|
||||||
<autoCount>2</autoCount>
|
|
||||||
<editTime>167</editTime>
|
|
||||||
</project>
|
|
||||||
<settings>
|
|
||||||
<doBackup>True</doBackup>
|
|
||||||
<language>en_GB</language>
|
|
||||||
<spellCheck>False</spellCheck>
|
|
||||||
<spellLang>None</spellLang>
|
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>a508bb932959c</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>10</lastWordCount>
|
|
||||||
<novelWordCount>10</novelWordCount>
|
|
||||||
<notesWordCount>0</notesWordCount>
|
|
||||||
<autoReplace/>
|
|
||||||
<titleFormat>
|
|
||||||
<title>%title%</title>
|
|
||||||
<chapter>Chapter %ch%: %title%</chapter>
|
|
||||||
<unnumbered>%title%</unnumbered>
|
|
||||||
<scene>* * *</scene>
|
|
||||||
<section></section>
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
|
||||||
<entry key="s72322d" count="5" red="100" green="100" blue="100">New</entry>
|
|
||||||
<entry key="sb6adc7" count="0" red="200" green="50" blue="0">Note</entry>
|
|
||||||
<entry key="s2ae76d" count="0" red="200" green="150" blue="0">Draft</entry>
|
|
||||||
<entry key="s541953" count="0" red="50" green="200" blue="0">Finished</entry>
|
|
||||||
</status>
|
|
||||||
<importance>
|
|
||||||
<entry key="iffcacb" count="3" red="100" green="100" blue="100">New</entry>
|
|
||||||
<entry key="i6b130b" count="0" red="200" green="50" blue="0">Minor</entry>
|
|
||||||
<entry key="iece803" count="0" red="200" green="150" blue="0">Major</entry>
|
|
||||||
<entry key="i5ba06e" count="0" red="50" green="200" blue="0">Main</entry>
|
|
||||||
</importance>
|
|
||||||
</settings>
|
|
||||||
<content count="8">
|
|
||||||
<item handle="a508bb932959c" parent="None" root="a508bb932959c" order="0" type="ROOT" class="NOVEL">
|
|
||||||
<meta expanded="True"/>
|
|
||||||
<name status="s72322d" import="iffcacb">Novel</name>
|
|
||||||
</item>
|
|
||||||
<item handle="a35baf2e93843" parent="a508bb932959c" root="a508bb932959c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="33"/>
|
|
||||||
<name status="s72322d" import="iffcacb" active="True">Title Page</name>
|
|
||||||
</item>
|
|
||||||
<item handle="a6d311a93600a" parent="a508bb932959c" root="a508bb932959c" order="1" type="FOLDER" class="NOVEL">
|
|
||||||
<meta expanded="True"/>
|
|
||||||
<name status="s72322d" import="iffcacb">New Chapter</name>
|
|
||||||
</item>
|
|
||||||
<item handle="f5ab3e30151e1" parent="a6d311a93600a" root="a508bb932959c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="16"/>
|
|
||||||
<name status="s72322d" import="iffcacb" active="True">New Chapter</name>
|
|
||||||
</item>
|
|
||||||
<item handle="8c659a11cd429" parent="a6d311a93600a" root="a508bb932959c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
|
||||||
<meta expanded="False" mainHeading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="15"/>
|
|
||||||
<name status="s72322d" import="iffcacb" active="True">New Scene</name>
|
|
||||||
</item>
|
|
||||||
<item handle="7695ce551d265" parent="None" root="7695ce551d265" order="1" type="ROOT" class="PLOT">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s72322d" import="iffcacb">Plot</name>
|
|
||||||
</item>
|
|
||||||
<item handle="afb3043c7b2b3" parent="None" root="afb3043c7b2b3" order="2" type="ROOT" class="CHARACTER">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s72322d" import="iffcacb">Characters</name>
|
|
||||||
</item>
|
|
||||||
<item handle="9d5247ab588e0" parent="None" root="9d5247ab588e0" order="3" type="ROOT" class="WORLD">
|
|
||||||
<meta expanded="False"/>
|
|
||||||
<name status="s72322d" import="iffcacb">World</name>
|
|
||||||
</item>
|
|
||||||
</content>
|
|
||||||
</novelWriterXML>
|
|
||||||
+5
-1
@@ -19,14 +19,18 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject
|
||||||
|
|
||||||
|
|
||||||
# =========================================================================== #
|
# =========================================================================== #
|
||||||
# Mock GUI
|
# Mock GUI
|
||||||
# =========================================================================== #
|
# =========================================================================== #
|
||||||
|
|
||||||
class MockGuiMain:
|
class MockGuiMain(QObject):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
self.mainConf = None
|
self.mainConf = None
|
||||||
self.hasProject = True
|
self.hasProject = True
|
||||||
self.theProject = None
|
self.theProject = None
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:56">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||||
<project>
|
<project>
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
@@ -14,122 +14,123 @@
|
|||||||
<language>None</language>
|
<language>None</language>
|
||||||
<spellCheck>False</spellCheck>
|
<spellCheck>False</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>0</lastWordCount>
|
|
||||||
<novelWordCount>0</novelWordCount>
|
<novelWordCount>0</novelWordCount>
|
||||||
<notesWordCount>0</notesWordCount>
|
<notesWordCount>0</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>%title%</chapter>
|
<entry key="chapter">%title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>* * *</scene>
|
<entry key="scene">* * *</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000008" count="15" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" 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="s000001" 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="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||||
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||||
</status>
|
</status>
|
||||||
<importance>
|
<importance>
|
||||||
<entry key="i00000c" count="7" red="100" green="100" blue="100">New</entry>
|
<entry key="i000004" 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="i000005" 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="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||||
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content count="22">
|
<content count="22">
|
||||||
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000009" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Title Page</name>
|
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000a" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Chapter 1</name>
|
<name status="s000000" import="i000004" active="True">Chapter 1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000013" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000b" parent="000000000000a" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 1.1</name>
|
<name status="s000000" import="i000004" active="True">Scene 1.1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000014" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000c" parent="000000000000a" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 1.2</name>
|
<name status="s000000" import="i000004" active="True">Scene 1.2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000015" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000d" parent="000000000000a" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 1.3</name>
|
<name status="s000000" import="i000004" active="True">Scene 1.3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000e" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Chapter 2</name>
|
<name status="s000000" import="i000004" active="True">Chapter 2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000017" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="000000000000e" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 2.1</name>
|
<name status="s000000" import="i000004" active="True">Scene 2.1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000018" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000010" parent="000000000000e" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 2.2</name>
|
<name status="s000000" import="i000004" active="True">Scene 2.2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000019" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000011" parent="000000000000e" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 2.3</name>
|
<name status="s000000" import="i000004" active="True">Scene 2.3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001a" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000012" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Chapter 3</name>
|
<name status="s000000" import="i000004" active="True">Chapter 3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001b" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000013" parent="0000000000012" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 3.1</name>
|
<name status="s000000" import="i000004" active="True">Scene 3.1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001c" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000014" parent="0000000000012" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 3.2</name>
|
<name status="s000000" import="i000004" active="True">Scene 3.2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001d" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000015" parent="0000000000012" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 3.3</name>
|
<name status="s000000" import="i000004" active="True">Scene 3.3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="PLOT">
|
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="PLOT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Plot</name>
|
<name status="s000000" import="i000004">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001f" parent="000000000001e" root="000000000001e" order="0" type="FILE" class="PLOT" layout="NOTE">
|
<item handle="0000000000017" parent="0000000000016" root="0000000000016" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Main Plot</name>
|
<name status="s000000" import="i000004" active="True">Main Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000020" parent="None" root="0000000000020" order="0" type="ROOT" class="CHARACTER">
|
<item handle="0000000000018" parent="None" root="0000000000018" order="0" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Characters</name>
|
<name status="s000000" import="i000004">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000021" parent="0000000000020" root="0000000000020" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="0000000000019" parent="0000000000018" root="0000000000018" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Protagonist</name>
|
<name status="s000000" import="i000004" active="True">Protagonist</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000022" parent="None" root="0000000000022" order="0" type="ROOT" class="WORLD">
|
<item handle="000000000001a" parent="None" root="000000000001a" order="0" type="ROOT" class="WORLD">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Locations</name>
|
<name status="s000000" import="i000004">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000023" parent="0000000000022" root="0000000000022" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="000000000001b" parent="000000000001a" root="000000000001a" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Main Location</name>
|
<name status="s000000" import="i000004" active="True">Main Location</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000024" parent="None" root="0000000000024" order="0" type="ROOT" class="ARCHIVE">
|
<item handle="000000000001c" parent="None" root="000000000001c" order="0" type="ROOT" class="ARCHIVE">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Archive</name>
|
<name status="s000000" import="i000004">Archive</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000025" parent="None" root="0000000000025" order="0" type="ROOT" class="TRASH">
|
<item handle="000000000001d" parent="None" root="000000000001d" order="0" type="ROOT" class="TRASH">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Trash</name>
|
<name status="s000000" import="i000004">Trash</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
</novelWriterXML>
|
</novelWriterXML>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:56">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||||
<project>
|
<project>
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
@@ -14,98 +14,99 @@
|
|||||||
<language>None</language>
|
<language>None</language>
|
||||||
<spellCheck>False</spellCheck>
|
<spellCheck>False</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>0</lastWordCount>
|
|
||||||
<novelWordCount>0</novelWordCount>
|
<novelWordCount>0</novelWordCount>
|
||||||
<notesWordCount>0</notesWordCount>
|
<notesWordCount>0</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>%title%</chapter>
|
<entry key="chapter">%title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>* * *</scene>
|
<entry key="scene">* * *</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000008" count="9" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" 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="s000001" 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="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||||
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||||
</status>
|
</status>
|
||||||
<importance>
|
<importance>
|
||||||
<entry key="i00000c" count="7" red="100" green="100" blue="100">New</entry>
|
<entry key="i000004" 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="i000005" 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="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||||
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content count="16">
|
<content count="16">
|
||||||
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000009" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Title Page</name>
|
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000a" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 1</name>
|
<name status="s000000" import="i000004" active="True">Scene 1</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000013" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000b" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 2</name>
|
<name status="s000000" import="i000004" active="True">Scene 2</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 3</name>
|
<name status="s000000" import="i000004" active="True">Scene 3</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 4</name>
|
<name status="s000000" import="i000004" active="True">Scene 4</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000e" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 5</name>
|
<name status="s000000" import="i000004" active="True">Scene 5</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Scene 6</name>
|
<name status="s000000" import="i000004" active="True">Scene 6</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000018" parent="None" root="0000000000018" order="0" type="ROOT" class="PLOT">
|
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="PLOT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Plot</name>
|
<name status="s000000" import="i000004">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000019" parent="0000000000018" root="0000000000018" order="0" type="FILE" class="PLOT" layout="NOTE">
|
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Main Plot</name>
|
<name status="s000000" import="i000004" active="True">Main Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001a" parent="None" root="000000000001a" order="0" type="ROOT" class="CHARACTER">
|
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Characters</name>
|
<name status="s000000" import="i000004">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001b" parent="000000000001a" root="000000000001a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="0000000000013" parent="0000000000012" root="0000000000012" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Protagonist</name>
|
<name status="s000000" import="i000004" active="True">Protagonist</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001c" parent="None" root="000000000001c" order="0" type="ROOT" class="WORLD">
|
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="WORLD">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Locations</name>
|
<name status="s000000" import="i000004">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001d" parent="000000000001c" root="000000000001c" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="0000000000015" parent="0000000000014" root="0000000000014" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Main Location</name>
|
<name status="s000000" import="i000004" active="True">Main Location</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001e" parent="None" root="000000000001e" order="0" type="ROOT" class="ARCHIVE">
|
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="ARCHIVE">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Archive</name>
|
<name status="s000000" import="i000004">Archive</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000001f" parent="None" root="000000000001f" order="0" type="ROOT" class="TRASH">
|
<item handle="0000000000017" parent="None" root="0000000000017" order="0" type="ROOT" class="TRASH">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Trash</name>
|
<name status="s000000" import="i000004">Trash</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
</novelWriterXML>
|
</novelWriterXML>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:56">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||||
<project>
|
<project>
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -13,20 +13,21 @@
|
|||||||
<language>None</language>
|
<language>None</language>
|
||||||
<spellCheck>False</spellCheck>
|
<spellCheck>False</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>13</lastWordCount>
|
|
||||||
<novelWordCount>10</novelWordCount>
|
<novelWordCount>10</novelWordCount>
|
||||||
<notesWordCount>3</notesWordCount>
|
<notesWordCount>3</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>%title%</chapter>
|
<entry key="chapter">%title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>* * *</scene>
|
<entry key="scene">* * *</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
<name status="s000000" import="i000004">World</name>
|
<name status="s000000" import="i000004">World</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
<meta expanded="False" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
||||||
@@ -67,23 +68,23 @@
|
|||||||
<name status="s000000" import="i000004">New Chapter</name>
|
<name status="s000000" import="i000004">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000020" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
<item handle="0000000000010" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Stuff</name>
|
<name status="s000000" import="i000004">Stuff</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000021" parent="0000000000020" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000011" parent="0000000000010" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H2" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">Hello</name>
|
<name status="s000000" import="i000004" active="True">Hello</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000022" parent="000000000000a" root="000000000000a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="0000000000012" parent="000000000000a" root="000000000000a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="11" wordCount="3" paraCount="1" cursorPos="0"/>
|
<meta expanded="False" heading="H1" charCount="11" wordCount="3" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">Jane</name>
|
<name status="s000000" import="i000004" active="True">Jane</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:56">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||||
<project>
|
<project>
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title></title>
|
<title>None</title>
|
||||||
<saveCount>2</saveCount>
|
<saveCount>2</saveCount>
|
||||||
<autoCount>1</autoCount>
|
<autoCount>1</autoCount>
|
||||||
<editTime>0</editTime>
|
<editTime>0</editTime>
|
||||||
@@ -12,66 +12,67 @@
|
|||||||
<language>None</language>
|
<language>None</language>
|
||||||
<spellCheck>False</spellCheck>
|
<spellCheck>False</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>0</lastWordCount>
|
|
||||||
<novelWordCount>0</novelWordCount>
|
<novelWordCount>0</novelWordCount>
|
||||||
<notesWordCount>0</notesWordCount>
|
<notesWordCount>0</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>%title%</chapter>
|
<entry key="chapter">%title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>* * *</scene>
|
<entry key="scene">* * *</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000008" count="5" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s000001" 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="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||||
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||||
</status>
|
</status>
|
||||||
<importance>
|
<importance>
|
||||||
<entry key="i00000c" count="3" red="100" green="100" blue="100">New</entry>
|
<entry key="i000004" count="3" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
|
<entry key="i000005" 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="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||||
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content count="8">
|
<content count="8">
|
||||||
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="0000000000009" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">Title Page</name>
|
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000a" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">New Chapter</name>
|
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000013" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000b" parent="000000000000a" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000008" import="i00000c" active="True">New Scene</name>
|
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="PLOT">
|
<item handle="000000000000c" parent="None" root="000000000000c" order="0" type="ROOT" class="PLOT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Plot</name>
|
<name status="s000000" import="i000004">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="CHARACTER">
|
<item handle="000000000000d" parent="None" root="000000000000d" order="0" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Characters</name>
|
<name status="s000000" import="i000004">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="WORLD">
|
<item handle="000000000000e" parent="None" root="000000000000e" order="0" type="ROOT" class="WORLD">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Locations</name>
|
<name status="s000000" import="i000004">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000017" parent="None" root="0000000000017" order="0" type="ROOT" class="ARCHIVE">
|
<item handle="000000000000f" parent="None" root="000000000000f" order="0" type="ROOT" class="ARCHIVE">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000008" import="i00000c">Archive</name>
|
<name status="s000000" import="i000004">Archive</name>
|
||||||
</item>
|
</item>
|
||||||
</content>
|
</content>
|
||||||
</novelWriterXML>
|
</novelWriterXML>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:21:56">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||||
<project>
|
<project>
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -13,20 +13,21 @@
|
|||||||
<language>None</language>
|
<language>None</language>
|
||||||
<spellCheck>False</spellCheck>
|
<spellCheck>False</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>9</lastWordCount>
|
|
||||||
<novelWordCount>9</novelWordCount>
|
<novelWordCount>9</novelWordCount>
|
||||||
<notesWordCount>0</notesWordCount>
|
<notesWordCount>0</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>%title%</chapter>
|
<entry key="chapter">%title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>* * *</scene>
|
<entry key="scene">* * *</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="6" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="6" red="100" green="100" blue="100">New</entry>
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
<name status="s000000" import="i000004">World</name>
|
<name status="s000000" import="i000004">World</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
<meta expanded="False" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
||||||
@@ -67,42 +68,42 @@
|
|||||||
<name status="s000000" import="i000004">New Chapter</name>
|
<name status="s000000" import="i000004">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000020" parent="None" root="0000000000020" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000021" parent="None" root="0000000000021" order="0" type="ROOT" class="PLOT">
|
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Plot</name>
|
<name status="s000000" import="i000004">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000022" parent="None" root="0000000000022" order="0" type="ROOT" class="CHARACTER">
|
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Characters</name>
|
<name status="s000000" import="i000004">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000023" parent="None" root="0000000000023" order="0" type="ROOT" class="WORLD">
|
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Locations</name>
|
<name status="s000000" import="i000004">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000024" parent="None" root="0000000000024" order="0" type="ROOT" class="TIMELINE">
|
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="TIMELINE">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Timeline</name>
|
<name status="s000000" import="i000004">Timeline</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000025" parent="None" root="0000000000025" order="0" type="ROOT" class="OBJECT">
|
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="OBJECT">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Objects</name>
|
<name status="s000000" import="i000004">Objects</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000026" parent="None" root="0000000000026" order="0" type="ROOT" class="CUSTOM">
|
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="CUSTOM">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Custom</name>
|
<name status="s000000" import="i000004">Custom</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000027" parent="None" root="0000000000027" order="0" type="ROOT" class="CUSTOM">
|
<item handle="0000000000017" parent="None" root="0000000000017" order="0" type="ROOT" class="CUSTOM">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Custom</name>
|
<name status="s000000" import="i000004">Custom</name>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
%%~name: New Note
|
%%~name: New Note
|
||||||
%%~path: 000000000000a/0000000000020
|
%%~path: 000000000000a/0000000000010
|
||||||
%%~kind: CHARACTER/NOTE
|
%%~kind: CHARACTER/NOTE
|
||||||
# Jane Doe
|
# Jane Doe
|
||||||
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
%%~name: New Note
|
%%~name: New Note
|
||||||
%%~path: 0000000000009/0000000000021
|
%%~path: 0000000000009/0000000000011
|
||||||
%%~kind: PLOT/NOTE
|
%%~kind: PLOT/NOTE
|
||||||
# Main Plot
|
# Main Plot
|
||||||
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
%%~name: New Note
|
%%~name: New Note
|
||||||
%%~path: 000000000000b/0000000000022
|
%%~path: 000000000000b/0000000000012
|
||||||
%%~kind: WORLD/NOTE
|
%%~kind: WORLD/NOTE
|
||||||
# Main Location
|
# Main Location
|
||||||
|
|
||||||
@@ -1,32 +1,33 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:25:48">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 12:01:37">
|
||||||
<project>
|
<project>
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
<saveCount>4</saveCount>
|
<saveCount>4</saveCount>
|
||||||
<autoCount>2</autoCount>
|
<autoCount>2</autoCount>
|
||||||
<editTime>4</editTime>
|
<editTime>3</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>True</doBackup>
|
<doBackup>True</doBackup>
|
||||||
<language>None</language>
|
<language>None</language>
|
||||||
<spellCheck>True</spellCheck>
|
<spellCheck>True</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>000000000000f</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>0000000000008</lastNovel>
|
|
||||||
<lastOutline>0000000000008</lastOutline>
|
|
||||||
<lastWordCount>163</lastWordCount>
|
|
||||||
<novelWordCount>136</novelWordCount>
|
<novelWordCount>136</novelWordCount>
|
||||||
<notesWordCount>27</notesWordCount>
|
<notesWordCount>27</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">000000000000f</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">0000000000008</entry>
|
||||||
|
<entry key="outline">0000000000008</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>%title%</chapter>
|
<entry key="chapter">%title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>* * *</scene>
|
<entry key="scene">* * *</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
<meta expanded="False" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
|
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
|
||||||
@@ -55,38 +56,38 @@
|
|||||||
<name status="s000000" import="i000004">New Chapter</name>
|
<name status="s000000" import="i000004">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="781" wordCount="129" paraCount="14" cursorPos="1010"/>
|
<meta expanded="False" heading="H1" charCount="781" wordCount="129" paraCount="14" cursorPos="1010"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="s000000" import="i000004">Plot</name>
|
<name status="s000000" import="i000004">Plot</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000021" parent="0000000000009" root="0000000000009" order="0" type="FILE" class="PLOT" layout="NOTE">
|
<item handle="0000000000011" parent="0000000000009" root="0000000000009" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="48" wordCount="10" paraCount="1" cursorPos="69"/>
|
<meta expanded="False" heading="H1" charCount="48" wordCount="10" paraCount="1" cursorPos="69"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Note</name>
|
<name status="s000000" import="i000004" active="True">New Note</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
|
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="s000000" import="i000004">Characters</name>
|
<name status="s000000" import="i000004">Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000020" parent="000000000000a" root="000000000000a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="0000000000010" parent="000000000000a" root="000000000000a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="34" wordCount="8" paraCount="1" cursorPos="51"/>
|
<meta expanded="False" heading="H1" charCount="34" wordCount="8" paraCount="1" cursorPos="51"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Note</name>
|
<name status="s000000" import="i000004" active="True">New Note</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
|
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
|
||||||
<meta expanded="True"/>
|
<meta expanded="True"/>
|
||||||
<name status="s000000" import="i000004">World</name>
|
<name status="s000000" import="i000004">World</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000022" parent="000000000000b" root="000000000000b" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="0000000000012" parent="000000000000b" root="000000000000b" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="51" wordCount="9" paraCount="1" cursorPos="68"/>
|
<meta expanded="False" heading="H1" charCount="51" wordCount="9" paraCount="1" cursorPos="68"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Note</name>
|
<name status="s000000" import="i000004" active="True">New Note</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000024" parent="None" root="0000000000024" order="4" type="ROOT" class="TRASH">
|
<item handle="0000000000014" parent="None" root="0000000000014" order="4" type="ROOT" class="TRASH">
|
||||||
<meta expanded="False"/>
|
<meta expanded="False"/>
|
||||||
<name status="s000000" import="i000004">Trash</name>
|
<name status="s000000" import="i000004">Trash</name>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-20 18:22:03">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:30">
|
||||||
<project>
|
<project>
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -13,20 +13,21 @@
|
|||||||
<language>None</language>
|
<language>None</language>
|
||||||
<spellCheck>False</spellCheck>
|
<spellCheck>False</spellCheck>
|
||||||
<spellLang>None</spellLang>
|
<spellLang>None</spellLang>
|
||||||
<lastEdited>None</lastEdited>
|
|
||||||
<lastViewed>None</lastViewed>
|
|
||||||
<lastNovel>None</lastNovel>
|
|
||||||
<lastOutline>None</lastOutline>
|
|
||||||
<lastWordCount>9</lastWordCount>
|
|
||||||
<novelWordCount>9</novelWordCount>
|
<novelWordCount>9</novelWordCount>
|
||||||
<notesWordCount>0</notesWordCount>
|
<notesWordCount>0</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
<autoReplace/>
|
<autoReplace/>
|
||||||
<titleFormat>
|
<titleFormat>
|
||||||
<title>%title%</title>
|
<entry key="title">%title%</entry>
|
||||||
<chapter>%title%</chapter>
|
<entry key="chapter">%title%</entry>
|
||||||
<unnumbered>%title%</unnumbered>
|
<entry key="unnumbered">%title%</entry>
|
||||||
<scene>* * *</scene>
|
<entry key="scene">* * *</entry>
|
||||||
<section></section>
|
<entry key="section"></entry>
|
||||||
</titleFormat>
|
</titleFormat>
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
<meta expanded="False" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
|
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
|
||||||
@@ -55,11 +56,11 @@
|
|||||||
<name status="s000000" import="i000004">New Chapter</name>
|
<name status="s000000" import="i000004">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="False" mainHeading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
<meta expanded="False" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
||||||
|
|||||||
@@ -0,0 +1,515 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"handle": "7031beac91f75",
|
||||||
|
"parent": null,
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 0,
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "NO_LAYOUT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "Novel",
|
||||||
|
"status": "sc24b8f",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "53b69b83cdafc",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 93,
|
||||||
|
"wordCount": 19,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 119,
|
||||||
|
"label": "Title Page",
|
||||||
|
"status": "sc24b8f",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "974e400180a99",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 1,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 251,
|
||||||
|
"wordCount": 50,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 277,
|
||||||
|
"label": "Page",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "edca4be2fcaf8",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 2,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 26,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 36,
|
||||||
|
"label": "Part One",
|
||||||
|
"status": "s90e6c9",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "6a2d6d5f4f401",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 3,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H2",
|
||||||
|
"charCount": 95,
|
||||||
|
"wordCount": 18,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 291,
|
||||||
|
"label": "Chapter One",
|
||||||
|
"status": "sf24ce6",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "636b6aa9b697b",
|
||||||
|
"parent": "6a2d6d5f4f401",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H3",
|
||||||
|
"charCount": 2687,
|
||||||
|
"wordCount": 479,
|
||||||
|
"paraCount": 14,
|
||||||
|
"cursorPos": 67,
|
||||||
|
"label": "Making a Scene",
|
||||||
|
"status": "s90e6c9",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bc0cbd2a407f3",
|
||||||
|
"parent": "6a2d6d5f4f401",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 1,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H3",
|
||||||
|
"charCount": 548,
|
||||||
|
"wordCount": 108,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 465,
|
||||||
|
"label": "Another Scene",
|
||||||
|
"status": "s90e6c9",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ba8a28a246524",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 4,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H2",
|
||||||
|
"charCount": 617,
|
||||||
|
"wordCount": 101,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 310,
|
||||||
|
"label": "Interlude",
|
||||||
|
"status": "s78ea90",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "96b68994dfa3d",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 5,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "NOTE",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 1909,
|
||||||
|
"wordCount": 346,
|
||||||
|
"paraCount": 7,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "A Note on Structure",
|
||||||
|
"status": "sf24ce6",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "88706ddc78b1b",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 6,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H2",
|
||||||
|
"charCount": 139,
|
||||||
|
"wordCount": 28,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 188,
|
||||||
|
"label": "Chapter Two",
|
||||||
|
"status": "s90e6c9",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ae7339df26ded",
|
||||||
|
"parent": "88706ddc78b1b",
|
||||||
|
"root": "7031beac91f75",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H3",
|
||||||
|
"charCount": 189,
|
||||||
|
"wordCount": 37,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "We Found John!",
|
||||||
|
"status": "s90e6c9",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "e5e47ebf63b1c",
|
||||||
|
"parent": null,
|
||||||
|
"root": "e5e47ebf63b1c",
|
||||||
|
"order": 1,
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "NO_LAYOUT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "Sequel",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bacb7059e3083",
|
||||||
|
"parent": "e5e47ebf63b1c",
|
||||||
|
"root": "e5e47ebf63b1c",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 27,
|
||||||
|
"wordCount": 5,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 100,
|
||||||
|
"label": "Title Page",
|
||||||
|
"status": "sc24b8f",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "a520879ca0b45",
|
||||||
|
"parent": "e5e47ebf63b1c",
|
||||||
|
"root": "e5e47ebf63b1c",
|
||||||
|
"order": 1,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H2",
|
||||||
|
"charCount": 299,
|
||||||
|
"wordCount": 55,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 104,
|
||||||
|
"label": "Chapter One",
|
||||||
|
"status": "s90e6c9",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f6622b4617424",
|
||||||
|
"parent": null,
|
||||||
|
"root": "f6622b4617424",
|
||||||
|
"order": 2,
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"layout": "NO_LAYOUT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "Characters",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f7e2d9f330615",
|
||||||
|
"parent": "f6622b4617424",
|
||||||
|
"root": "f6622b4617424",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"layout": "NO_LAYOUT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "Main Characters",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "14298de4d9524",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": "f6622b4617424",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"layout": "NOTE",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 49,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 24,
|
||||||
|
"label": "John Smith",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "icfb3a5",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bb2c23b3c42cc",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": "f6622b4617424",
|
||||||
|
"order": 1,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"layout": "NOTE",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 55,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 25,
|
||||||
|
"label": "Jane Smith",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "i2d7a54",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "15c4492bd5107",
|
||||||
|
"parent": null,
|
||||||
|
"root": "15c4492bd5107",
|
||||||
|
"order": 3,
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "WORLD",
|
||||||
|
"layout": "NO_LAYOUT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "Locations",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b3e74dbc1f584",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": "15c4492bd5107",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"layout": "NOTE",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 76,
|
||||||
|
"wordCount": 15,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 20,
|
||||||
|
"label": "Earth",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "i56be10",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f1471bef9f2ae",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": "15c4492bd5107",
|
||||||
|
"order": 1,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"layout": "NOTE",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 115,
|
||||||
|
"wordCount": 24,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 133,
|
||||||
|
"label": "Space",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "icfb3a5",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "5eaea4e8cdee8",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": "15c4492bd5107",
|
||||||
|
"order": 2,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"layout": "NOTE",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 28,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 45,
|
||||||
|
"label": "Mars",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "i2d7a54",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "6827118336ac1",
|
||||||
|
"parent": null,
|
||||||
|
"root": "6827118336ac1",
|
||||||
|
"order": 4,
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "ARCHIVE",
|
||||||
|
"layout": "NO_LAYOUT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "Archive",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ae9bf3c3ea159",
|
||||||
|
"parent": "6827118336ac1",
|
||||||
|
"root": "6827118336ac1",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "ARCHIVE",
|
||||||
|
"layout": "NO_LAYOUT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "Scenes",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "8a5deb88c0e97",
|
||||||
|
"parent": "ae9bf3c3ea159",
|
||||||
|
"root": "6827118336ac1",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "ARCHIVE",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H3",
|
||||||
|
"charCount": 232,
|
||||||
|
"wordCount": 42,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 239,
|
||||||
|
"label": "Old File",
|
||||||
|
"status": "s90e6c9",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "98acd8c76c93a",
|
||||||
|
"parent": null,
|
||||||
|
"root": "98acd8c76c93a",
|
||||||
|
"order": 5,
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "TRASH",
|
||||||
|
"layout": "NO_LAYOUT",
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"label": "Trash",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b8136a5a774a0",
|
||||||
|
"parent": "98acd8c76c93a",
|
||||||
|
"root": "98acd8c76c93a",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "TRASH",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H3",
|
||||||
|
"charCount": 30,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 36,
|
||||||
|
"label": "Delete Me!",
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,362 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"handle": "7031beac91f75",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Novel",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "53b69b83cdafc",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Title Page",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 72,
|
||||||
|
"wordCount": 15,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 78,
|
||||||
|
"status": "s000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "974e400180a99",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Page",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 208,
|
||||||
|
"wordCount": 40,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 213,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "edca4be2fcaf8",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Part One",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 23,
|
||||||
|
"wordCount": 5,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "e7ded148d6e4a",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "A Folder",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "6a2d6d5f4f401",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Chapter One",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 12,
|
||||||
|
"wordCount": 3,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 215,
|
||||||
|
"status": "s000001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "636b6aa9b697b",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Making a Scene",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 1199,
|
||||||
|
"wordCount": 216,
|
||||||
|
"paraCount": 7,
|
||||||
|
"cursorPos": 527,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bc0cbd2a407f3",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Another Scene",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 476,
|
||||||
|
"wordCount": 93,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 551,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ba8a28a246524",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Interlude",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 633,
|
||||||
|
"wordCount": 101,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 1238,
|
||||||
|
"status": "s000006"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "96b68994dfa3d",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 4,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "A Note on Structure",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": false,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 1692,
|
||||||
|
"wordCount": 313,
|
||||||
|
"paraCount": 6,
|
||||||
|
"cursorPos": 1721,
|
||||||
|
"status": "s000004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "88706ddc78b1b",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 5,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Chapter Two",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 139,
|
||||||
|
"wordCount": 28,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 343,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ae7339df26ded",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 6,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "We Found John!",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 189,
|
||||||
|
"wordCount": 37,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 224,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f6622b4617424",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Characters",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f7e2d9f330615",
|
||||||
|
"parent": "f6622b4617424",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Main Characters",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "14298de4d9524",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "John Smith",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 49,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 24,
|
||||||
|
"import": "i000008"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bb2c23b3c42cc",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Jane Smith",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 55,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 25,
|
||||||
|
"import": "i000009"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "15c4492bd5107",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Locations",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "WORLD",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b3e74dbc1f584",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Earth",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 76,
|
||||||
|
"wordCount": 15,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 20,
|
||||||
|
"import": "i00000a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f1471bef9f2ae",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Space",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 115,
|
||||||
|
"wordCount": 24,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 133,
|
||||||
|
"import": "i000008"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "5eaea4e8cdee8",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Mars",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 28,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 45,
|
||||||
|
"import": "i000009"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "98acd8c76c93a",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Trash",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "TRASH",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b8136a5a774a0",
|
||||||
|
"parent": "98acd8c76c93a",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Delete Me!",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": false,
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 36,
|
||||||
|
"status": "s000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2020-05-28 09:59:15">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<saveCount>0</saveCount>
|
||||||
|
<autoCount>0</autoCount>
|
||||||
|
<editTime>1000</editTime>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<doBackup>True</doBackup>
|
||||||
|
<language>None</language>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<spellLang>None</spellLang>
|
||||||
|
<novelWordCount>0</novelWordCount>
|
||||||
|
<notesWordCount>0</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
|
<autoReplace>
|
||||||
|
<entry key="A">B</entry>
|
||||||
|
<entry key="B">E</entry>
|
||||||
|
<entry key="C">D</entry>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<entry key="title">%title%</entry>
|
||||||
|
<entry key="chapter">Chapter %ch%: %title%</entry>
|
||||||
|
<entry key="unnumbered">%title%</entry>
|
||||||
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
|
<entry key="section"></entry>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||||
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Notes</entry>
|
||||||
|
<entry key="s000002" count="0" red="182" green="60" blue="0">Started</entry>
|
||||||
|
<entry key="s000003" count="0" red="193" green="129" blue="0">1st Draft</entry>
|
||||||
|
<entry key="s000004" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||||
|
<entry key="s000005" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||||
|
<entry key="s000006" count="0" red="58" green="180" blue="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry key="i000007" count="0" red="100" green="100" blue="100">None</entry>
|
||||||
|
<entry key="i000008" count="0" red="0" green="122" blue="188">Minor</entry>
|
||||||
|
<entry key="i000009" count="0" red="21" green="0" blue="180">Major</entry>
|
||||||
|
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="22">
|
||||||
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000002" import="i000007">Novel</name>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="72" wordCount="15" paraCount="2" cursorPos="78"/>
|
||||||
|
<name status="s000002" import="i000007" active="True">Title Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" parent="7031beac91f75" root="None" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="208" wordCount="40" paraCount="2" cursorPos="213"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="None" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="23" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Part One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="None" order="3" type="FOLDER" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000003" import="i000007">A Folder</name>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" parent="e7ded148d6e4a" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="12" wordCount="3" paraCount="0" cursorPos="215"/>
|
||||||
|
<name status="s000001" import="i000007" active="True">Chapter One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" parent="e7ded148d6e4a" root="None" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="1199" wordCount="216" paraCount="7" cursorPos="527"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Making a Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" parent="e7ded148d6e4a" root="None" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="476" wordCount="93" paraCount="3" cursorPos="551"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Another Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" parent="e7ded148d6e4a" root="None" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="633" wordCount="101" paraCount="3" cursorPos="1238"/>
|
||||||
|
<name status="s000006" import="i000007" active="True">Interlude</name>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" parent="e7ded148d6e4a" root="None" order="4" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="1692" wordCount="313" paraCount="6" cursorPos="1721"/>
|
||||||
|
<name status="s000004" import="i000007" active="False">A Note on Structure</name>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" parent="e7ded148d6e4a" root="None" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Chapter Two</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" parent="e7ded148d6e4a" root="None" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">We Found John!</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" parent="f6622b4617424" root="None" order="0" type="FOLDER" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Main Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" parent="f7e2d9f330615" root="None" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||||
|
<name status="s000000" import="i000008" active="True">John Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="None" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||||
|
<name status="s000000" import="i000009" active="True">Jane Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Locations</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="None" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||||
|
<name status="s000000" import="i00000a" active="True">Earth</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="None" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||||
|
<name status="s000000" import="i000008" active="True">Space</name>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="None" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||||
|
<name status="s000000" import="i000009" active="True">Mars</name>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="3" type="ROOT" class="TRASH">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Trash</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="36"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Delete Me!</name>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
@@ -0,0 +1,346 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"handle": "7031beac91f75",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Novel",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "53b69b83cdafc",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Title Page",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 72,
|
||||||
|
"wordCount": 15,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 78,
|
||||||
|
"status": "s000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "974e400180a99",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Page",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 210,
|
||||||
|
"wordCount": 40,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 213,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "edca4be2fcaf8",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Part One",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 23,
|
||||||
|
"wordCount": 5,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 0,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "e7ded148d6e4a",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "A Folder",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "6a2d6d5f4f401",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Chapter One",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 12,
|
||||||
|
"wordCount": 3,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 215,
|
||||||
|
"status": "s000001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "636b6aa9b697b",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Making a Scene",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 1483,
|
||||||
|
"wordCount": 263,
|
||||||
|
"paraCount": 8,
|
||||||
|
"cursorPos": 1086,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bc0cbd2a407f3",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Another Scene",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 476,
|
||||||
|
"wordCount": 93,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 428,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ba8a28a246524",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Interlude",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 633,
|
||||||
|
"wordCount": 101,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 1238,
|
||||||
|
"status": "s000006"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "96b68994dfa3d",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 4,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "A Note on Structure",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": false,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 1692,
|
||||||
|
"wordCount": 313,
|
||||||
|
"paraCount": 6,
|
||||||
|
"cursorPos": 1721,
|
||||||
|
"status": "s000004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "88706ddc78b1b",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 5,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Chapter Two",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 139,
|
||||||
|
"wordCount": 28,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 343,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ae7339df26ded",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 6,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "We Found John!",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 189,
|
||||||
|
"wordCount": 37,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 224,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f6622b4617424",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Characters",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f7e2d9f330615",
|
||||||
|
"parent": "f6622b4617424",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Main Characters",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "14298de4d9524",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "John Smith",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 49,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 24,
|
||||||
|
"import": "i000008"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bb2c23b3c42cc",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Jane Smith",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 55,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 25,
|
||||||
|
"import": "i000009"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "15c4492bd5107",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Locations",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "WORLD",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b3e74dbc1f584",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Earth",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 76,
|
||||||
|
"wordCount": 15,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 20,
|
||||||
|
"import": "i00000a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f1471bef9f2ae",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Space",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 115,
|
||||||
|
"wordCount": 24,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 133,
|
||||||
|
"import": "i000008"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "5eaea4e8cdee8",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Mars",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 28,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 45,
|
||||||
|
"import": "i000009"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "98acd8c76c93a",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Trash",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "TRASH",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b8136a5a774a0",
|
||||||
|
"parent": "98acd8c76c93a",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Delete Me!",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 30,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 36,
|
||||||
|
"status": "s000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2020-06-26 21:20:24">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<saveCount>5</saveCount>
|
||||||
|
<autoCount>10</autoCount>
|
||||||
|
<editTime>1000</editTime>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<doBackup>True</doBackup>
|
||||||
|
<language>None</language>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<spellLang>None</spellLang>
|
||||||
|
<novelWordCount>0</novelWordCount>
|
||||||
|
<notesWordCount>0</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
|
<autoReplace>
|
||||||
|
<entry key="A">B</entry>
|
||||||
|
<entry key="B">E</entry>
|
||||||
|
<entry key="C">D</entry>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<entry key="title">%title%</entry>
|
||||||
|
<entry key="chapter">Chapter %ch%: %title%</entry>
|
||||||
|
<entry key="unnumbered">%title%</entry>
|
||||||
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
|
<entry key="section"></entry>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||||
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Notes</entry>
|
||||||
|
<entry key="s000002" count="0" red="182" green="60" blue="0">Started</entry>
|
||||||
|
<entry key="s000003" count="0" red="193" green="129" blue="0">1st Draft</entry>
|
||||||
|
<entry key="s000004" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||||
|
<entry key="s000005" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||||
|
<entry key="s000006" count="0" red="58" green="180" blue="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry key="i000007" count="0" red="100" green="100" blue="100">None</entry>
|
||||||
|
<entry key="i000008" count="0" red="0" green="122" blue="188">Minor</entry>
|
||||||
|
<entry key="i000009" count="0" red="21" green="0" blue="180">Major</entry>
|
||||||
|
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="22">
|
||||||
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000002" import="i000007">Novel</name>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="72" wordCount="15" paraCount="2" cursorPos="78"/>
|
||||||
|
<name status="s000002" import="i000007" active="True">Title Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" parent="7031beac91f75" root="None" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="210" wordCount="40" paraCount="2" cursorPos="213"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="None" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="23" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Part One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="None" order="3" type="FOLDER" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000003" import="i000007">A Folder</name>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" parent="e7ded148d6e4a" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="12" wordCount="3" paraCount="0" cursorPos="215"/>
|
||||||
|
<name status="s000001" import="i000007" active="True">Chapter One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" parent="e7ded148d6e4a" root="None" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="1483" wordCount="263" paraCount="8" cursorPos="1086"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Making a Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" parent="e7ded148d6e4a" root="None" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="476" wordCount="93" paraCount="3" cursorPos="428"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Another Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" parent="e7ded148d6e4a" root="None" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="633" wordCount="101" paraCount="3" cursorPos="1238"/>
|
||||||
|
<name status="s000006" import="i000007" active="True">Interlude</name>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" parent="e7ded148d6e4a" root="None" order="4" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="1692" wordCount="313" paraCount="6" cursorPos="1721"/>
|
||||||
|
<name status="s000004" import="i000007" active="False">A Note on Structure</name>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" parent="e7ded148d6e4a" root="None" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Chapter Two</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" parent="e7ded148d6e4a" root="None" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">We Found John!</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" parent="f6622b4617424" root="None" order="0" type="FOLDER" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Main Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" parent="f7e2d9f330615" root="None" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||||
|
<name status="s000000" import="i000008" active="True">John Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="None" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||||
|
<name status="s000000" import="i000009" active="True">Jane Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Locations</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="None" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||||
|
<name status="s000000" import="i00000a" active="True">Earth</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="None" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||||
|
<name status="s000000" import="i000008" active="True">Space</name>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="None" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||||
|
<name status="s000000" import="i000009" active="True">Mars</name>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="3" type="ROOT" class="TRASH">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Trash</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Delete Me!</name>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"handle": "7031beac91f75",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Novel",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "53b69b83cdafc",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Title Page",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 241,
|
||||||
|
"wordCount": 42,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 252,
|
||||||
|
"status": "s000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "974e400180a99",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Page",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 125,
|
||||||
|
"wordCount": 26,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 127,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "edca4be2fcaf8",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Part One",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 26,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 30,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "e7ded148d6e4a",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "A Folder",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "6a2d6d5f4f401",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Chapter One",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 75,
|
||||||
|
"wordCount": 14,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 279,
|
||||||
|
"status": "s000001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "636b6aa9b697b",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Making a Scene",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 2429,
|
||||||
|
"wordCount": 432,
|
||||||
|
"paraCount": 14,
|
||||||
|
"cursorPos": 61,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bc0cbd2a407f3",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Another Scene",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 476,
|
||||||
|
"wordCount": 93,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 577,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ba8a28a246524",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Interlude",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 617,
|
||||||
|
"wordCount": 101,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 1137,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "96b68994dfa3d",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 4,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "A Note on Structure",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": false,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 1692,
|
||||||
|
"wordCount": 313,
|
||||||
|
"paraCount": 6,
|
||||||
|
"cursorPos": 1110,
|
||||||
|
"status": "s000004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "88706ddc78b1b",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 5,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Chapter Two",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 139,
|
||||||
|
"wordCount": 28,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 343,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ae7339df26ded",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 6,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "We Found John!",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 189,
|
||||||
|
"wordCount": 37,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 224,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f6622b4617424",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Characters",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f7e2d9f330615",
|
||||||
|
"parent": "f6622b4617424",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Main Characters",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "14298de4d9524",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "John Smith",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 49,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 24,
|
||||||
|
"import": "i000008"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bb2c23b3c42cc",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Jane Smith",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 55,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 25,
|
||||||
|
"import": "i000009"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "15c4492bd5107",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Locations",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "WORLD",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b3e74dbc1f584",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Earth",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 76,
|
||||||
|
"wordCount": 15,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 20,
|
||||||
|
"import": "i00000a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f1471bef9f2ae",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Space",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 115,
|
||||||
|
"wordCount": 24,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 133,
|
||||||
|
"import": "i000008"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "5eaea4e8cdee8",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Mars",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 28,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 45,
|
||||||
|
"import": "i000009"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "6827118336ac1",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Outtakes",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "ARCHIVE",
|
||||||
|
"expanded": true,
|
||||||
|
"status": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ae9bf3c3ea159",
|
||||||
|
"parent": "6827118336ac1",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Scenes",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "ARCHIVE",
|
||||||
|
"expanded": true,
|
||||||
|
"status": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "8a5deb88c0e97",
|
||||||
|
"parent": "ae9bf3c3ea159",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Old File",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 315,
|
||||||
|
"wordCount": 55,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 322,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "98acd8c76c93a",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 4,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Trash",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "TRASH",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b8136a5a774a0",
|
||||||
|
"parent": "98acd8c76c93a",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Delete Me!",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 30,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 36,
|
||||||
|
"status": "s000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2021-08-30 23:33:44">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<saveCount>5</saveCount>
|
||||||
|
<autoCount>10</autoCount>
|
||||||
|
<editTime>1000</editTime>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<doBackup>True</doBackup>
|
||||||
|
<language>en_GB</language>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<spellLang>en_GB</spellLang>
|
||||||
|
<novelWordCount>840</novelWordCount>
|
||||||
|
<notesWordCount>376</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
|
<autoReplace>
|
||||||
|
<entry key="A">B</entry>
|
||||||
|
<entry key="B">E</entry>
|
||||||
|
<entry key="C">D</entry>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<entry key="title">%title%</entry>
|
||||||
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
|
<entry key="unnumbered">%title%</entry>
|
||||||
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
|
<entry key="section"></entry>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||||
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Notes</entry>
|
||||||
|
<entry key="s000002" count="0" red="182" green="60" blue="0">Started</entry>
|
||||||
|
<entry key="s000003" count="0" red="193" green="129" blue="0">1st Draft</entry>
|
||||||
|
<entry key="s000004" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||||
|
<entry key="s000005" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||||
|
<entry key="s000006" count="0" red="58" green="180" blue="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry key="i000007" count="0" red="100" green="100" blue="100">None</entry>
|
||||||
|
<entry key="i000008" count="0" red="0" green="122" blue="188">Minor</entry>
|
||||||
|
<entry key="i000009" count="0" red="21" green="0" blue="180">Major</entry>
|
||||||
|
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="25">
|
||||||
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000002" import="i000007">Novel</name>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="241" wordCount="42" paraCount="3" cursorPos="252"/>
|
||||||
|
<name status="s000002" import="i000007" active="True">Title Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" parent="7031beac91f75" root="None" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="125" wordCount="26" paraCount="2" cursorPos="127"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="None" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="26" wordCount="6" paraCount="1" cursorPos="30"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Part One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="None" order="3" type="FOLDER" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000003" import="i000007">A Folder</name>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" parent="e7ded148d6e4a" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="75" wordCount="14" paraCount="1" cursorPos="279"/>
|
||||||
|
<name status="s000001" import="i000007" active="True">Chapter One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" parent="e7ded148d6e4a" root="None" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="2429" wordCount="432" paraCount="14" cursorPos="61"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Making a Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" parent="e7ded148d6e4a" root="None" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="476" wordCount="93" paraCount="3" cursorPos="577"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Another Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" parent="e7ded148d6e4a" root="None" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="617" wordCount="101" paraCount="3" cursorPos="1137"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Interlude</name>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" parent="e7ded148d6e4a" root="None" order="4" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="1692" wordCount="313" paraCount="6" cursorPos="1110"/>
|
||||||
|
<name status="s000004" import="i000007" active="False">A Note on Structure</name>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" parent="e7ded148d6e4a" root="None" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Chapter Two</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" parent="e7ded148d6e4a" root="None" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">We Found John!</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" parent="f6622b4617424" root="None" order="0" type="FOLDER" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Main Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" parent="f7e2d9f330615" root="None" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||||
|
<name status="s000000" import="i000008" active="True">John Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="None" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||||
|
<name status="s000000" import="i000009" active="True">Jane Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Locations</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="None" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||||
|
<name status="s000000" import="i00000a" active="True">Earth</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="None" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||||
|
<name status="s000000" import="i000008" active="True">Space</name>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="None" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||||
|
<name status="s000000" import="i000009" active="True">Mars</name>
|
||||||
|
</item>
|
||||||
|
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="3" type="ROOT" class="ARCHIVE">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Outtakes</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ae9bf3c3ea159" parent="6827118336ac1" root="None" order="0" type="FOLDER" class="ARCHIVE">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Scenes</name>
|
||||||
|
</item>
|
||||||
|
<item handle="8a5deb88c0e97" parent="ae9bf3c3ea159" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="315" wordCount="55" paraCount="1" cursorPos="322"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Old File</name>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="4" type="ROOT" class="TRASH">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Trash</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Delete Me!</name>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"handle": "7031beac91f75",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Novel",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "53b69b83cdafc",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Title Page",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 93,
|
||||||
|
"wordCount": 19,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 2,
|
||||||
|
"status": "s000002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "974e400180a99",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Page",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 186,
|
||||||
|
"wordCount": 39,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 212,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "edca4be2fcaf8",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Part One",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 26,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 33,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "e7ded148d6e4a",
|
||||||
|
"parent": "7031beac91f75",
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "A Folder",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "6a2d6d5f4f401",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Chapter One",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 75,
|
||||||
|
"wordCount": 14,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 279,
|
||||||
|
"status": "s000001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "636b6aa9b697b",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Making a Scene",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 2429,
|
||||||
|
"wordCount": 432,
|
||||||
|
"paraCount": 14,
|
||||||
|
"cursorPos": 62,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bc0cbd2a407f3",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Another Scene",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 476,
|
||||||
|
"wordCount": 93,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 577,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ba8a28a246524",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Interlude",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 617,
|
||||||
|
"wordCount": 101,
|
||||||
|
"paraCount": 3,
|
||||||
|
"cursorPos": 4,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "96b68994dfa3d",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 4,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "A Note on Structure",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": false,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 1692,
|
||||||
|
"wordCount": 313,
|
||||||
|
"paraCount": 6,
|
||||||
|
"cursorPos": 1110,
|
||||||
|
"status": "s000004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "88706ddc78b1b",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 5,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Chapter Two",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 139,
|
||||||
|
"wordCount": 28,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 343,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ae7339df26ded",
|
||||||
|
"parent": "e7ded148d6e4a",
|
||||||
|
"root": null,
|
||||||
|
"order": 6,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "We Found John!",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 189,
|
||||||
|
"wordCount": 37,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 224,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f6622b4617424",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Characters",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f7e2d9f330615",
|
||||||
|
"parent": "f6622b4617424",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Main Characters",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "14298de4d9524",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "John Smith",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 49,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 24,
|
||||||
|
"import": "i000008"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "bb2c23b3c42cc",
|
||||||
|
"parent": "f7e2d9f330615",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Jane Smith",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "CHARACTER",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 55,
|
||||||
|
"wordCount": 9,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 25,
|
||||||
|
"import": "i000009"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "15c4492bd5107",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Locations",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "WORLD",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b3e74dbc1f584",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Earth",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 76,
|
||||||
|
"wordCount": 15,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 20,
|
||||||
|
"import": "i00000a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "f1471bef9f2ae",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 1,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Space",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 115,
|
||||||
|
"wordCount": 24,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 133,
|
||||||
|
"import": "i000008"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "5eaea4e8cdee8",
|
||||||
|
"parent": "15c4492bd5107",
|
||||||
|
"root": null,
|
||||||
|
"order": 2,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Mars",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "WORLD",
|
||||||
|
"active": true,
|
||||||
|
"layout": "NOTE",
|
||||||
|
"charCount": 28,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 45,
|
||||||
|
"import": "i000009"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "6827118336ac1",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 3,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Archive",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "ARCHIVE",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "ae9bf3c3ea159",
|
||||||
|
"parent": "6827118336ac1",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Scenes",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "ARCHIVE",
|
||||||
|
"expanded": true,
|
||||||
|
"status": "s000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "8a5deb88c0e97",
|
||||||
|
"parent": "ae9bf3c3ea159",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Old File",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 314,
|
||||||
|
"wordCount": 55,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 322,
|
||||||
|
"status": "s000003"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "98acd8c76c93a",
|
||||||
|
"parent": null,
|
||||||
|
"root": null,
|
||||||
|
"order": 4,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Trash",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "TRASH",
|
||||||
|
"expanded": true,
|
||||||
|
"import": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"handle": "b8136a5a774a0",
|
||||||
|
"parent": "98acd8c76c93a",
|
||||||
|
"root": null,
|
||||||
|
"order": 0,
|
||||||
|
"heading": "H0",
|
||||||
|
"label": "Delete Me!",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"active": true,
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"charCount": 30,
|
||||||
|
"wordCount": 6,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 36,
|
||||||
|
"status": "s000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-25 18:26:15">
|
||||||
|
<project>
|
||||||
|
<name>Sample Project</name>
|
||||||
|
<title>Sample Project</title>
|
||||||
|
<author>Jane Smith</author>
|
||||||
|
<author>Jay Doh</author>
|
||||||
|
<saveCount>5</saveCount>
|
||||||
|
<autoCount>10</autoCount>
|
||||||
|
<editTime>1000</editTime>
|
||||||
|
</project>
|
||||||
|
<settings>
|
||||||
|
<doBackup>True</doBackup>
|
||||||
|
<language>en_GB</language>
|
||||||
|
<spellCheck>True</spellCheck>
|
||||||
|
<spellLang>en_GB</spellLang>
|
||||||
|
<novelWordCount>830</novelWordCount>
|
||||||
|
<notesWordCount>376</notesWordCount>
|
||||||
|
<lastHandle>
|
||||||
|
<entry key="editor">None</entry>
|
||||||
|
<entry key="viewer">None</entry>
|
||||||
|
<entry key="novelTree">None</entry>
|
||||||
|
<entry key="outline">None</entry>
|
||||||
|
</lastHandle>
|
||||||
|
<autoReplace>
|
||||||
|
<entry key="A">B</entry>
|
||||||
|
<entry key="B">E</entry>
|
||||||
|
<entry key="C">D</entry>
|
||||||
|
</autoReplace>
|
||||||
|
<titleFormat>
|
||||||
|
<entry key="title">%title%</entry>
|
||||||
|
<entry key="chapter">Chapter %chw%: %title%</entry>
|
||||||
|
<entry key="unnumbered">%title%</entry>
|
||||||
|
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
||||||
|
<entry key="section"></entry>
|
||||||
|
</titleFormat>
|
||||||
|
<status>
|
||||||
|
<entry key="s000000" count="0" red="100" green="100" blue="100">New</entry>
|
||||||
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Notes</entry>
|
||||||
|
<entry key="s000002" count="0" red="182" green="60" blue="0">Started</entry>
|
||||||
|
<entry key="s000003" count="0" red="193" green="129" blue="0">1st Draft</entry>
|
||||||
|
<entry key="s000004" count="0" red="193" green="129" blue="0">2nd Draft</entry>
|
||||||
|
<entry key="s000005" count="0" red="193" green="129" blue="0">3rd Draft</entry>
|
||||||
|
<entry key="s000006" count="0" red="58" green="180" blue="58">Finished</entry>
|
||||||
|
</status>
|
||||||
|
<importance>
|
||||||
|
<entry key="i000007" count="0" red="100" green="100" blue="100">None</entry>
|
||||||
|
<entry key="i000008" count="0" red="0" green="122" blue="188">Minor</entry>
|
||||||
|
<entry key="i000009" count="0" red="21" green="0" blue="180">Major</entry>
|
||||||
|
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||||
|
</importance>
|
||||||
|
</settings>
|
||||||
|
<content count="25">
|
||||||
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000002" import="i000007">Novel</name>
|
||||||
|
</item>
|
||||||
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="93" wordCount="19" paraCount="2" cursorPos="2"/>
|
||||||
|
<name status="s000002" import="i000007" active="True">Title Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="974e400180a99" parent="7031beac91f75" root="None" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="186" wordCount="39" paraCount="2" cursorPos="212"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Page</name>
|
||||||
|
</item>
|
||||||
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="None" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="26" wordCount="6" paraCount="1" cursorPos="33"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Part One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="None" order="3" type="FOLDER" class="NOVEL">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000003" import="i000007">A Folder</name>
|
||||||
|
</item>
|
||||||
|
<item handle="6a2d6d5f4f401" parent="e7ded148d6e4a" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="75" wordCount="14" paraCount="1" cursorPos="279"/>
|
||||||
|
<name status="s000001" import="i000007" active="True">Chapter One</name>
|
||||||
|
</item>
|
||||||
|
<item handle="636b6aa9b697b" parent="e7ded148d6e4a" root="None" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="2429" wordCount="432" paraCount="14" cursorPos="62"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Making a Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bc0cbd2a407f3" parent="e7ded148d6e4a" root="None" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="476" wordCount="93" paraCount="3" cursorPos="577"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Another Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ba8a28a246524" parent="e7ded148d6e4a" root="None" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="617" wordCount="101" paraCount="3" cursorPos="4"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Interlude</name>
|
||||||
|
</item>
|
||||||
|
<item handle="96b68994dfa3d" parent="e7ded148d6e4a" root="None" order="4" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="1692" wordCount="313" paraCount="6" cursorPos="1110"/>
|
||||||
|
<name status="s000004" import="i000007" active="False">A Note on Structure</name>
|
||||||
|
</item>
|
||||||
|
<item handle="88706ddc78b1b" parent="e7ded148d6e4a" root="None" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Chapter Two</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ae7339df26ded" parent="e7ded148d6e4a" root="None" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">We Found John!</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f7e2d9f330615" parent="f6622b4617424" root="None" order="0" type="FOLDER" class="CHARACTER">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Main Characters</name>
|
||||||
|
</item>
|
||||||
|
<item handle="14298de4d9524" parent="f7e2d9f330615" root="None" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||||
|
<name status="s000000" import="i000008" active="True">John Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="None" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||||
|
<name status="s000000" import="i000009" active="True">Jane Smith</name>
|
||||||
|
</item>
|
||||||
|
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Locations</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="None" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||||
|
<name status="s000000" import="i00000a" active="True">Earth</name>
|
||||||
|
</item>
|
||||||
|
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="None" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||||
|
<name status="s000000" import="i000008" active="True">Space</name>
|
||||||
|
</item>
|
||||||
|
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="None" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||||
|
<meta expanded="False" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||||
|
<name status="s000000" import="i000009" active="True">Mars</name>
|
||||||
|
</item>
|
||||||
|
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="3" type="ROOT" class="ARCHIVE">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Archive</name>
|
||||||
|
</item>
|
||||||
|
<item handle="ae9bf3c3ea159" parent="6827118336ac1" root="None" order="0" type="FOLDER" class="ARCHIVE">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Scenes</name>
|
||||||
|
</item>
|
||||||
|
<item handle="8a5deb88c0e97" parent="ae9bf3c3ea159" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="314" wordCount="55" paraCount="1" cursorPos="322"/>
|
||||||
|
<name status="s000003" import="i000007" active="True">Old File</name>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="4" type="ROOT" class="TRASH">
|
||||||
|
<meta expanded="True"/>
|
||||||
|
<name status="s000000" import="i000007">Trash</name>
|
||||||
|
</item>
|
||||||
|
<item handle="b8136a5a774a0" parent="98acd8c76c93a" root="None" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
<meta expanded="False" heading="H0" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||||
|
<name status="s000000" import="i000007" active="True">Delete Me!</name>
|
||||||
|
</item>
|
||||||
|
</content>
|
||||||
|
</novelWriterXML>
|
||||||
@@ -54,53 +54,63 @@ def testBaseCommon_CheckStringNone():
|
|||||||
|
|
||||||
@pytest.mark.base
|
@pytest.mark.base
|
||||||
def testBaseCommon_CheckString():
|
def testBaseCommon_CheckString():
|
||||||
"""Test the checkString function.
|
"""Test the checkString function. Anything that is a string should
|
||||||
|
be returned, otherwise it returns the default.
|
||||||
"""
|
"""
|
||||||
assert checkString("None", "NotNone") == "None"
|
assert checkString("None", "default") == "None"
|
||||||
assert checkString(None, "NotNone") == "NotNone"
|
assert checkString("Text", "default") == "Text"
|
||||||
assert checkString(1, "NotNone") == "NotNone"
|
assert checkString(None, "default") == "default"
|
||||||
assert checkString(1.0, "NotNone") == "NotNone"
|
assert checkString(1, "default") == "default"
|
||||||
assert checkString(True, "NotNone") == "NotNone"
|
assert checkString(1.0, "default") == "default"
|
||||||
|
assert checkString(True, "default") == "default"
|
||||||
|
|
||||||
# END Test testBaseCommon_CheckString
|
# END Test testBaseCommon_CheckString
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.base
|
@pytest.mark.base
|
||||||
def testBaseCommon_CheckInt():
|
def testBaseCommon_CheckInt():
|
||||||
"""Test the checkInt function.
|
"""Test the checkInt function. Anything that can be converted to an
|
||||||
|
integer should be returned, otherwise it returns the default.
|
||||||
"""
|
"""
|
||||||
assert checkInt(None, 3) == 3
|
|
||||||
assert checkInt("1", 3) == 1
|
|
||||||
assert checkInt("1.0", 3) == 3
|
|
||||||
assert checkInt(1, 3) == 1
|
assert checkInt(1, 3) == 1
|
||||||
assert checkInt(1.0, 3) == 1
|
assert checkInt(1.0, 3) == 1
|
||||||
assert checkInt(True, 3) == 1
|
assert checkInt(True, 3) == 1
|
||||||
|
assert checkInt(False, 3) == 0
|
||||||
|
assert checkInt(None, 3) == 3
|
||||||
|
assert checkInt("1", 3) == 1
|
||||||
|
assert checkInt("1.0", 3) == 3
|
||||||
|
|
||||||
# END Test testBaseCommon_CheckInt
|
# END Test testBaseCommon_CheckInt
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.base
|
@pytest.mark.base
|
||||||
def testBaseCommon_CheckFloat():
|
def testBaseCommon_CheckFloat():
|
||||||
"""Test the checkFloat function.
|
"""Test the checkFloat function. Anything that can be converted to an
|
||||||
|
integer should be returned, otherwise it returns the default.
|
||||||
"""
|
"""
|
||||||
assert checkFloat(None, 3.0) == 3.0
|
|
||||||
assert checkFloat("1", 3.0) == 1.0
|
|
||||||
assert checkFloat("1.0", 3.0) == 1.0
|
|
||||||
assert checkFloat(1, 3.0) == 1.0
|
assert checkFloat(1, 3.0) == 1.0
|
||||||
assert checkFloat(1.0, 3.0) == 1.0
|
assert checkFloat(1.0, 3.0) == 1.0
|
||||||
assert checkFloat(True, 3.0) == 1.0
|
assert checkFloat(True, 3.0) == 1.0
|
||||||
|
assert checkFloat(False, 3.0) == 0.0
|
||||||
|
assert checkFloat(None, 3.0) == 3.0
|
||||||
|
assert checkFloat("1", 3.0) == 1.0
|
||||||
|
assert checkFloat("1.0", 3.0) == 1.0
|
||||||
|
|
||||||
# END Test testBaseCommon_CheckInt
|
# END Test testBaseCommon_CheckInt
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.base
|
@pytest.mark.base
|
||||||
def testBaseCommon_CheckBool():
|
def testBaseCommon_CheckBool():
|
||||||
"""Test the checkBool function.
|
"""Test the checkBool function. Any bool, string version of Python
|
||||||
|
bool, or integer 1 or 0, are returned as bool. Otherwise, the
|
||||||
|
default is returned.
|
||||||
"""
|
"""
|
||||||
assert checkBool("True", False) is True
|
assert checkBool("True", False) is True
|
||||||
assert checkBool("False", True) is False
|
assert checkBool("False", True) is False
|
||||||
assert checkBool("Boo", False) is False
|
assert checkBool("Boo", False) is False
|
||||||
assert checkBool("Boo", True) is True
|
assert checkBool("Boo", True) is True
|
||||||
|
assert checkBool(None, True) is True
|
||||||
|
assert checkBool(None, False) is False
|
||||||
assert checkBool(0, True) is False
|
assert checkBool(0, True) is False
|
||||||
assert checkBool(1, False) is True
|
assert checkBool(1, False) is True
|
||||||
assert checkBool(2, True) is True
|
assert checkBool(2, True) is True
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
from tools import readFile, writeFile
|
from tools import C, buildTestProject, readFile, writeFile
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
@@ -31,14 +31,12 @@ from novelwriter.core.document import NWDoc
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
|
def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||||
"""Test loading and saving a document with the NWDoc class.
|
"""Test loading and saving a document with the NWDoc class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
assert theProject.openProject(nwMinimal) is True
|
mockRnd.reset()
|
||||||
assert theProject.projPath == nwMinimal
|
buildTestProject(theProject, fncDir)
|
||||||
|
|
||||||
sHandle = "8c659a11cd429"
|
|
||||||
|
|
||||||
# Read Document
|
# Read Document
|
||||||
# =============
|
# =============
|
||||||
@@ -49,25 +47,23 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
|
|||||||
assert theDoc.readDocument() is None
|
assert theDoc.readDocument() is None
|
||||||
|
|
||||||
# Non-existent handle
|
# Non-existent handle
|
||||||
theDoc = NWDoc(theProject, "0000000000000")
|
theDoc = NWDoc(theProject, C.hInvalid)
|
||||||
assert theDoc.readDocument() is None
|
assert theDoc.readDocument() is None
|
||||||
assert theDoc._currHash is None
|
assert theDoc._currHash is None
|
||||||
|
|
||||||
# Cause open() to fail while loading
|
# Cause open() to fail while loading
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("builtins.open", causeOSError)
|
mp.setattr("builtins.open", causeOSError)
|
||||||
theDoc = NWDoc(theProject, sHandle)
|
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||||
assert theDoc.readDocument() is None
|
assert theDoc.readDocument() is None
|
||||||
assert theDoc.getError() == "OSError: Mock OSError"
|
assert theDoc.getError() == "OSError: Mock OSError"
|
||||||
|
|
||||||
# Load the text
|
# Load the text
|
||||||
theDoc = NWDoc(theProject, sHandle)
|
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||||
assert theDoc.readDocument() == "### New Scene\n\n"
|
assert theDoc.readDocument() == "### New Scene\n\n"
|
||||||
|
|
||||||
# Try to open a new (non-existent) file
|
# Try to open a new (non-existent) file
|
||||||
nHandle = theProject.tree.findRoot(nwItemClass.NOVEL)
|
xHandle = theProject.newFile("New File", C.hNovelRoot)
|
||||||
assert nHandle is not None
|
|
||||||
xHandle = theProject.newFile("New File", nHandle)
|
|
||||||
theDoc = NWDoc(theProject, xHandle)
|
theDoc = NWDoc(theProject, xHandle)
|
||||||
assert bool(theDoc) is True
|
assert bool(theDoc) is True
|
||||||
assert repr(theDoc) == f"<NWDoc handle={xHandle}>"
|
assert repr(theDoc) == f"<NWDoc handle={xHandle}>"
|
||||||
@@ -86,10 +82,10 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
|
|||||||
assert theDoc.writeDocument(theText)
|
assert theDoc.writeDocument(theText)
|
||||||
|
|
||||||
# Check file content
|
# Check file content
|
||||||
docPath = os.path.join(nwMinimal, "content", xHandle+".nwd")
|
docPath = os.path.join(fncDir, "content", xHandle+".nwd")
|
||||||
assert readFile(docPath) == (
|
assert readFile(docPath) == (
|
||||||
"%%~name: New File\n"
|
"%%~name: New File\n"
|
||||||
f"%%~path: a508bb932959c/{xHandle}\n"
|
f"%%~path: {C.hNovelRoot}/{xHandle}\n"
|
||||||
"%%~kind: NOVEL/DOCUMENT\n"
|
"%%~kind: NOVEL/DOCUMENT\n"
|
||||||
"### Test File\n\n"
|
"### Test File\n\n"
|
||||||
"Text ...\n\n"
|
"Text ...\n\n"
|
||||||
@@ -153,16 +149,15 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreDocument_Methods(mockGUI, nwMinimal):
|
def testCoreDocument_Methods(mockGUI, fncDir, mockRnd):
|
||||||
"""Test other methods of the NWDoc class.
|
"""Test other methods of the NWDoc class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
assert theProject.openProject(nwMinimal)
|
mockRnd.reset()
|
||||||
assert theProject.projPath == nwMinimal
|
buildTestProject(theProject, fncDir)
|
||||||
|
|
||||||
sHandle = "8c659a11cd429"
|
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||||
theDoc = NWDoc(theProject, sHandle)
|
docPath = os.path.join(fncDir, "content", C.hSceneDoc+".nwd")
|
||||||
docPath = os.path.join(nwMinimal, "content", sHandle+".nwd")
|
|
||||||
|
|
||||||
assert theDoc.readDocument() == "### New Scene\n\n"
|
assert theDoc.readDocument() == "### New Scene\n\n"
|
||||||
|
|
||||||
@@ -171,12 +166,12 @@ def testCoreDocument_Methods(mockGUI, nwMinimal):
|
|||||||
|
|
||||||
# Check the item
|
# Check the item
|
||||||
assert theDoc.getCurrentItem() is not None
|
assert theDoc.getCurrentItem() is not None
|
||||||
assert theDoc.getCurrentItem().itemHandle == sHandle
|
assert theDoc.getCurrentItem().itemHandle == C.hSceneDoc
|
||||||
|
|
||||||
# Check the meta
|
# Check the meta
|
||||||
theName, theParent, theClass, theLayout = theDoc.getMeta()
|
theName, theParent, theClass, theLayout = theDoc.getMeta()
|
||||||
assert theName == "New Scene"
|
assert theName == "New Scene"
|
||||||
assert theParent == "a6d311a93600a"
|
assert theParent == C.hChapterDir
|
||||||
assert theClass == nwItemClass.NOVEL
|
assert theClass == nwItemClass.NOVEL
|
||||||
assert theLayout == nwItemLayout.DOCUMENT
|
assert theLayout == nwItemLayout.DOCUMENT
|
||||||
|
|
||||||
@@ -184,7 +179,7 @@ def testCoreDocument_Methods(mockGUI, nwMinimal):
|
|||||||
assert theDoc.writeDocument("%%~ stuff\n### Test File\n\nText ...\n\n")
|
assert theDoc.writeDocument("%%~ stuff\n### Test File\n\nText ...\n\n")
|
||||||
assert readFile(docPath) == (
|
assert readFile(docPath) == (
|
||||||
"%%~name: New Scene\n"
|
"%%~name: New Scene\n"
|
||||||
f"%%~path: a6d311a93600a/{sHandle}\n"
|
f"%%~path: {C.hChapterDir}/{C.hSceneDoc}\n"
|
||||||
"%%~kind: NOVEL/DOCUMENT\n"
|
"%%~kind: NOVEL/DOCUMENT\n"
|
||||||
"%%~ stuff\n"
|
"%%~ stuff\n"
|
||||||
"### Test File\n\n"
|
"### Test File\n\n"
|
||||||
|
|||||||
+197
-248
@@ -21,20 +21,22 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
|
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
|
|
||||||
|
from tools import C, buildTestProject
|
||||||
|
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_Setters(mockGUI, mockRnd):
|
def testCoreItem_Setters(mockGUI, mockRnd, fncDir):
|
||||||
"""Test all the simple setters for the NWItem class.
|
"""Test all the simple setters for the NWItem class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
mockRnd.reset()
|
||||||
|
buildTestProject(theProject, fncDir)
|
||||||
theItem = NWItem(theProject)
|
theItem = NWItem(theProject)
|
||||||
|
|
||||||
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
|
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
|
||||||
@@ -153,7 +155,7 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
|||||||
theItem.setCharCount(None)
|
theItem.setCharCount(None)
|
||||||
assert theItem.charCount == 0
|
assert theItem.charCount == 0
|
||||||
theItem.setCharCount("1")
|
theItem.setCharCount("1")
|
||||||
assert theItem.charCount == 1
|
assert theItem.charCount == 0
|
||||||
theItem.setCharCount(1)
|
theItem.setCharCount(1)
|
||||||
assert theItem.charCount == 1
|
assert theItem.charCount == 1
|
||||||
|
|
||||||
@@ -161,7 +163,7 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
|||||||
theItem.setWordCount(None)
|
theItem.setWordCount(None)
|
||||||
assert theItem.wordCount == 0
|
assert theItem.wordCount == 0
|
||||||
theItem.setWordCount("1")
|
theItem.setWordCount("1")
|
||||||
assert theItem.wordCount == 1
|
assert theItem.wordCount == 0
|
||||||
theItem.setWordCount(1)
|
theItem.setWordCount(1)
|
||||||
assert theItem.wordCount == 1
|
assert theItem.wordCount == 1
|
||||||
|
|
||||||
@@ -169,7 +171,7 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
|||||||
theItem.setParaCount(None)
|
theItem.setParaCount(None)
|
||||||
assert theItem.paraCount == 0
|
assert theItem.paraCount == 0
|
||||||
theItem.setParaCount("1")
|
theItem.setParaCount("1")
|
||||||
assert theItem.paraCount == 1
|
assert theItem.paraCount == 0
|
||||||
theItem.setParaCount(1)
|
theItem.setParaCount(1)
|
||||||
assert theItem.paraCount == 1
|
assert theItem.paraCount == 1
|
||||||
|
|
||||||
@@ -177,7 +179,7 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
|||||||
theItem.setCursorPos(None)
|
theItem.setCursorPos(None)
|
||||||
assert theItem.cursorPos == 0
|
assert theItem.cursorPos == 0
|
||||||
theItem.setCursorPos("1")
|
theItem.setCursorPos("1")
|
||||||
assert theItem.cursorPos == 1
|
assert theItem.cursorPos == 0
|
||||||
theItem.setCursorPos(1)
|
theItem.setCursorPos(1)
|
||||||
assert theItem.cursorPos == 1
|
assert theItem.cursorPos == 1
|
||||||
|
|
||||||
@@ -190,10 +192,12 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_Methods(mockGUI):
|
def testCoreItem_Methods(mockGUI, mockRnd, fncDir):
|
||||||
"""Test the simple methods of the NWItem class.
|
"""Test the simple methods of the NWItem class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
mockRnd.reset()
|
||||||
|
buildTestProject(theProject, fncDir)
|
||||||
theItem = NWItem(theProject)
|
theItem = NWItem(theProject)
|
||||||
|
|
||||||
# Describe Me
|
# Describe Me
|
||||||
@@ -250,15 +254,15 @@ def testCoreItem_Methods(mockGUI):
|
|||||||
# =============
|
# =============
|
||||||
|
|
||||||
theItem.setType("FILE")
|
theItem.setType("FILE")
|
||||||
theItem.setStatus("Note")
|
theItem.setStatus(C.sNote)
|
||||||
theItem.setImport("Minor")
|
theItem.setImport(C.iMinor)
|
||||||
|
|
||||||
theItem.setClass("NOVEL")
|
theItem.setClass("NOVEL")
|
||||||
stT, stI = theItem.getImportStatus()
|
stT, stI = theItem.getImportStatus()
|
||||||
assert stT == "Note"
|
assert stT == "Note"
|
||||||
assert isinstance(stI, QIcon)
|
assert isinstance(stI, QIcon)
|
||||||
|
|
||||||
theItem.setImportStatus("Draft")
|
theItem.setImportStatus(C.sDraft)
|
||||||
stT, stI = theItem.getImportStatus()
|
stT, stI = theItem.getImportStatus()
|
||||||
assert stT == "Draft"
|
assert stT == "Draft"
|
||||||
|
|
||||||
@@ -267,7 +271,7 @@ def testCoreItem_Methods(mockGUI):
|
|||||||
assert stT == "Minor"
|
assert stT == "Minor"
|
||||||
assert isinstance(stI, QIcon)
|
assert isinstance(stI, QIcon)
|
||||||
|
|
||||||
theItem.setImportStatus("Major")
|
theItem.setImportStatus(C.iMajor)
|
||||||
stT, stI = theItem.getImportStatus()
|
stT, stI = theItem.getImportStatus()
|
||||||
assert stT == "Major"
|
assert stT == "Major"
|
||||||
|
|
||||||
@@ -491,254 +495,199 @@ def testCoreItem_ClassDefaults(mockGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_XMLPackUnpack(mockGUI, caplog, mockRnd):
|
def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||||
"""Test packing and unpacking XML objects for the NWItem class.
|
"""Test packing and unpacking entries for the NWItem class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
nwXML = etree.Element("novelWriterXML")
|
theProject.data.itemStatus.write(None, "New", (100, 100, 100))
|
||||||
|
theProject.data.itemImport.write(None, "New", (100, 100, 100))
|
||||||
|
|
||||||
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
|
# Invalid
|
||||||
importKeys = ["i000004", "i000005", "i000006", "i000007"]
|
theItem = NWItem(theProject)
|
||||||
|
assert theItem.unpack({}) is False
|
||||||
|
|
||||||
# File
|
# File
|
||||||
# ====
|
|
||||||
|
|
||||||
theItem = NWItem(theProject)
|
theItem = NWItem(theProject)
|
||||||
theItem.setHandle("0123456789abc")
|
assert theItem.unpack({
|
||||||
theItem.setParent("0123456789abc")
|
"label": "A File",
|
||||||
theItem.setRoot("0123456789abc")
|
"handle": "0000000000003",
|
||||||
theItem.setOrder(1)
|
"parent": "0000000000002",
|
||||||
theItem.setName("A Name")
|
"root": "0000000000001",
|
||||||
theItem.setClass("NOVEL")
|
"order": 1,
|
||||||
theItem.setType("FILE")
|
"type": "FILE",
|
||||||
theItem.setImport(importKeys[3])
|
"class": "NOVEL",
|
||||||
theItem.setLayout("NOTE")
|
"layout": "DOCUMENT",
|
||||||
theItem.setActive(False)
|
"expanded": True,
|
||||||
theItem.setParaCount(3)
|
"status": None,
|
||||||
theItem.setWordCount(5)
|
"import": None,
|
||||||
theItem.setCharCount(7)
|
"heading": "H1",
|
||||||
theItem.setCursorPos(11)
|
"charCount": 100,
|
||||||
|
"wordCount": 20,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 50,
|
||||||
|
"active": False,
|
||||||
|
}) is True
|
||||||
|
|
||||||
# Pack
|
assert theItem.itemName == "A File"
|
||||||
xContent = etree.SubElement(nwXML, "content")
|
assert theItem.itemHandle == "0000000000003"
|
||||||
theItem.packXML(xContent)
|
assert theItem.itemParent == "0000000000002"
|
||||||
assert etree.tostring(xContent, pretty_print=False, encoding="utf-8") == (
|
assert theItem.itemRoot == "0000000000001"
|
||||||
b'<content>'
|
|
||||||
b'<item handle="0123456789abc" parent="0123456789abc" root="0123456789abc" order="1" '
|
|
||||||
b'type="FILE" class="NOVEL" layout="NOTE"><meta expanded="False" mainHeading="H0" '
|
|
||||||
b'charCount="7" wordCount="5" paraCount="3" cursorPos="11"/><name status="None" '
|
|
||||||
b'import="%s" active="False">A Name</name></item>'
|
|
||||||
b'</content>'
|
|
||||||
) % bytes(importKeys[3], encoding="utf8")
|
|
||||||
|
|
||||||
# Unpack
|
|
||||||
theItem = NWItem(theProject)
|
|
||||||
assert theItem.unpackXML(xContent[0])
|
|
||||||
assert theItem.itemHandle == "0123456789abc"
|
|
||||||
assert theItem.itemParent == "0123456789abc"
|
|
||||||
assert theItem.itemRoot == "0123456789abc"
|
|
||||||
assert theItem.itemOrder == 1
|
assert theItem.itemOrder == 1
|
||||||
|
assert theItem.itemType == nwItemType.FILE
|
||||||
|
assert theItem.itemClass == nwItemClass.NOVEL
|
||||||
|
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
||||||
|
assert theItem.itemStatus == "s000000"
|
||||||
|
assert theItem.itemImport == "i000001"
|
||||||
assert theItem.isActive is False
|
assert theItem.isActive is False
|
||||||
assert theItem.paraCount == 3
|
|
||||||
assert theItem.wordCount == 5
|
|
||||||
assert theItem.charCount == 7
|
|
||||||
assert theItem.cursorPos == 11
|
|
||||||
assert theItem.itemClass == nwItemClass.NOVEL
|
|
||||||
assert theItem.itemType == nwItemType.FILE
|
|
||||||
assert theItem.itemLayout == nwItemLayout.NOTE
|
|
||||||
assert theItem.itemStatus == statusKeys[0] # Was None, should now be default
|
|
||||||
assert theItem.itemImport == importKeys[3]
|
|
||||||
|
|
||||||
# Folder
|
|
||||||
# ======
|
|
||||||
|
|
||||||
theItem = NWItem(theProject)
|
|
||||||
theItem.setHandle("0123456789abc")
|
|
||||||
theItem.setParent("0123456789abc")
|
|
||||||
theItem.setRoot("0123456789abc")
|
|
||||||
theItem.setOrder(1)
|
|
||||||
theItem.setName("A Name")
|
|
||||||
theItem.setClass("NOVEL")
|
|
||||||
theItem.setType("FOLDER")
|
|
||||||
theItem.setStatus(statusKeys[1])
|
|
||||||
theItem.setLayout("NOTE")
|
|
||||||
theItem.setExpanded(True)
|
|
||||||
theItem.setActive(False)
|
|
||||||
theItem.setParaCount(3)
|
|
||||||
theItem.setWordCount(5)
|
|
||||||
theItem.setCharCount(7)
|
|
||||||
theItem.setCursorPos(11)
|
|
||||||
|
|
||||||
# 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" parent="0123456789abc" root="0123456789abc" order="1" '
|
|
||||||
b'type="FOLDER" class="NOVEL"><meta expanded="True"/><name status="%s" '
|
|
||||||
b'import="None">A Name</name></item>'
|
|
||||||
b'</content>'
|
|
||||||
) % bytes(statusKeys[1], encoding="utf8")
|
|
||||||
|
|
||||||
# Unpack
|
|
||||||
theItem = NWItem(theProject)
|
|
||||||
assert theItem.unpackXML(xContent[0])
|
|
||||||
assert theItem.itemHandle == "0123456789abc"
|
|
||||||
assert theItem.itemParent == "0123456789abc"
|
|
||||||
assert theItem.itemRoot == "0123456789abc"
|
|
||||||
assert theItem.itemOrder == 1
|
|
||||||
assert theItem.isExpanded is True
|
assert theItem.isExpanded is True
|
||||||
assert theItem.isActive is True
|
assert theItem.mainHeading == "H1"
|
||||||
assert theItem.paraCount == 0
|
assert theItem.charCount == 100
|
||||||
assert theItem.wordCount == 0
|
assert theItem.wordCount == 20
|
||||||
assert theItem.charCount == 0
|
assert theItem.paraCount == 2
|
||||||
assert theItem.cursorPos == 0
|
|
||||||
assert theItem.itemClass == nwItemClass.NOVEL
|
|
||||||
assert theItem.itemType == nwItemType.FOLDER
|
|
||||||
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
|
||||||
assert theItem.itemStatus == statusKeys[1]
|
|
||||||
assert theItem.itemImport == importKeys[0] # Was None, should now be default
|
|
||||||
|
|
||||||
# Errors
|
|
||||||
# ======
|
|
||||||
|
|
||||||
# Not an Item
|
|
||||||
mockXml = etree.SubElement(nwXML, "stuff")
|
|
||||||
assert theItem.unpackXML(mockXml) is False
|
|
||||||
|
|
||||||
# Item without Handle
|
|
||||||
mockXml = etree.SubElement(nwXML, "item", attrib={"stuff": "nah"})
|
|
||||||
assert theItem.unpackXML(mockXml) is False
|
|
||||||
|
|
||||||
# Item with Invalid SubElement is Accepted w/Error
|
|
||||||
mockXml = etree.SubElement(nwXML, "item", attrib={"handle": "0123456789abc"})
|
|
||||||
xParam = etree.SubElement(mockXml, "invalid")
|
|
||||||
xParam.text = "stuff"
|
|
||||||
caplog.clear()
|
|
||||||
assert theItem.unpackXML(mockXml) is True
|
|
||||||
assert "Unknown tag 'invalid'" in caplog.text
|
|
||||||
|
|
||||||
# Pack Valid Item
|
|
||||||
mockXml = etree.SubElement(nwXML, "group")
|
|
||||||
theItem._subPack(mockXml, "subGroup", {"one": "two"}, "value", False)
|
|
||||||
assert etree.tostring(mockXml, pretty_print=False, encoding="utf-8") == (
|
|
||||||
b"<group><subGroup one=\"two\">value</subGroup></group>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Pack Not Allowed None
|
|
||||||
mockXml = etree.SubElement(nwXML, "group")
|
|
||||||
assert theItem._subPack(mockXml, "subGroup", {}, None, False) is None
|
|
||||||
assert theItem._subPack(mockXml, "subGroup", {}, "None", False) is None
|
|
||||||
assert etree.tostring(mockXml, pretty_print=False, encoding="utf-8") == (
|
|
||||||
b"<group/>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# END Test testCoreItem_XMLPackUnpack
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testCoreItem_ConvertFromFmt12(mockGUI):
|
|
||||||
"""Test the setter for all the nwItemLayout values for the NWItem
|
|
||||||
class using the class names that were present in file format 1.2.
|
|
||||||
"""
|
|
||||||
theProject = NWProject(mockGUI)
|
|
||||||
theItem = NWItem(theProject)
|
|
||||||
|
|
||||||
# Deprecated Layouts
|
|
||||||
theItem.setLayout("TITLE")
|
|
||||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
|
||||||
theItem.setLayout("PAGE")
|
|
||||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
|
||||||
theItem.setLayout("BOOK")
|
|
||||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
|
||||||
theItem.setLayout("PARTITION")
|
|
||||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
|
||||||
theItem.setLayout("UNNUMBERED")
|
|
||||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
|
||||||
theItem.setLayout("CHAPTER")
|
|
||||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
|
||||||
theItem.setLayout("SCENE")
|
|
||||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
|
||||||
theItem.setLayout("MUMBOJUMBO")
|
|
||||||
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
|
||||||
|
|
||||||
# END Test testCoreItem_ConvertFromFmt12
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testCoreItem_ConvertFromFmt13(mockGUI):
|
|
||||||
"""Test packing and unpacking XML objects for the NWItem class from
|
|
||||||
format version 1.3
|
|
||||||
"""
|
|
||||||
theProject = NWProject(mockGUI)
|
|
||||||
|
|
||||||
# Make Version 1.3 XML
|
|
||||||
nwXML = etree.Element("novelWriterXML")
|
|
||||||
xContent = etree.SubElement(nwXML, "content")
|
|
||||||
|
|
||||||
# Folder
|
|
||||||
xPack = etree.SubElement(xContent, "item", attrib={
|
|
||||||
"handle": "a000000000001",
|
|
||||||
"order": "1",
|
|
||||||
"parent": "b000000000001",
|
|
||||||
})
|
|
||||||
NWItem._subPack(xPack, "name", text="Folder")
|
|
||||||
NWItem._subPack(xPack, "type", text="FOLDER")
|
|
||||||
NWItem._subPack(xPack, "class", text="NOVEL")
|
|
||||||
NWItem._subPack(xPack, "status", text="New")
|
|
||||||
NWItem._subPack(xPack, "expanded", text="True")
|
|
||||||
|
|
||||||
# Unpack Folder
|
|
||||||
theItem = NWItem(theProject)
|
|
||||||
theItem.unpackXML(xContent[0])
|
|
||||||
assert theItem.itemHandle == "a000000000001"
|
|
||||||
assert theItem.itemParent == "b000000000001"
|
|
||||||
assert theItem.itemOrder == 1
|
|
||||||
assert theItem.isExpanded is True
|
|
||||||
assert theItem.isActive is True
|
|
||||||
assert theItem.charCount == 0
|
|
||||||
assert theItem.wordCount == 0
|
|
||||||
assert theItem.paraCount == 0
|
|
||||||
assert theItem.cursorPos == 0
|
|
||||||
assert theItem.itemClass == nwItemClass.NOVEL
|
|
||||||
assert theItem.itemType == nwItemType.FOLDER
|
|
||||||
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
|
||||||
|
|
||||||
# File
|
|
||||||
xPack = etree.SubElement(xContent, "item", attrib={
|
|
||||||
"handle": "c000000000001",
|
|
||||||
"order": "2",
|
|
||||||
"parent": "a000000000001",
|
|
||||||
})
|
|
||||||
NWItem._subPack(xPack, "name", text="Scene")
|
|
||||||
NWItem._subPack(xPack, "type", text="FILE")
|
|
||||||
NWItem._subPack(xPack, "class", text="NOVEL")
|
|
||||||
NWItem._subPack(xPack, "status", text="New")
|
|
||||||
NWItem._subPack(xPack, "exported", text="True")
|
|
||||||
NWItem._subPack(xPack, "layout", text="DOCUMENT")
|
|
||||||
NWItem._subPack(xPack, "charCount", text="600")
|
|
||||||
NWItem._subPack(xPack, "wordCount", text="100")
|
|
||||||
NWItem._subPack(xPack, "paraCount", text="6")
|
|
||||||
NWItem._subPack(xPack, "cursorPos", text="50")
|
|
||||||
|
|
||||||
# Unpack File
|
|
||||||
theItem = NWItem(theProject)
|
|
||||||
theItem.unpackXML(xContent[1])
|
|
||||||
assert theItem.itemHandle == "c000000000001"
|
|
||||||
assert theItem.itemParent == "a000000000001"
|
|
||||||
assert theItem.itemOrder == 2
|
|
||||||
assert theItem.isExpanded is False
|
|
||||||
assert theItem.isActive is True
|
|
||||||
assert theItem.charCount == 600
|
|
||||||
assert theItem.wordCount == 100
|
|
||||||
assert theItem.paraCount == 6
|
|
||||||
assert theItem.cursorPos == 50
|
assert theItem.cursorPos == 50
|
||||||
|
|
||||||
|
assert theItem.pack() == {
|
||||||
|
"name": "A File",
|
||||||
|
"itemAttr": {
|
||||||
|
"handle": "0000000000003",
|
||||||
|
"parent": "0000000000002",
|
||||||
|
"root": "0000000000001",
|
||||||
|
"order": "1",
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
},
|
||||||
|
"metaAttr": {
|
||||||
|
"expanded": "True",
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": "100",
|
||||||
|
"wordCount": "20",
|
||||||
|
"paraCount": "2",
|
||||||
|
"cursorPos": "50",
|
||||||
|
},
|
||||||
|
"nameAttr": {
|
||||||
|
"status": "s000000",
|
||||||
|
"import": "i000001",
|
||||||
|
"active": "False",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Folder
|
||||||
|
theItem = NWItem(theProject)
|
||||||
|
assert theItem.unpack({
|
||||||
|
"label": "A Folder",
|
||||||
|
"handle": "0000000000003",
|
||||||
|
"parent": "0000000000002",
|
||||||
|
"root": "0000000000001",
|
||||||
|
"order": 1,
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": True,
|
||||||
|
"status": "",
|
||||||
|
"import": "",
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 100,
|
||||||
|
"wordCount": 20,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 50,
|
||||||
|
"active": True,
|
||||||
|
}) is True
|
||||||
|
|
||||||
|
assert theItem.itemName == "A Folder"
|
||||||
|
assert theItem.itemHandle == "0000000000003"
|
||||||
|
assert theItem.itemParent == "0000000000002"
|
||||||
|
assert theItem.itemRoot == "0000000000001"
|
||||||
|
assert theItem.itemOrder == 1
|
||||||
|
assert theItem.itemType == nwItemType.FOLDER
|
||||||
assert theItem.itemClass == nwItemClass.NOVEL
|
assert theItem.itemClass == nwItemClass.NOVEL
|
||||||
assert theItem.itemType == nwItemType.FILE
|
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
assert theItem.itemStatus == "s000000"
|
||||||
|
assert theItem.itemImport == "i000001"
|
||||||
|
assert theItem.isActive is False
|
||||||
|
assert theItem.isExpanded is True
|
||||||
|
assert theItem.mainHeading == "H0"
|
||||||
|
assert theItem.charCount == 0
|
||||||
|
assert theItem.wordCount == 0
|
||||||
|
assert theItem.paraCount == 0
|
||||||
|
assert theItem.cursorPos == 0
|
||||||
|
|
||||||
# Deprecated Type
|
assert theItem.pack() == {
|
||||||
theItem.setType("TRASH")
|
"name": "A Folder",
|
||||||
|
"itemAttr": {
|
||||||
|
"handle": "0000000000003",
|
||||||
|
"parent": "0000000000002",
|
||||||
|
"root": "0000000000001",
|
||||||
|
"order": "1",
|
||||||
|
"type": "FOLDER",
|
||||||
|
"class": "NOVEL",
|
||||||
|
},
|
||||||
|
"metaAttr": {
|
||||||
|
"expanded": "True",
|
||||||
|
},
|
||||||
|
"nameAttr": {
|
||||||
|
"status": "s000000",
|
||||||
|
"import": "i000001",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Root
|
||||||
|
theItem = NWItem(theProject)
|
||||||
|
assert theItem.unpack({
|
||||||
|
"label": "A Novel",
|
||||||
|
"handle": "0000000000003",
|
||||||
|
"parent": "0000000000002",
|
||||||
|
"root": "0000000000001",
|
||||||
|
"order": 1,
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "NOVEL",
|
||||||
|
"layout": "DOCUMENT",
|
||||||
|
"expanded": True,
|
||||||
|
"status": None,
|
||||||
|
"import": None,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 100,
|
||||||
|
"wordCount": 20,
|
||||||
|
"paraCount": 2,
|
||||||
|
"cursorPos": 50,
|
||||||
|
"active": True,
|
||||||
|
}) is True
|
||||||
|
|
||||||
|
assert theItem.itemName == "A Novel"
|
||||||
|
assert theItem.itemHandle == "0000000000003"
|
||||||
|
assert theItem.itemParent is None
|
||||||
|
assert theItem.itemRoot == "0000000000003"
|
||||||
|
assert theItem.itemOrder == 1
|
||||||
assert theItem.itemType == nwItemType.ROOT
|
assert theItem.itemType == nwItemType.ROOT
|
||||||
|
assert theItem.itemClass == nwItemClass.NOVEL
|
||||||
|
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||||
|
assert theItem.itemStatus == "s000000"
|
||||||
|
assert theItem.itemImport == "i000001"
|
||||||
|
assert theItem.isActive is False
|
||||||
|
assert theItem.isExpanded is True
|
||||||
|
assert theItem.mainHeading == "H0"
|
||||||
|
assert theItem.charCount == 0
|
||||||
|
assert theItem.wordCount == 0
|
||||||
|
assert theItem.paraCount == 0
|
||||||
|
assert theItem.cursorPos == 0
|
||||||
|
|
||||||
# END Test testCoreItem_ConvertFromFmt13
|
assert theItem.pack() == {
|
||||||
|
"name": "A Novel",
|
||||||
|
"itemAttr": {
|
||||||
|
"handle": "0000000000003",
|
||||||
|
"parent": "None",
|
||||||
|
"root": "0000000000003",
|
||||||
|
"order": "1",
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "NOVEL",
|
||||||
|
},
|
||||||
|
"metaAttr": {
|
||||||
|
"expanded": "True",
|
||||||
|
},
|
||||||
|
"nameAttr": {
|
||||||
|
"status": "s000000",
|
||||||
|
"import": "i000001",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# END Test testCoreItem_PackUnpack
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE, C
|
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
|
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE, C
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
from novelwriter.common import formatTimeStamp
|
from novelwriter.common import formatTimeStamp
|
||||||
@@ -37,6 +37,7 @@ from novelwriter.core.index import NWIndex
|
|||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.options import OptionState
|
from novelwriter.core.options import OptionState
|
||||||
from novelwriter.core.document import NWDoc
|
from novelwriter.core.document import NWDoc
|
||||||
|
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -202,7 +203,7 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, mockGUI, tmpDir):
|
|||||||
|
|
||||||
assert theProject.newProject(projData) is True
|
assert theProject.newProject(projData) is True
|
||||||
assert theProject.openProject(fncDir) is True
|
assert theProject.openProject(fncDir) is True
|
||||||
assert theProject.projName == "Sample Project"
|
assert theProject.data.name == "Sample Project"
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
assert theProject.closeProject() is True
|
assert theProject.closeProject() is True
|
||||||
os.unlink(dstSample)
|
os.unlink(dstSample)
|
||||||
@@ -236,7 +237,7 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir):
|
|||||||
monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx")
|
monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx")
|
||||||
assert theProject.newProject(projData) is True
|
assert theProject.newProject(projData) is True
|
||||||
assert theProject.openProject(fncDir) is True
|
assert theProject.openProject(fncDir) is True
|
||||||
assert theProject.projName == "Sample Project"
|
assert theProject.data.name == "Sample Project"
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
assert theProject.closeProject() is True
|
assert theProject.closeProject() is True
|
||||||
|
|
||||||
@@ -264,14 +265,14 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
|||||||
assert theProject.closeProject() is True
|
assert theProject.closeProject() is True
|
||||||
assert theProject.openProject(projFile) is True
|
assert theProject.openProject(projFile) is True
|
||||||
|
|
||||||
assert theProject.newRoot(nwItemClass.NOVEL) == "0000000000020"
|
assert theProject.newRoot(nwItemClass.NOVEL) == "0000000000010"
|
||||||
assert theProject.newRoot(nwItemClass.PLOT) == "0000000000021"
|
assert theProject.newRoot(nwItemClass.PLOT) == "0000000000011"
|
||||||
assert theProject.newRoot(nwItemClass.CHARACTER) == "0000000000022"
|
assert theProject.newRoot(nwItemClass.CHARACTER) == "0000000000012"
|
||||||
assert theProject.newRoot(nwItemClass.WORLD) == "0000000000023"
|
assert theProject.newRoot(nwItemClass.WORLD) == "0000000000013"
|
||||||
assert theProject.newRoot(nwItemClass.TIMELINE) == "0000000000024"
|
assert theProject.newRoot(nwItemClass.TIMELINE) == "0000000000014"
|
||||||
assert theProject.newRoot(nwItemClass.OBJECT) == "0000000000025"
|
assert theProject.newRoot(nwItemClass.OBJECT) == "0000000000015"
|
||||||
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000026"
|
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000016"
|
||||||
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000027"
|
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000017"
|
||||||
|
|
||||||
assert theProject.projChanged is True
|
assert theProject.projChanged is True
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
@@ -282,23 +283,23 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
|||||||
assert theProject.projChanged is False
|
assert theProject.projChanged is False
|
||||||
|
|
||||||
# Delete the new items
|
# Delete the new items
|
||||||
assert theProject.removeItem("0000000000020") is True
|
assert theProject.removeItem("0000000000010") is True
|
||||||
assert theProject.removeItem("0000000000021") is True
|
assert theProject.removeItem("0000000000011") is True
|
||||||
assert theProject.removeItem("0000000000022") is True
|
assert theProject.removeItem("0000000000012") is True
|
||||||
assert theProject.removeItem("0000000000023") is True
|
assert theProject.removeItem("0000000000013") is True
|
||||||
assert theProject.removeItem("0000000000024") is True
|
assert theProject.removeItem("0000000000014") is True
|
||||||
assert theProject.removeItem("0000000000025") is True
|
assert theProject.removeItem("0000000000015") is True
|
||||||
assert theProject.removeItem("0000000000026") is True
|
assert theProject.removeItem("0000000000016") is True
|
||||||
assert theProject.removeItem("0000000000027") is True
|
assert theProject.removeItem("0000000000017") is True
|
||||||
|
|
||||||
assert "0000000000020" not in theProject.tree
|
assert "0000000000010" not in theProject.tree
|
||||||
assert "0000000000021" not in theProject.tree
|
assert "0000000000011" not in theProject.tree
|
||||||
assert "0000000000022" not in theProject.tree
|
assert "0000000000012" not in theProject.tree
|
||||||
assert "0000000000023" not in theProject.tree
|
assert "0000000000013" not in theProject.tree
|
||||||
assert "0000000000024" not in theProject.tree
|
assert "0000000000014" not in theProject.tree
|
||||||
assert "0000000000025" not in theProject.tree
|
assert "0000000000015" not in theProject.tree
|
||||||
assert "0000000000026" not in theProject.tree
|
assert "0000000000016" not in theProject.tree
|
||||||
assert "0000000000027" not in theProject.tree
|
assert "0000000000017" not in theProject.tree
|
||||||
|
|
||||||
# END Test testCoreProject_NewRoot
|
# END Test testCoreProject_NewRoot
|
||||||
|
|
||||||
@@ -325,26 +326,26 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
|
|||||||
assert theProject.newFile("New File", "1234567890abc") is None
|
assert theProject.newFile("New File", "1234567890abc") is None
|
||||||
|
|
||||||
# Add files properly
|
# Add files properly
|
||||||
assert theProject.newFolder("Stuff", C.hNovelRoot) == "0000000000020"
|
assert theProject.newFolder("Stuff", C.hNovelRoot) == "0000000000010"
|
||||||
assert theProject.newFile("Hello", "0000000000020") == "0000000000021"
|
assert theProject.newFile("Hello", "0000000000010") == "0000000000011"
|
||||||
assert theProject.newFile("Jane", C.hCharRoot) == "0000000000022"
|
assert theProject.newFile("Jane", C.hCharRoot) == "0000000000012"
|
||||||
|
|
||||||
assert "0000000000020" in theProject.tree
|
assert "0000000000010" in theProject.tree
|
||||||
assert "0000000000021" in theProject.tree
|
assert "0000000000011" in theProject.tree
|
||||||
assert "0000000000022" in theProject.tree
|
assert "0000000000012" in theProject.tree
|
||||||
|
|
||||||
# Write to file, failed
|
# Write to file, failed
|
||||||
assert theProject.writeNewFile("blabla", 1, True) is False # Not a handle
|
assert theProject.writeNewFile("blabla", 1, True) is False # Not a handle
|
||||||
assert theProject.writeNewFile("0000000000020", 1, True) is False # Not a file
|
assert theProject.writeNewFile("0000000000010", 1, True) is False # Not a file
|
||||||
assert theProject.writeNewFile(C.hTitlePage, 1, True) is False # Already has content
|
assert theProject.writeNewFile(C.hTitlePage, 1, True) is False # Already has content
|
||||||
|
|
||||||
# Write to file, success
|
# Write to file, success
|
||||||
assert theProject.writeNewFile("0000000000021", 2, True) is True
|
assert theProject.writeNewFile("0000000000011", 2, True) is True
|
||||||
assert NWDoc(theProject, "0000000000021").readDocument() == "## Hello\n\n"
|
assert NWDoc(theProject, "0000000000011").readDocument() == "## Hello\n\n"
|
||||||
|
|
||||||
# Write to file with additional text, success
|
# Write to file with additional text, success
|
||||||
assert theProject.writeNewFile("0000000000022", 1, False, "Hi Jane\n\n") is True
|
assert theProject.writeNewFile("0000000000012", 1, False, "Hi Jane\n\n") is True
|
||||||
assert NWDoc(theProject, "0000000000022").readDocument() == "# Jane\n\nHi Jane\n\n"
|
assert NWDoc(theProject, "0000000000012").readDocument() == "# Jane\n\nHi Jane\n\n"
|
||||||
|
|
||||||
# Save, close and check
|
# Save, close and check
|
||||||
assert theProject.projChanged is True
|
assert theProject.projChanged is True
|
||||||
@@ -357,23 +358,23 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
|
|||||||
# Delete new file, but block access
|
# Delete new file, but block access
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("os.unlink", causeOSError)
|
mp.setattr("os.unlink", causeOSError)
|
||||||
assert theProject.removeItem("0000000000021") is False
|
assert theProject.removeItem("0000000000011") is False
|
||||||
assert "0000000000021" in theProject.tree
|
assert "0000000000011" in theProject.tree
|
||||||
|
|
||||||
# Delete new files and folders
|
# Delete new files and folders
|
||||||
assert os.path.isfile(os.path.join(fncDir, "content", "0000000000022.nwd"))
|
assert os.path.isfile(os.path.join(fncDir, "content", "0000000000012.nwd"))
|
||||||
assert os.path.isfile(os.path.join(fncDir, "content", "0000000000021.nwd"))
|
assert os.path.isfile(os.path.join(fncDir, "content", "0000000000011.nwd"))
|
||||||
|
|
||||||
assert theProject.removeItem("0000000000022") is True
|
assert theProject.removeItem("0000000000012") is True
|
||||||
assert theProject.removeItem("0000000000021") is True
|
assert theProject.removeItem("0000000000011") is True
|
||||||
assert theProject.removeItem("0000000000020") is True
|
assert theProject.removeItem("0000000000010") is True
|
||||||
|
|
||||||
assert not os.path.isfile(os.path.join(fncDir, "content", "0000000000022.nwd"))
|
assert not os.path.isfile(os.path.join(fncDir, "content", "0000000000012.nwd"))
|
||||||
assert not os.path.isfile(os.path.join(fncDir, "content", "0000000000021.nwd"))
|
assert not os.path.isfile(os.path.join(fncDir, "content", "0000000000011.nwd"))
|
||||||
|
|
||||||
assert "0000000000020" not in theProject.tree
|
assert "0000000000010" not in theProject.tree
|
||||||
assert "0000000000021" not in theProject.tree
|
assert "0000000000011" not in theProject.tree
|
||||||
assert "0000000000022" not in theProject.tree
|
assert "0000000000012" not in theProject.tree
|
||||||
|
|
||||||
assert theProject.closeProject() is True
|
assert theProject.closeProject() is True
|
||||||
|
|
||||||
@@ -381,139 +382,90 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
|
def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncDir, mockRnd):
|
||||||
"""Test opening a project.
|
"""Test opening a project.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
mockRnd.reset()
|
||||||
|
buildTestProject(theProject, fncDir)
|
||||||
|
|
||||||
# Rename the project file to check handling
|
# Rename the project file to check handling
|
||||||
rName = os.path.join(nwMinimal, nwFiles.PROJ_FILE)
|
rName = os.path.join(fncDir, nwFiles.PROJ_FILE)
|
||||||
wName = os.path.join(nwMinimal, nwFiles.PROJ_FILE+"_sdfghj")
|
wName = os.path.join(fncDir, nwFiles.PROJ_FILE+"_sdfghj")
|
||||||
os.rename(rName, wName)
|
os.rename(rName, wName)
|
||||||
assert theProject.openProject(nwMinimal) is False
|
assert theProject.openProject(fncDir) is False
|
||||||
os.rename(wName, rName)
|
os.rename(wName, rName)
|
||||||
|
|
||||||
# Fail on folder structure check
|
# Fail on folder structure check
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("os.mkdir", causeOSError)
|
mp.setattr("os.mkdir", causeOSError)
|
||||||
assert theProject.openProject(nwMinimal) is False
|
shutil.rmtree(os.path.join(fncDir, "meta"))
|
||||||
|
assert theProject.openProject(fncDir) is False
|
||||||
|
|
||||||
# Fail on lock file
|
# Fail on lock file
|
||||||
theProject.setProjectPath(nwMinimal)
|
theProject.setProjectPath(fncDir)
|
||||||
assert theProject._writeLockFile()
|
assert theProject._writeLockFile()
|
||||||
assert theProject.openProject(nwMinimal) is False
|
assert theProject.openProject(fncDir) is False
|
||||||
|
|
||||||
# Fail to read lockfile (which still opens the project)
|
# Fail to read lockfile (which still opens the project)
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("builtins.open", causeOSError)
|
mp.setattr("builtins.open", causeOSError)
|
||||||
assert theProject.openProject(nwMinimal) is True
|
caplog.clear()
|
||||||
|
assert theProject.openProject(fncDir) is True
|
||||||
|
assert "Failed to check lock file" in caplog.text
|
||||||
assert theProject.closeProject()
|
assert theProject.closeProject()
|
||||||
|
|
||||||
# Force open with lockfile
|
# Force open with lockfile
|
||||||
theProject.setProjectPath(nwMinimal)
|
theProject.setProjectPath(fncDir)
|
||||||
assert theProject._writeLockFile()
|
assert theProject._writeLockFile()
|
||||||
assert theProject.openProject(nwMinimal, overrideLock=True) is True
|
assert theProject.openProject(fncDir, overrideLock=True) is True
|
||||||
assert theProject.closeProject()
|
assert theProject.closeProject()
|
||||||
|
|
||||||
# Make a junk XML file
|
# Not a novelwriter XML file
|
||||||
oName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"orig")
|
with monkeypatch.context() as mp:
|
||||||
bName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"bak")
|
mp.setattr(ProjectXMLReader, "read", lambda *a: False)
|
||||||
os.rename(rName, oName)
|
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.NOT_NWX_FILE))
|
||||||
writeFile(rName, "stuff")
|
assert theProject.openProject(fncDir) is False
|
||||||
assert theProject.openProject(nwMinimal) is False
|
assert "Project file does not appear" in mockGUI.lastAlert
|
||||||
|
|
||||||
# Also write a jun XML backup file
|
# Unknown project file version
|
||||||
writeFile(bName, "stuff")
|
with monkeypatch.context() as mp:
|
||||||
assert theProject.openProject(nwMinimal) is False
|
mp.setattr(ProjectXMLReader, "read", lambda *a: False)
|
||||||
|
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.UNKNOWN_VERSION))
|
||||||
|
assert theProject.openProject(fncDir) is False
|
||||||
|
assert "Unknown or unsupported novelWriter project file" in mockGUI.lastAlert
|
||||||
|
|
||||||
# Wrong root item
|
# Other parse error
|
||||||
writeFile(rName, "<not_novelWriterXML></not_novelWriterXML>\n")
|
with monkeypatch.context() as mp:
|
||||||
assert theProject.openProject(nwMinimal) is False
|
mp.setattr(ProjectXMLReader, "read", lambda *a: False)
|
||||||
|
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.CANNOT_PARSE))
|
||||||
|
assert theProject.openProject(fncDir) is False
|
||||||
|
assert "Failed to parse project xml" in mockGUI.lastAlert
|
||||||
|
|
||||||
# Wrong file version
|
# Won't convert legacy file
|
||||||
writeFile(rName, (
|
with monkeypatch.context() as mp:
|
||||||
"<?xml version='0.0' encoding='utf-8'?>\n"
|
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY))
|
||||||
"<novelWriterXML "
|
mockGUI.askResponse = False
|
||||||
"appVersion=\"1.0\" "
|
assert theProject.openProject(fncDir) is False
|
||||||
"hexVersion=\"0x01000000\" "
|
assert "The file format of your project is about to be" in mockGUI.lastQuestion[1]
|
||||||
"fileVersion=\"1.0\" "
|
mockGUI.askResponse = True
|
||||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
|
||||||
"</novelWriterXML>\n"
|
|
||||||
))
|
|
||||||
mockGUI.askResponse = False
|
|
||||||
assert theProject.openProject(nwMinimal) is False
|
|
||||||
mockGUI.undo()
|
|
||||||
|
|
||||||
# Future file version
|
# Won't convert legacy file
|
||||||
writeFile(rName, (
|
with monkeypatch.context() as mp:
|
||||||
"<?xml version='1.0' encoding='utf-8'?>\n"
|
mp.setattr(ProjectXMLReader, "hexVersion", property(lambda *a: "0x99999999"))
|
||||||
"<novelWriterXML "
|
mockGUI.askResponse = False
|
||||||
"appVersion=\"1.0\" "
|
assert theProject.openProject(fncDir) is False
|
||||||
"hexVersion=\"0x01000000\" "
|
assert "This project was saved by a newer version" in mockGUI.lastQuestion[1]
|
||||||
"fileVersion=\"99.99\" "
|
mockGUI.askResponse = True
|
||||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
|
||||||
"</novelWriterXML>\n"
|
|
||||||
))
|
|
||||||
assert theProject.openProject(nwMinimal) is False
|
|
||||||
|
|
||||||
# Update file version
|
|
||||||
writeFile(rName, (
|
|
||||||
"<?xml version='1.0' encoding='utf-8'?>\n"
|
|
||||||
"<novelWriterXML "
|
|
||||||
"appVersion=\"1.0\" "
|
|
||||||
"hexVersion=\"0xffffffff\" "
|
|
||||||
"fileVersion=\"1.2\" "
|
|
||||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
|
||||||
"</novelWriterXML>\n"
|
|
||||||
))
|
|
||||||
mockGUI.askResponse = False
|
|
||||||
assert theProject.openProject(nwMinimal) is False
|
|
||||||
assert mockGUI.lastQuestion[0] == "File Version"
|
|
||||||
mockGUI.undo()
|
|
||||||
|
|
||||||
# Larger hex version
|
|
||||||
writeFile(rName, (
|
|
||||||
"<?xml version='1.0' encoding='utf-8'?>\n"
|
|
||||||
"<novelWriterXML "
|
|
||||||
"appVersion=\"1.0\" "
|
|
||||||
"hexVersion=\"0xffffffff\" "
|
|
||||||
"fileVersion=\"%s\" "
|
|
||||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
|
||||||
"</novelWriterXML>\n"
|
|
||||||
) % theProject.FILE_VERSION)
|
|
||||||
mockGUI.askResponse = False
|
|
||||||
assert theProject.openProject(nwMinimal) is False
|
|
||||||
assert mockGUI.lastQuestion[0] == "Version Conflict"
|
|
||||||
mockGUI.undo()
|
|
||||||
|
|
||||||
# Test skipping XML entries
|
|
||||||
writeFile(rName, (
|
|
||||||
"<?xml version='1.0' encoding='utf-8'?>\n"
|
|
||||||
"<novelWriterXML "
|
|
||||||
"appVersion=\"1.0\" "
|
|
||||||
"hexVersion=\"0x01000000\" "
|
|
||||||
"fileVersion=\"1.2\" "
|
|
||||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
|
||||||
"<project><stuff/></project>\n"
|
|
||||||
"<settings><stuff/></settings>\n"
|
|
||||||
"</novelWriterXML>\n"
|
|
||||||
))
|
|
||||||
assert theProject.openProject(nwMinimal) is True
|
|
||||||
assert theProject.closeProject()
|
|
||||||
|
|
||||||
# Clean up XML files
|
|
||||||
os.unlink(rName)
|
|
||||||
os.unlink(bName)
|
|
||||||
os.rename(oName, rName)
|
|
||||||
|
|
||||||
# Add some legacy stuff that cannot be removed
|
# Add some legacy stuff that cannot be removed
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(theProject, "_legacyDataFolder", causeOSError)
|
mp.setattr(theProject, "_legacyDataFolder", causeOSError)
|
||||||
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
os.mkdir(os.path.join(fncDir, "data_0"))
|
||||||
writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.nwd"), "stuff")
|
writeFile(os.path.join(fncDir, "data_0", "123456789abc_main.nwd"), "stuff")
|
||||||
writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.bak"), "stuff")
|
writeFile(os.path.join(fncDir, "data_0", "123456789abc_main.bak"), "stuff")
|
||||||
mockGUI.clear()
|
mockGUI.clear()
|
||||||
assert theProject.openProject(nwMinimal) is True
|
assert theProject.openProject(fncDir) is True
|
||||||
assert "There was an error updating the project." in mockGUI.lastAlert
|
assert "There was an error updating the project." in mockGUI.lastAlert
|
||||||
|
|
||||||
assert theProject.closeProject()
|
assert theProject.closeProject()
|
||||||
@@ -522,56 +474,31 @@ def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
|
def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncDir, refDir):
|
||||||
"""Test saving a project.
|
"""Test saving a project.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
testFile = os.path.join(nwMinimal, "nwProject.nwx")
|
|
||||||
backFile = os.path.join(nwMinimal, "nwProject.bak")
|
|
||||||
compFile = os.path.join(refDir, os.path.pardir, "minimal", "nwProject.nwx")
|
|
||||||
|
|
||||||
# Nothing to save
|
# Nothing to save
|
||||||
assert theProject.saveProject() is False
|
assert theProject.saveProject() is False
|
||||||
|
|
||||||
# Open test project
|
mockRnd.reset()
|
||||||
assert theProject.openProject(nwMinimal)
|
buildTestProject(theProject, fncDir)
|
||||||
|
|
||||||
# Fail on folder structure check
|
# Fail on folder structure check
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("os.path.isdir", lambda *a: False)
|
mp.setattr("os.mkdir", causeOSError)
|
||||||
|
shutil.rmtree(os.path.join(fncDir, "meta"))
|
||||||
assert theProject.saveProject() is False
|
assert theProject.saveProject() is False
|
||||||
|
|
||||||
# Fail on open file
|
# Fail writing
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("builtins.open", causeOSError)
|
mp.setattr(ProjectXMLWriter, "write", lambda *a: False)
|
||||||
assert theProject.saveProject() is False
|
assert theProject.saveProject() is False
|
||||||
|
|
||||||
# Fail on creating .bak file
|
# Save with and without autosave
|
||||||
with monkeypatch.context() as mp:
|
assert theProject.saveProject(autoSave=False) is True
|
||||||
mp.setattr("os.replace", causeOSError)
|
|
||||||
assert theProject.saveProject() is False
|
|
||||||
assert os.path.isfile(backFile) is False
|
|
||||||
|
|
||||||
# Successful save
|
|
||||||
saveCount = theProject.saveCount
|
|
||||||
autoCount = theProject.autoCount
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.saveCount == saveCount + 1
|
|
||||||
assert theProject.autoCount == autoCount
|
|
||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
|
||||||
|
|
||||||
# Check that a second save creates a .bak file
|
|
||||||
assert os.path.isfile(backFile) is True
|
|
||||||
|
|
||||||
# Successful autosave
|
|
||||||
saveCount = theProject.saveCount
|
|
||||||
autoCount = theProject.autoCount
|
|
||||||
assert theProject.saveProject(autoSave=True) is True
|
assert theProject.saveProject(autoSave=True) is True
|
||||||
assert theProject.saveCount == saveCount
|
|
||||||
assert theProject.autoCount == autoCount + 1
|
|
||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
|
||||||
|
|
||||||
# Close test project
|
|
||||||
assert theProject.closeProject()
|
assert theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreProject_Save
|
# END Test testCoreProject_Save
|
||||||
@@ -682,11 +609,11 @@ def testCoreProject_Helpers(monkeypatch, fncDir, mockGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
def testCoreProject_AccessItems(mockGUI, fncDir, mockRnd):
|
||||||
"""Test helper functions for the project folder.
|
"""Test helper functions for the project folder.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.openProject(nwMinimal)
|
buildTestProject(theProject, fncDir)
|
||||||
|
|
||||||
# Storage Objects
|
# Storage Objects
|
||||||
assert isinstance(theProject.index, NWIndex)
|
assert isinstance(theProject.index, NWIndex)
|
||||||
@@ -695,34 +622,34 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
|||||||
|
|
||||||
# Move Novel ROOT to after its files
|
# Move Novel ROOT to after its files
|
||||||
oldOrder = [
|
oldOrder = [
|
||||||
"a508bb932959c", # ROOT: Novel
|
C.hNovelRoot,
|
||||||
"a35baf2e93843", # FILE: Title Page
|
C.hPlotRoot,
|
||||||
"a6d311a93600a", # FOLDER: New Chapter
|
C.hCharRoot,
|
||||||
"f5ab3e30151e1", # FILE: New Chapter
|
C.hWorldRoot,
|
||||||
"8c659a11cd429", # FILE: New Scene
|
C.hTitlePage,
|
||||||
"7695ce551d265", # ROOT: Plot
|
C.hChapterDir,
|
||||||
"afb3043c7b2b3", # ROOT: Characters
|
C.hChapterDoc,
|
||||||
"9d5247ab588e0", # ROOT: World
|
C.hSceneDoc,
|
||||||
]
|
]
|
||||||
newOrder = [
|
newOrder = [
|
||||||
"a35baf2e93843", # FILE: Title Page
|
C.hTitlePage,
|
||||||
"f5ab3e30151e1", # FILE: New Chapter
|
C.hChapterDoc,
|
||||||
"8c659a11cd429", # FILE: New Scene
|
C.hSceneDoc,
|
||||||
"a6d311a93600a", # FOLDER: New Chapter
|
C.hChapterDir,
|
||||||
"a508bb932959c", # ROOT: Novel
|
C.hNovelRoot,
|
||||||
"7695ce551d265", # ROOT: Plot
|
C.hPlotRoot,
|
||||||
"afb3043c7b2b3", # ROOT: Characters
|
C.hCharRoot,
|
||||||
"9d5247ab588e0", # ROOT: World
|
C.hWorldRoot,
|
||||||
]
|
]
|
||||||
assert theProject.tree.handles() == oldOrder
|
assert theProject.tree.handles() == oldOrder
|
||||||
assert theProject.setTreeOrder(newOrder)
|
assert theProject.setTreeOrder(newOrder)
|
||||||
assert theProject.tree.handles() == newOrder
|
assert theProject.tree.handles() == newOrder
|
||||||
|
|
||||||
# Add a non-existing item
|
# Add a non-existing item
|
||||||
theProject.tree._treeOrder.append("01234567789abc")
|
theProject.tree._treeOrder.append(C.hInvalid)
|
||||||
|
|
||||||
# Add an item with a non-existent parent
|
# Add an item with a non-existent parent
|
||||||
nHandle = theProject.newFile("Test File", "a6d311a93600a")
|
nHandle = theProject.newFile("Test File", C.hChapterDir)
|
||||||
theProject.tree[nHandle].setParent("cba9876543210")
|
theProject.tree[nHandle].setParent("cba9876543210")
|
||||||
assert theProject.tree[nHandle].itemParent == "cba9876543210"
|
assert theProject.tree[nHandle].itemParent == "cba9876543210"
|
||||||
|
|
||||||
@@ -731,15 +658,15 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
|||||||
retOrder.append(tItem.itemHandle)
|
retOrder.append(tItem.itemHandle)
|
||||||
|
|
||||||
assert retOrder == [
|
assert retOrder == [
|
||||||
"a508bb932959c", # ROOT: Novel
|
C.hNovelRoot,
|
||||||
"7695ce551d265", # ROOT: Plot
|
C.hPlotRoot,
|
||||||
"afb3043c7b2b3", # ROOT: Characters
|
C.hCharRoot,
|
||||||
"9d5247ab588e0", # ROOT: World
|
C.hWorldRoot,
|
||||||
nHandle, # FILE: Test File
|
nHandle,
|
||||||
"a35baf2e93843", # FILE: Title Page
|
C.hTitlePage,
|
||||||
"a6d311a93600a", # FOLDER: New Chapter
|
C.hChapterDir,
|
||||||
"f5ab3e30151e1", # FILE: New Chapter
|
C.hChapterDoc,
|
||||||
"8c659a11cd429", # FILE: New Scene
|
C.hSceneDoc,
|
||||||
]
|
]
|
||||||
assert theProject.tree[nHandle].itemParent is None
|
assert theProject.tree[nHandle].itemParent is None
|
||||||
|
|
||||||
@@ -751,23 +678,24 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
|||||||
"""Test the status and importance flag handling.
|
"""Test the status and importance flag handling.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
mockRnd.reset()
|
||||||
buildTestProject(theProject, fncDir)
|
buildTestProject(theProject, fncDir)
|
||||||
|
|
||||||
statusKeys = ["s000008", "s000009", "s00000a", "s00000b"]
|
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
|
||||||
importKeys = ["i00000c", "i00000d", "i00000e", "i00000f"]
|
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
|
||||||
|
|
||||||
# Change Status
|
# Change Status
|
||||||
# =============
|
# =============
|
||||||
|
|
||||||
theProject.tree["0000000000014"].setStatus("Finished")
|
theProject.tree[C.hNovelRoot].setStatus(statusKeys[3])
|
||||||
theProject.tree["0000000000015"].setStatus("Draft")
|
theProject.tree[C.hPlotRoot].setStatus(statusKeys[2])
|
||||||
theProject.tree["0000000000016"].setStatus("Note")
|
theProject.tree[C.hCharRoot].setStatus(statusKeys[1])
|
||||||
theProject.tree["0000000000017"].setStatus("Finished")
|
theProject.tree[C.hWorldRoot].setStatus(statusKeys[3])
|
||||||
|
|
||||||
assert theProject.tree["0000000000014"].itemStatus == statusKeys[3]
|
assert theProject.tree[C.hNovelRoot].itemStatus == statusKeys[3]
|
||||||
assert theProject.tree["0000000000015"].itemStatus == statusKeys[2]
|
assert theProject.tree[C.hPlotRoot].itemStatus == statusKeys[2]
|
||||||
assert theProject.tree["0000000000016"].itemStatus == statusKeys[1]
|
assert theProject.tree[C.hCharRoot].itemStatus == statusKeys[1]
|
||||||
assert theProject.tree["0000000000017"].itemStatus == statusKeys[3]
|
assert theProject.tree[C.hWorldRoot].itemStatus == statusKeys[3]
|
||||||
|
|
||||||
newList = [
|
newList = [
|
||||||
{"key": statusKeys[0], "name": "New", "cols": (1, 1, 1)},
|
{"key": statusKeys[0], "name": "New", "cols": (1, 1, 1)},
|
||||||
@@ -780,30 +708,30 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
|||||||
assert theProject.setStatusColours([], []) is False
|
assert theProject.setStatusColours([], []) is False
|
||||||
assert theProject.setStatusColours(newList, []) is True
|
assert theProject.setStatusColours(newList, []) is True
|
||||||
|
|
||||||
assert theProject.statusItems.name(statusKeys[0]) == "New"
|
assert theProject.data.itemStatus.name(statusKeys[0]) == "New"
|
||||||
assert theProject.statusItems.name(statusKeys[1]) == "Draft"
|
assert theProject.data.itemStatus.name(statusKeys[1]) == "Draft"
|
||||||
assert theProject.statusItems.name(statusKeys[2]) == "Note"
|
assert theProject.data.itemStatus.name(statusKeys[2]) == "Note"
|
||||||
assert theProject.statusItems.name(statusKeys[3]) == "Edited"
|
assert theProject.data.itemStatus.name(statusKeys[3]) == "Edited"
|
||||||
assert theProject.statusItems.cols(statusKeys[0]) == (1, 1, 1)
|
assert theProject.data.itemStatus.cols(statusKeys[0]) == (1, 1, 1)
|
||||||
assert theProject.statusItems.cols(statusKeys[1]) == (2, 2, 2)
|
assert theProject.data.itemStatus.cols(statusKeys[1]) == (2, 2, 2)
|
||||||
assert theProject.statusItems.cols(statusKeys[2]) == (3, 3, 3)
|
assert theProject.data.itemStatus.cols(statusKeys[2]) == (3, 3, 3)
|
||||||
assert theProject.statusItems.cols(statusKeys[3]) == (4, 4, 4)
|
assert theProject.data.itemStatus.cols(statusKeys[3]) == (4, 4, 4)
|
||||||
|
|
||||||
# Check the new entry
|
# Check the new entry
|
||||||
lastKey = theProject.statusItems.check("Finished")
|
lastKey = theProject.data.itemStatus.check("s000010")
|
||||||
assert lastKey == "s000018"
|
assert lastKey == "s000010"
|
||||||
assert theProject.statusItems.name(lastKey) == "Finished"
|
assert theProject.data.itemStatus.name(lastKey) == "Finished"
|
||||||
assert theProject.statusItems.cols(lastKey) == (5, 5, 5)
|
assert theProject.data.itemStatus.cols(lastKey) == (5, 5, 5)
|
||||||
|
|
||||||
# Delete last entry
|
# Delete last entry
|
||||||
assert theProject.setStatusColours([], [lastKey]) is True
|
assert theProject.setStatusColours([], [lastKey]) is True
|
||||||
assert theProject.statusItems.name(lastKey) == "New"
|
assert theProject.data.itemStatus.name(lastKey) == "New"
|
||||||
|
|
||||||
# Change Importance
|
# Change Importance
|
||||||
# =================
|
# =================
|
||||||
|
|
||||||
fHandle = theProject.newFile("Jane Doe", "0000000000012")
|
fHandle = theProject.newFile("Jane Doe", C.hCharRoot)
|
||||||
theProject.tree[fHandle].setImport("Main")
|
theProject.tree[fHandle].setImport(importKeys[3])
|
||||||
|
|
||||||
assert theProject.tree[fHandle].itemImport == importKeys[3]
|
assert theProject.tree[fHandle].itemImport == importKeys[3]
|
||||||
newList = [
|
newList = [
|
||||||
@@ -817,53 +745,41 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
|||||||
assert theProject.setImportColours([], []) is False
|
assert theProject.setImportColours([], []) is False
|
||||||
assert theProject.setImportColours(newList, []) is True
|
assert theProject.setImportColours(newList, []) is True
|
||||||
|
|
||||||
assert theProject.importItems.name(importKeys[0]) == "New"
|
assert theProject.data.itemImport.name(importKeys[0]) == "New"
|
||||||
assert theProject.importItems.name(importKeys[1]) == "Minor"
|
assert theProject.data.itemImport.name(importKeys[1]) == "Minor"
|
||||||
assert theProject.importItems.name(importKeys[2]) == "Major"
|
assert theProject.data.itemImport.name(importKeys[2]) == "Major"
|
||||||
assert theProject.importItems.name(importKeys[3]) == "Min"
|
assert theProject.data.itemImport.name(importKeys[3]) == "Min"
|
||||||
assert theProject.importItems.cols(importKeys[0]) == (1, 1, 1)
|
assert theProject.data.itemImport.cols(importKeys[0]) == (1, 1, 1)
|
||||||
assert theProject.importItems.cols(importKeys[1]) == (2, 2, 2)
|
assert theProject.data.itemImport.cols(importKeys[1]) == (2, 2, 2)
|
||||||
assert theProject.importItems.cols(importKeys[2]) == (3, 3, 3)
|
assert theProject.data.itemImport.cols(importKeys[2]) == (3, 3, 3)
|
||||||
assert theProject.importItems.cols(importKeys[3]) == (4, 4, 4)
|
assert theProject.data.itemImport.cols(importKeys[3]) == (4, 4, 4)
|
||||||
|
|
||||||
# Check the new entry
|
# Check the new entry
|
||||||
lastKey = theProject.importItems.check("Max")
|
lastKey = theProject.data.itemImport.check("i000012")
|
||||||
assert lastKey == "i00001a"
|
assert lastKey == "i000012"
|
||||||
assert theProject.importItems.name(lastKey) == "Max"
|
assert theProject.data.itemImport.name(lastKey) == "Max"
|
||||||
assert theProject.importItems.cols(lastKey) == (5, 5, 5)
|
assert theProject.data.itemImport.cols(lastKey) == (5, 5, 5)
|
||||||
|
|
||||||
# Delete last entry
|
# Delete last entry
|
||||||
assert theProject.setImportColours([], [lastKey]) is True
|
assert theProject.setImportColours([], [lastKey]) is True
|
||||||
assert theProject.importItems.name(lastKey) == "New"
|
assert theProject.data.itemImport.name(lastKey) == "New"
|
||||||
|
|
||||||
# Delete Status/Import
|
# Delete Status/Import
|
||||||
# ====================
|
# ====================
|
||||||
|
|
||||||
theProject.statusItems.resetCounts()
|
theProject.data.itemStatus.resetCounts()
|
||||||
for key in list(theProject.statusItems.keys()):
|
for key in list(theProject.data.itemStatus.keys()):
|
||||||
assert theProject.statusItems.remove(key) is True
|
assert theProject.data.itemStatus.remove(key) is True
|
||||||
|
|
||||||
theProject.importItems.resetCounts()
|
theProject.data.itemImport.resetCounts()
|
||||||
for key in list(theProject.importItems.keys()):
|
for key in list(theProject.data.itemImport.keys()):
|
||||||
assert theProject.importItems.remove(key) is True
|
assert theProject.data.itemImport.remove(key) is True
|
||||||
|
|
||||||
assert len(theProject.statusItems) == 0
|
assert len(theProject.data.itemStatus) == 0
|
||||||
assert len(theProject.importItems) == 0
|
assert len(theProject.data.itemImport) == 0
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
assert theProject.closeProject() is True
|
assert theProject.closeProject() is True
|
||||||
|
|
||||||
# This should restore the default status/import labels
|
|
||||||
assert theProject.openProject(fncDir) is True
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.statusItems.name("s000023") == "New"
|
|
||||||
assert theProject.statusItems.name("s000024") == "Note"
|
|
||||||
assert theProject.statusItems.name("s000025") == "Draft"
|
|
||||||
assert theProject.statusItems.name("s000026") == "Finished"
|
|
||||||
assert theProject.importItems.name("i000027") == "New"
|
|
||||||
assert theProject.importItems.name("i000028") == "Minor"
|
|
||||||
assert theProject.importItems.name("i000029") == "Major"
|
|
||||||
assert theProject.importItems.name("i00002a") == "Main"
|
|
||||||
|
|
||||||
# END Test testCoreProject_StatusImport
|
# END Test testCoreProject_StatusImport
|
||||||
|
|
||||||
|
|
||||||
@@ -895,117 +811,104 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
|
|||||||
assert theProject.setProjectPath(fncDir)
|
assert theProject.setProjectPath(fncDir)
|
||||||
|
|
||||||
# Project Name
|
# Project Name
|
||||||
assert theProject.setProjectName(" A Name ")
|
theProject.data.setName(" A Name ")
|
||||||
assert theProject.projName == "A Name"
|
assert theProject.data.name == "A Name"
|
||||||
|
|
||||||
# Project Title
|
# Project Title
|
||||||
assert theProject.setBookTitle(" A Title ")
|
theProject.data.setTitle(" A Title ")
|
||||||
assert theProject.bookTitle == "A Title"
|
assert theProject.data.title == "A Title"
|
||||||
|
|
||||||
# Project Authors
|
# Project Authors
|
||||||
# Check that the list is cleaned up and that it can be extracted as
|
# Check that the list is cleaned up and that it can be extracted as
|
||||||
# a properly formatted string, depending on number of names
|
# a properly formatted string, depending on number of names
|
||||||
assert not theProject.setBookAuthors([])
|
theProject.data.setAuthors([])
|
||||||
assert theProject.setBookAuthors(" Jane Doe \n John Doh \n ")
|
assert theProject.data.authors == []
|
||||||
assert theProject.bookAuthors == ["Jane Doe", "John Doh"]
|
theProject.data.setAuthors(" Jane Doe \n John Doh \n ")
|
||||||
|
assert theProject.data.authors == ["Jane Doe", "John Doh"]
|
||||||
|
|
||||||
assert theProject.setBookAuthors("")
|
theProject.data.setAuthors("")
|
||||||
assert theProject.getAuthors() == ""
|
assert theProject.getFormattedAuthors() == ""
|
||||||
|
|
||||||
assert theProject.setBookAuthors("Jane Doe")
|
theProject.data.setAuthors("Jane Doe")
|
||||||
assert theProject.getAuthors() == "Jane Doe"
|
assert theProject.getFormattedAuthors() == "Jane Doe"
|
||||||
|
|
||||||
assert theProject.setBookAuthors("Jane Doe\nJohn Doh")
|
theProject.data.setAuthors("Jane Doe\nJohn Doh")
|
||||||
assert theProject.getAuthors() == "Jane Doe and John Doh"
|
assert theProject.getFormattedAuthors() == "Jane Doe and John Doh"
|
||||||
|
|
||||||
assert theProject.setBookAuthors("Jane Doe\nJohn Doh\nBod Owens")
|
theProject.data.setAuthors("Jane Doe\nJohn Doh\nBod Owens")
|
||||||
assert theProject.getAuthors() == "Jane Doe, John Doh and Bod Owens"
|
assert theProject.getFormattedAuthors() == "Jane Doe, John Doh and Bod Owens"
|
||||||
|
|
||||||
# Edit Time
|
# Edit Time
|
||||||
theProject.editTime = 1234
|
theProject.data.setEditTime(1234)
|
||||||
theProject.projOpened = 1600000000
|
theProject._projOpened = 1600000000
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("novelwriter.core.project.time", lambda: 1600005600)
|
mp.setattr("novelwriter.core.project.time", lambda: 1600005600)
|
||||||
assert theProject.getCurrentEditTime() == 6834
|
assert theProject.getCurrentEditTime() == 6834
|
||||||
|
|
||||||
# Trash folder
|
# Trash folder
|
||||||
# Should create on first call, and just returned on later calls
|
# Should create on first call, and just returned on later calls
|
||||||
hTrash = "0000000000018"
|
hTrash = "0000000000010"
|
||||||
assert theProject.tree[hTrash] is None
|
assert theProject.tree[hTrash] is None
|
||||||
assert theProject.trashFolder() == hTrash
|
assert theProject.trashFolder() == hTrash
|
||||||
assert theProject.trashFolder() == hTrash
|
assert theProject.trashFolder() == hTrash
|
||||||
|
|
||||||
# Project backup
|
|
||||||
assert theProject.doBackup is True
|
|
||||||
assert theProject.setProjBackup(False)
|
|
||||||
assert theProject.doBackup is False
|
|
||||||
|
|
||||||
assert not theProject.setProjBackup(True)
|
|
||||||
theProject.mainConf.backupPath = tmpDir
|
|
||||||
assert theProject.setProjBackup(True)
|
|
||||||
|
|
||||||
assert theProject.setProjectName("")
|
|
||||||
assert not theProject.setProjBackup(True)
|
|
||||||
assert theProject.setProjectName("A Name")
|
|
||||||
assert theProject.setProjBackup(True)
|
|
||||||
|
|
||||||
# Spell check
|
# Spell check
|
||||||
theProject.projChanged = False
|
theProject.setProjectChanged(False)
|
||||||
assert theProject.setSpellCheck(True)
|
theProject.data.setSpellCheck(True)
|
||||||
assert not theProject.setSpellCheck(False)
|
theProject.data.setSpellCheck(False)
|
||||||
assert theProject.projChanged
|
assert theProject.projChanged
|
||||||
|
|
||||||
# Spell language
|
# Spell language
|
||||||
theProject.projChanged = False
|
theProject.setProjectChanged(False)
|
||||||
assert theProject.projSpell is None
|
assert theProject.data.spellLang is None
|
||||||
assert theProject.setSpellLang(None) is False
|
theProject.data.setSpellLang(None)
|
||||||
assert theProject.projSpell is None
|
assert theProject.data.spellLang is None
|
||||||
assert theProject.setSpellLang("None") is False # Should be interpreded as None
|
theProject.data.setSpellLang("None") # Should be interpreded as None
|
||||||
assert theProject.projSpell is None
|
assert theProject.data.spellLang is None
|
||||||
assert theProject.setSpellLang("en_GB")
|
theProject.data.setSpellLang("en_GB")
|
||||||
assert theProject.projSpell == "en_GB"
|
assert theProject.data.spellLang == "en_GB"
|
||||||
assert theProject.projChanged
|
assert theProject.projChanged
|
||||||
|
|
||||||
# Project Language
|
# Project Language
|
||||||
theProject.projChanged = False
|
theProject.setProjectChanged(False)
|
||||||
theProject.projLang = "en"
|
theProject.data.setLanguage("en")
|
||||||
assert theProject.setProjectLang(None) is True
|
assert theProject.setProjectLang(None) is True
|
||||||
assert theProject.projLang is None
|
assert theProject.data.language is None
|
||||||
assert theProject.setProjectLang("en_GB") is True
|
assert theProject.setProjectLang("en_GB") is True
|
||||||
assert theProject.projLang == "en_GB"
|
assert theProject.data.language == "en_GB"
|
||||||
|
|
||||||
# Language Lookup
|
# Language Lookup
|
||||||
assert theProject.localLookup(1) == "One"
|
assert theProject.localLookup(1) == "One"
|
||||||
assert theProject.localLookup(10) == "Ten"
|
assert theProject.localLookup(10) == "Ten"
|
||||||
|
|
||||||
# Last edited
|
# Last edited
|
||||||
theProject.projChanged = False
|
theProject.setProjectChanged(False)
|
||||||
assert theProject.setLastEdited("0123456789abc")
|
theProject._data.setLastHandle("0123456789abc", "editor")
|
||||||
assert theProject.lastEdited == "0123456789abc"
|
assert theProject._data.getLastHandle("editor") == "0123456789abc"
|
||||||
assert theProject.projChanged
|
assert theProject.projChanged
|
||||||
|
|
||||||
# Last viewed
|
# Last viewed
|
||||||
theProject.projChanged = False
|
theProject.setProjectChanged(False)
|
||||||
assert theProject.setLastViewed("0123456789abc")
|
theProject._data.setLastHandle("0123456789abc", "viewer")
|
||||||
assert theProject.lastViewed == "0123456789abc"
|
assert theProject._data.getLastHandle("viewer") == "0123456789abc"
|
||||||
assert theProject.projChanged
|
assert theProject.projChanged
|
||||||
|
|
||||||
# Autoreplace
|
# Autoreplace
|
||||||
theProject.projChanged = False
|
theProject.setProjectChanged(False)
|
||||||
assert theProject.setAutoReplace({"A": "B", "C": "D"})
|
theProject.data.setAutoReplace({"A": "B", "C": "D"})
|
||||||
assert theProject.autoReplace == {"A": "B", "C": "D"}
|
assert theProject.data.autoReplace == {"A": "B", "C": "D"}
|
||||||
assert theProject.projChanged
|
assert theProject.projChanged
|
||||||
|
|
||||||
# Change project tree order
|
# Change project tree order
|
||||||
oldOrder = [
|
oldOrder = [
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000008", "0000000000009", "000000000000a",
|
||||||
"0000000000013", "0000000000014", "0000000000015",
|
"000000000000b", "000000000000c", "000000000000d",
|
||||||
"0000000000016", "0000000000017", "0000000000018",
|
"000000000000e", "000000000000f", "0000000000010",
|
||||||
]
|
]
|
||||||
newOrder = [
|
newOrder = [
|
||||||
"0000000000013", "0000000000014", "0000000000015",
|
"000000000000b", "000000000000c", "000000000000d",
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000008", "0000000000009", "000000000000a",
|
||||||
"0000000000016", "0000000000017",
|
"000000000000e", "000000000000f",
|
||||||
]
|
]
|
||||||
assert theProject.tree.handles() == oldOrder
|
assert theProject.tree.handles() == oldOrder
|
||||||
assert theProject.setTreeOrder(newOrder)
|
assert theProject.setTreeOrder(newOrder)
|
||||||
@@ -1014,8 +917,8 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
|
|||||||
assert theProject.tree.handles() == oldOrder
|
assert theProject.tree.handles() == oldOrder
|
||||||
|
|
||||||
# Session stats
|
# Session stats
|
||||||
theProject.currWCount = 200
|
theProject._data._initCounts = [50, 50]
|
||||||
theProject.lastWCount = 100
|
theProject._data._currCounts = [100, 100]
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("os.path.isdir", lambda *a, **k: False)
|
mp.setattr("os.path.isdir", lambda *a, **k: False)
|
||||||
assert not theProject._appendSessionStats(idleTime=0)
|
assert not theProject._appendSessionStats(idleTime=0)
|
||||||
@@ -1029,9 +932,8 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
|
|||||||
assert theProject.projMeta == os.path.join(fncDir, "meta")
|
assert theProject.projMeta == os.path.join(fncDir, "meta")
|
||||||
statsFile = os.path.join(theProject.projMeta, nwFiles.SESS_STATS)
|
statsFile = os.path.join(theProject.projMeta, nwFiles.SESS_STATS)
|
||||||
|
|
||||||
theProject.projOpened = 1600002000
|
theProject._projOpened = 1600002000
|
||||||
theProject.currNovelWC = 200
|
theProject._data._currCounts = [200, 100]
|
||||||
theProject.currNotesWC = 100
|
|
||||||
|
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("novelwriter.core.project.time", lambda: 1600005600)
|
mp.setattr("novelwriter.core.project.time", lambda: 1600005600)
|
||||||
@@ -1043,31 +945,6 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
|
|||||||
"%s %s 200 100 99\n"
|
"%s %s 200 100 99\n"
|
||||||
) % (formatTimeStamp(1600002000), formatTimeStamp(1600005600))
|
) % (formatTimeStamp(1600002000), formatTimeStamp(1600005600))
|
||||||
|
|
||||||
# Pack XML Value
|
|
||||||
xElem = etree.Element("element")
|
|
||||||
theProject._packProjectValue(xElem, "A", "B", allowNone=False)
|
|
||||||
assert etree.tostring(xElem, pretty_print=False, encoding="utf-8") == (
|
|
||||||
b"<element><A>B</A></element>"
|
|
||||||
)
|
|
||||||
|
|
||||||
xElem = etree.Element("element")
|
|
||||||
theProject._packProjectValue(xElem, "A", "", allowNone=False)
|
|
||||||
assert etree.tostring(xElem, pretty_print=False, encoding="utf-8") == (
|
|
||||||
b"<element/>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Pack XML Key/Value
|
|
||||||
xElem = etree.Element("element")
|
|
||||||
theProject._packProjectKeyValue(xElem, "item", {"A": "B", "C": "D"})
|
|
||||||
assert etree.tostring(xElem, pretty_print=False, encoding="utf-8") == (
|
|
||||||
b"<element>"
|
|
||||||
b"<item>"
|
|
||||||
b"<entry key=\"A\">B</entry>"
|
|
||||||
b"<entry key=\"C\">D</entry>"
|
|
||||||
b"</item>"
|
|
||||||
b"</element>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# END Test testCoreProject_Methods
|
# END Test testCoreProject_Methods
|
||||||
|
|
||||||
|
|
||||||
@@ -1305,14 +1182,14 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
|
def testCoreProject_Backup(monkeypatch, mockGUI, fncDir, tmpDir):
|
||||||
"""Test the automated backup feature of the project class. The test
|
"""Test the automated backup feature of the project class. The test
|
||||||
creates a backup of the Minimal test project, and then unzips the
|
creates a backup of the Minimal test project, and then unzips the
|
||||||
backupd file and checks that the project XML file is identical to
|
backupd file and checks that the project XML file is identical to
|
||||||
the original file.
|
the original file.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
assert theProject.openProject(nwMinimal)
|
buildTestProject(theProject, fncDir)
|
||||||
|
|
||||||
# Test faulty settings
|
# Test faulty settings
|
||||||
|
|
||||||
@@ -1327,20 +1204,20 @@ def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
|
|||||||
|
|
||||||
# Missing project name
|
# Missing project name
|
||||||
theProject.mainConf.backupPath = tmpDir
|
theProject.mainConf.backupPath = tmpDir
|
||||||
theProject.projName = ""
|
theProject.data.setName("")
|
||||||
assert theProject.zipIt(doNotify=False) is False
|
assert theProject.zipIt(doNotify=False) is False
|
||||||
|
|
||||||
# Non-existent folder
|
# Non-existent folder
|
||||||
theProject.mainConf.backupPath = os.path.join(tmpDir, "nonexistent")
|
theProject.mainConf.backupPath = os.path.join(tmpDir, "nonexistent")
|
||||||
theProject.projName = "Test Minimal"
|
theProject.data.setName("Test Minimal")
|
||||||
assert theProject.zipIt(doNotify=False) is False
|
assert theProject.zipIt(doNotify=False) is False
|
||||||
|
|
||||||
# Same folder as project (causes infinite loop in zipping)
|
# Same folder as project (causes infinite loop in zipping)
|
||||||
theProject.mainConf.backupPath = nwMinimal
|
theProject.mainConf.backupPath = fncDir
|
||||||
assert theProject.zipIt(doNotify=False) is False
|
assert theProject.zipIt(doNotify=False) is False
|
||||||
|
|
||||||
# Subfolder of project (causes infinite loop in zipping)
|
# Subfolder of project (causes infinite loop in zipping)
|
||||||
theProject.mainConf.backupPath = os.path.join(nwMinimal, "subdir")
|
theProject.mainConf.backupPath = os.path.join(fncDir, "subdir")
|
||||||
assert theProject.zipIt(doNotify=False) is False
|
assert theProject.zipIt(doNotify=False) is False
|
||||||
|
|
||||||
# Set a valid folder
|
# Set a valid folder
|
||||||
@@ -1372,7 +1249,7 @@ def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
|
|||||||
|
|
||||||
# Check that the main project file was restored
|
# Check that the main project file was restored
|
||||||
assert cmpFiles(
|
assert cmpFiles(
|
||||||
os.path.join(nwMinimal, "nwProject.nwx"),
|
os.path.join(fncDir, "nwProject.nwx"),
|
||||||
os.path.join(tmpDir, "extract", "nwProject.nwx")
|
os.path.join(tmpDir, "extract", "nwProject.nwx")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,798 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – ProjectXMLReader/Writer Class Tester
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2022, 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 json
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from mock import causeOSError
|
||||||
|
from tools import cmpFiles, writeFile
|
||||||
|
|
||||||
|
from novelwriter.core.item import NWItem
|
||||||
|
from novelwriter.core.project import NWProjectData
|
||||||
|
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
||||||
|
|
||||||
|
|
||||||
|
class MockProject:
|
||||||
|
def setProjectChanged(self, *a):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreProjectXML_ReadCurrent(monkeypatch, filesDir, fncDir, outDir, refDir):
|
||||||
|
"""Test reading the current XML file format.
|
||||||
|
"""
|
||||||
|
refFile = os.path.join(filesDir, "nwProject-1.4.nwx")
|
||||||
|
xmlFile = os.path.join(fncDir, "nwProject-1.4.nwx")
|
||||||
|
bakFile = os.path.join(fncDir, "nwProject-1.4.bak")
|
||||||
|
outFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
|
|
||||||
|
xmlReader = ProjectXMLReader(xmlFile)
|
||||||
|
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||||
|
|
||||||
|
data = NWProjectData(MockProject())
|
||||||
|
content = []
|
||||||
|
|
||||||
|
# With no valid files, the read should fail
|
||||||
|
writeFile(xmlFile, "")
|
||||||
|
assert xmlReader.read(data, content) is False
|
||||||
|
assert xmlReader.state == XMLReadState.CANNOT_PARSE
|
||||||
|
|
||||||
|
# Also add an invalid backup file
|
||||||
|
writeFile(bakFile, "")
|
||||||
|
assert xmlReader.read(data, content) is False
|
||||||
|
assert xmlReader.state == XMLReadState.CANNOT_PARSE
|
||||||
|
|
||||||
|
# Add a valid backup file, that is not novelWriter
|
||||||
|
writeFile(bakFile, "<xml/>")
|
||||||
|
assert xmlReader.read(data, content) is False
|
||||||
|
assert xmlReader.state == XMLReadState.NOT_NWX_FILE
|
||||||
|
|
||||||
|
# Add a valid project file, that is not novelWriter
|
||||||
|
writeFile(xmlFile, "<xml/>")
|
||||||
|
assert xmlReader.read(data, content) is False
|
||||||
|
assert xmlReader.state == XMLReadState.NOT_NWX_FILE
|
||||||
|
|
||||||
|
# Add a valid novelwriter file without a file version
|
||||||
|
writeFile(xmlFile, "<novelWriterXML/>")
|
||||||
|
assert xmlReader.read(data, content) is False
|
||||||
|
assert xmlReader.state == XMLReadState.UNKNOWN_VERSION
|
||||||
|
|
||||||
|
# Check parsing of unkown sections
|
||||||
|
writeFile(xmlFile, (
|
||||||
|
"<novelWriterXML fileVersion='1.4'>"
|
||||||
|
" <project>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
" </project>"
|
||||||
|
" <settings>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
" </settings>"
|
||||||
|
" <content>"
|
||||||
|
" <item>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
" </item>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
" </content>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
"</novelWriterXML>"
|
||||||
|
))
|
||||||
|
assert xmlReader.read(data, content) is True
|
||||||
|
assert xmlReader.state == XMLReadState.PARSED_OK
|
||||||
|
|
||||||
|
writeFile(xmlFile, (
|
||||||
|
"<novelWriterXML fileVersion='1.0'>"
|
||||||
|
" <project>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
" </project>"
|
||||||
|
" <settings>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
" </settings>"
|
||||||
|
" <content>"
|
||||||
|
" <item>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
" </item>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
" </content>"
|
||||||
|
" <stuff></stuff>"
|
||||||
|
"</novelWriterXML>"
|
||||||
|
))
|
||||||
|
assert xmlReader.read(data, content) is True
|
||||||
|
assert xmlReader.state == XMLReadState.WAS_LEGACY
|
||||||
|
|
||||||
|
# Reset data objects
|
||||||
|
data = NWProjectData(MockProject())
|
||||||
|
content = []
|
||||||
|
|
||||||
|
# Parse a valid, complete file
|
||||||
|
shutil.copy(refFile, xmlFile)
|
||||||
|
assert xmlReader.read(data, content) is True
|
||||||
|
assert xmlReader.state == XMLReadState.PARSED_OK
|
||||||
|
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||||
|
assert xmlReader.xmlVersion == 0x0104
|
||||||
|
assert xmlReader.appVersion == "2.0-rc1"
|
||||||
|
assert xmlReader.hexVersion == "0x020000c1"
|
||||||
|
|
||||||
|
# Check loaded data
|
||||||
|
assert data.name == "Sample Project"
|
||||||
|
assert data.title == "Sample Project"
|
||||||
|
assert data.authors == ["Jane Smith", "Jay Doh"]
|
||||||
|
assert data.saveCount == 5
|
||||||
|
assert data.autoCount == 10
|
||||||
|
assert data.editTime == 1000
|
||||||
|
|
||||||
|
assert data.doBackup is True
|
||||||
|
assert data.language == "en_GB"
|
||||||
|
assert data.spellCheck is True
|
||||||
|
assert data.spellLang == "en_GB"
|
||||||
|
assert data.initCounts == (954, 409)
|
||||||
|
assert data.currCounts == (954, 409)
|
||||||
|
|
||||||
|
assert data.getLastHandle("editor") == "636b6aa9b697b"
|
||||||
|
assert data.getLastHandle("viewer") == "636b6aa9b697b"
|
||||||
|
assert data.getLastHandle("novelTree") == "7031beac91f75"
|
||||||
|
assert data.getLastHandle("outline") == "7031beac91f75"
|
||||||
|
|
||||||
|
assert data.getTitleFormat("title") == "%title%"
|
||||||
|
assert data.getTitleFormat("chapter") == "Chapter %chw%: %title%"
|
||||||
|
assert data.getTitleFormat("unnumbered") == "%title%"
|
||||||
|
assert data.getTitleFormat("scene") == "Scene %ch%.%sc%: %title%"
|
||||||
|
assert data.getTitleFormat("section") == ""
|
||||||
|
|
||||||
|
assert data.itemStatus.name("sf12341") == "New"
|
||||||
|
assert data.itemStatus.name("sf24ce6") == "Notes"
|
||||||
|
assert data.itemStatus.name("sc24b8f") == "Started"
|
||||||
|
assert data.itemStatus.name("s90e6c9") == "1st Draft"
|
||||||
|
assert data.itemStatus.name("sd51c5b") == "2nd Draft"
|
||||||
|
assert data.itemStatus.name("s8ae72a") == "3rd Draft"
|
||||||
|
assert data.itemStatus.name("s78ea90") == "Finished"
|
||||||
|
|
||||||
|
assert data.itemImport.name("ia857f0") == "None"
|
||||||
|
assert data.itemImport.name("icfb3a5") == "Minor"
|
||||||
|
assert data.itemImport.name("i2d7a54") == "Major"
|
||||||
|
assert data.itemImport.name("i56be10") == "Main"
|
||||||
|
|
||||||
|
assert data.itemStatus.cols("sf12341") == (100, 100, 100)
|
||||||
|
assert data.itemStatus.cols("sf24ce6") == (200, 50, 0)
|
||||||
|
assert data.itemStatus.cols("sc24b8f") == (182, 60, 0)
|
||||||
|
assert data.itemStatus.cols("s90e6c9") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("sd51c5b") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s8ae72a") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s78ea90") == (58, 180, 58)
|
||||||
|
|
||||||
|
assert data.itemImport.cols("ia857f0") == (100, 100, 100)
|
||||||
|
assert data.itemImport.cols("icfb3a5") == (0, 122, 188)
|
||||||
|
assert data.itemImport.cols("i2d7a54") == (21, 0, 180)
|
||||||
|
assert data.itemImport.cols("i56be10") == (117, 0, 175)
|
||||||
|
|
||||||
|
assert data.itemStatus.count("sf12341") == 4
|
||||||
|
assert data.itemStatus.count("sf24ce6") == 2
|
||||||
|
assert data.itemStatus.count("sc24b8f") == 3
|
||||||
|
assert data.itemStatus.count("s90e6c9") == 7
|
||||||
|
assert data.itemStatus.count("sd51c5b") == 0
|
||||||
|
assert data.itemStatus.count("s8ae72a") == 0
|
||||||
|
assert data.itemStatus.count("s78ea90") == 1
|
||||||
|
|
||||||
|
assert data.itemImport.count("ia857f0") == 5
|
||||||
|
assert data.itemImport.count("icfb3a5") == 2
|
||||||
|
assert data.itemImport.count("i2d7a54") == 2
|
||||||
|
assert data.itemImport.count("i56be10") == 1
|
||||||
|
|
||||||
|
# Compare content
|
||||||
|
dumpFile = os.path.join(outDir, "projectXML_ReadCurrent.json")
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadCurrent.json")
|
||||||
|
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||||
|
json.dump(content, dump, indent=2)
|
||||||
|
assert cmpFiles(dumpFile, compFile)
|
||||||
|
|
||||||
|
packedContent = []
|
||||||
|
mockProject = MockProject()
|
||||||
|
mockProject.__setattr__("data", data)
|
||||||
|
for entry in content:
|
||||||
|
item = NWItem(mockProject)
|
||||||
|
item.unpack(entry)
|
||||||
|
packedContent.append(item.pack())
|
||||||
|
|
||||||
|
# Save the project again, which should produce an identical project xml
|
||||||
|
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||||
|
xmlWriter = ProjectXMLWriter(fncDir)
|
||||||
|
|
||||||
|
# Fail saving
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("builtins.open", causeOSError)
|
||||||
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is False
|
||||||
|
assert str(xmlWriter.error) == "Mock OSError"
|
||||||
|
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("os.replace", causeOSError)
|
||||||
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is False
|
||||||
|
assert str(xmlWriter.error) == "Mock OSError"
|
||||||
|
|
||||||
|
# Successful save (should be twice)
|
||||||
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||||
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||||
|
assert cmpFiles(outFile, xmlFile)
|
||||||
|
|
||||||
|
# END Test testCoreProjectXML_ReadCurrent
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreProjectXML_ReadLegacy10(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||||
|
"""Test reading the version 1.0 XML file format.
|
||||||
|
"""
|
||||||
|
refFile = os.path.join(filesDir, "nwProject-1.0.nwx")
|
||||||
|
xmlFile = os.path.join(fncDir, "nwProject-1.0.nwx")
|
||||||
|
outFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
|
shutil.copy(refFile, xmlFile)
|
||||||
|
|
||||||
|
xmlReader = ProjectXMLReader(xmlFile)
|
||||||
|
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||||
|
|
||||||
|
data = NWProjectData(MockProject())
|
||||||
|
content = []
|
||||||
|
|
||||||
|
assert xmlReader.read(data, content) is True
|
||||||
|
assert xmlReader.state == XMLReadState.WAS_LEGACY
|
||||||
|
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||||
|
assert xmlReader.xmlVersion == 0x0100
|
||||||
|
assert xmlReader.appVersion == "0.6.1"
|
||||||
|
assert xmlReader.hexVersion == "0x000601f0"
|
||||||
|
|
||||||
|
# Check loaded data
|
||||||
|
assert data.name == "Sample Project"
|
||||||
|
assert data.title == "Sample Project"
|
||||||
|
assert data.authors == ["Jane Smith", "Jay Doh"]
|
||||||
|
assert data.saveCount == 0 # Doesn't exist in 1.0
|
||||||
|
assert data.autoCount == 0 # Doesn't exist in 1.0
|
||||||
|
assert data.editTime == 0 # Doesn't exist in 1.0
|
||||||
|
|
||||||
|
assert data.doBackup is True
|
||||||
|
assert data.language is None # Doesn't exist in 1.0
|
||||||
|
assert data.spellCheck is True
|
||||||
|
assert data.spellLang is None # Doesn't exist in 1.0
|
||||||
|
assert data.initCounts == (0, 0)
|
||||||
|
assert data.currCounts == (0, 0)
|
||||||
|
|
||||||
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
|
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.0
|
||||||
|
assert data.getLastHandle("outline") is None # Doesn't exist in 1.0
|
||||||
|
|
||||||
|
assert data.getTitleFormat("title") == "%title%"
|
||||||
|
assert data.getTitleFormat("chapter") == "Chapter %ch%: %title%"
|
||||||
|
assert data.getTitleFormat("unnumbered") == "%title%"
|
||||||
|
assert data.getTitleFormat("scene") == "Scene %ch%.%sc%: %title%"
|
||||||
|
assert data.getTitleFormat("section") == ""
|
||||||
|
|
||||||
|
assert data.itemStatus.name("s000000") == "New"
|
||||||
|
assert data.itemStatus.name("s000001") == "Notes"
|
||||||
|
assert data.itemStatus.name("s000002") == "Started"
|
||||||
|
assert data.itemStatus.name("s000003") == "1st Draft"
|
||||||
|
assert data.itemStatus.name("s000004") == "2nd Draft"
|
||||||
|
assert data.itemStatus.name("s000005") == "3rd Draft"
|
||||||
|
assert data.itemStatus.name("s000006") == "Finished"
|
||||||
|
|
||||||
|
assert data.itemImport.name("i000007") == "None"
|
||||||
|
assert data.itemImport.name("i000008") == "Minor"
|
||||||
|
assert data.itemImport.name("i000009") == "Major"
|
||||||
|
assert data.itemImport.name("i00000a") == "Main"
|
||||||
|
|
||||||
|
assert data.itemStatus.cols("s000000") == (100, 100, 100)
|
||||||
|
assert data.itemStatus.cols("s000001") == (200, 50, 0)
|
||||||
|
assert data.itemStatus.cols("s000002") == (182, 60, 0)
|
||||||
|
assert data.itemStatus.cols("s000003") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000004") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000005") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000006") == (58, 180, 58)
|
||||||
|
|
||||||
|
assert data.itemImport.cols("i000007") == (100, 100, 100)
|
||||||
|
assert data.itemImport.cols("i000008") == (0, 122, 188)
|
||||||
|
assert data.itemImport.cols("i000009") == (21, 0, 180)
|
||||||
|
assert data.itemImport.cols("i00000a") == (117, 0, 175)
|
||||||
|
|
||||||
|
assert data.itemStatus.count("s000000") == 0
|
||||||
|
assert data.itemStatus.count("s000001") == 0
|
||||||
|
assert data.itemStatus.count("s000002") == 0
|
||||||
|
assert data.itemStatus.count("s000003") == 0
|
||||||
|
assert data.itemStatus.count("s000004") == 0
|
||||||
|
assert data.itemStatus.count("s000005") == 0
|
||||||
|
assert data.itemStatus.count("s000006") == 0
|
||||||
|
|
||||||
|
assert data.itemImport.count("i000007") == 0
|
||||||
|
assert data.itemImport.count("i000008") == 0
|
||||||
|
assert data.itemImport.count("i000009") == 0
|
||||||
|
assert data.itemImport.count("i00000a") == 0
|
||||||
|
|
||||||
|
# Compare content
|
||||||
|
dumpFile = os.path.join(outDir, "projectXML_ReadLegacy10.json")
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadLegacy10.json")
|
||||||
|
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||||
|
json.dump(content, dump, indent=2)
|
||||||
|
assert cmpFiles(dumpFile, compFile)
|
||||||
|
|
||||||
|
packedContent = []
|
||||||
|
mockProject = MockProject()
|
||||||
|
mockProject.__setattr__("data", data)
|
||||||
|
status = {}
|
||||||
|
for entry in content:
|
||||||
|
item = NWItem(mockProject)
|
||||||
|
item.unpack(entry)
|
||||||
|
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||||
|
packedContent.append(item.pack())
|
||||||
|
|
||||||
|
assert status == {
|
||||||
|
"7031beac91f75": "Started",
|
||||||
|
"53b69b83cdafc": "Started",
|
||||||
|
"974e400180a99": "New",
|
||||||
|
"edca4be2fcaf8": "New",
|
||||||
|
"e7ded148d6e4a": "1st Draft",
|
||||||
|
"6a2d6d5f4f401": "Notes",
|
||||||
|
"636b6aa9b697b": "1st Draft",
|
||||||
|
"bc0cbd2a407f3": "1st Draft",
|
||||||
|
"ba8a28a246524": "Finished",
|
||||||
|
"96b68994dfa3d": "2nd Draft",
|
||||||
|
"88706ddc78b1b": "1st Draft",
|
||||||
|
"ae7339df26ded": "1st Draft",
|
||||||
|
"f6622b4617424": "None",
|
||||||
|
"f7e2d9f330615": "None",
|
||||||
|
"14298de4d9524": "Minor",
|
||||||
|
"bb2c23b3c42cc": "Major",
|
||||||
|
"15c4492bd5107": "None",
|
||||||
|
"b3e74dbc1f584": "Main",
|
||||||
|
"f1471bef9f2ae": "Minor",
|
||||||
|
"5eaea4e8cdee8": "Major",
|
||||||
|
"98acd8c76c93a": "None",
|
||||||
|
"b8136a5a774a0": "New",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save the project again, which should produce an identical project xml
|
||||||
|
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||||
|
xmlWriter = ProjectXMLWriter(fncDir)
|
||||||
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadLegacy10.nwx")
|
||||||
|
assert cmpFiles(outFile, compFile)
|
||||||
|
|
||||||
|
# END Test testCoreProjectXML_ReadLegacy10
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreProjectXML_ReadLegacy11(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||||
|
"""Test reading the version 1.1 XML file format.
|
||||||
|
"""
|
||||||
|
refFile = os.path.join(filesDir, "nwProject-1.1.nwx")
|
||||||
|
xmlFile = os.path.join(fncDir, "nwProject-1.1.nwx")
|
||||||
|
outFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
|
shutil.copy(refFile, xmlFile)
|
||||||
|
|
||||||
|
xmlReader = ProjectXMLReader(xmlFile)
|
||||||
|
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||||
|
|
||||||
|
data = NWProjectData(MockProject())
|
||||||
|
content = []
|
||||||
|
|
||||||
|
assert xmlReader.read(data, content) is True
|
||||||
|
assert xmlReader.state == XMLReadState.WAS_LEGACY
|
||||||
|
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||||
|
assert xmlReader.xmlVersion == 0x0101
|
||||||
|
assert xmlReader.appVersion == "0.9.2"
|
||||||
|
assert xmlReader.hexVersion == "0x000902f0"
|
||||||
|
|
||||||
|
# Check loaded data
|
||||||
|
assert data.name == "Sample Project"
|
||||||
|
assert data.title == "Sample Project"
|
||||||
|
assert data.authors == ["Jane Smith", "Jay Doh"]
|
||||||
|
assert data.saveCount == 5
|
||||||
|
assert data.autoCount == 10
|
||||||
|
assert data.editTime == 1000
|
||||||
|
|
||||||
|
assert data.doBackup is True
|
||||||
|
assert data.language is None # Doesn't exist in 1.1
|
||||||
|
assert data.spellCheck is True
|
||||||
|
assert data.spellLang is None # Doesn't exist in 1.1
|
||||||
|
assert data.initCounts == (0, 0)
|
||||||
|
assert data.currCounts == (0, 0)
|
||||||
|
|
||||||
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
|
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.1
|
||||||
|
assert data.getLastHandle("outline") is None # Doesn't exist in 1.1
|
||||||
|
|
||||||
|
assert data.getTitleFormat("title") == "%title%"
|
||||||
|
assert data.getTitleFormat("chapter") == "Chapter %ch%: %title%"
|
||||||
|
assert data.getTitleFormat("unnumbered") == "%title%"
|
||||||
|
assert data.getTitleFormat("scene") == "Scene %ch%.%sc%: %title%"
|
||||||
|
assert data.getTitleFormat("section") == ""
|
||||||
|
|
||||||
|
assert data.itemStatus.name("s000000") == "New"
|
||||||
|
assert data.itemStatus.name("s000001") == "Notes"
|
||||||
|
assert data.itemStatus.name("s000002") == "Started"
|
||||||
|
assert data.itemStatus.name("s000003") == "1st Draft"
|
||||||
|
assert data.itemStatus.name("s000004") == "2nd Draft"
|
||||||
|
assert data.itemStatus.name("s000005") == "3rd Draft"
|
||||||
|
assert data.itemStatus.name("s000006") == "Finished"
|
||||||
|
|
||||||
|
assert data.itemImport.name("i000007") == "None"
|
||||||
|
assert data.itemImport.name("i000008") == "Minor"
|
||||||
|
assert data.itemImport.name("i000009") == "Major"
|
||||||
|
assert data.itemImport.name("i00000a") == "Main"
|
||||||
|
|
||||||
|
assert data.itemStatus.cols("s000000") == (100, 100, 100)
|
||||||
|
assert data.itemStatus.cols("s000001") == (200, 50, 0)
|
||||||
|
assert data.itemStatus.cols("s000002") == (182, 60, 0)
|
||||||
|
assert data.itemStatus.cols("s000003") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000004") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000005") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000006") == (58, 180, 58)
|
||||||
|
|
||||||
|
assert data.itemImport.cols("i000007") == (100, 100, 100)
|
||||||
|
assert data.itemImport.cols("i000008") == (0, 122, 188)
|
||||||
|
assert data.itemImport.cols("i000009") == (21, 0, 180)
|
||||||
|
assert data.itemImport.cols("i00000a") == (117, 0, 175)
|
||||||
|
|
||||||
|
assert data.itemStatus.count("s000000") == 0
|
||||||
|
assert data.itemStatus.count("s000001") == 0
|
||||||
|
assert data.itemStatus.count("s000002") == 0
|
||||||
|
assert data.itemStatus.count("s000003") == 0
|
||||||
|
assert data.itemStatus.count("s000004") == 0
|
||||||
|
assert data.itemStatus.count("s000005") == 0
|
||||||
|
assert data.itemStatus.count("s000006") == 0
|
||||||
|
|
||||||
|
assert data.itemImport.count("i000007") == 0
|
||||||
|
assert data.itemImport.count("i000008") == 0
|
||||||
|
assert data.itemImport.count("i000009") == 0
|
||||||
|
assert data.itemImport.count("i00000a") == 0
|
||||||
|
|
||||||
|
# Compare content
|
||||||
|
dumpFile = os.path.join(outDir, "projectXML_ReadLegacy11.json")
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadLegacy11.json")
|
||||||
|
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||||
|
json.dump(content, dump, indent=2)
|
||||||
|
assert cmpFiles(dumpFile, compFile)
|
||||||
|
|
||||||
|
packedContent = []
|
||||||
|
mockProject = MockProject()
|
||||||
|
mockProject.__setattr__("data", data)
|
||||||
|
status = {}
|
||||||
|
for entry in content:
|
||||||
|
item = NWItem(mockProject)
|
||||||
|
item.unpack(entry)
|
||||||
|
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||||
|
packedContent.append(item.pack())
|
||||||
|
|
||||||
|
assert status == {
|
||||||
|
"7031beac91f75": "Started",
|
||||||
|
"53b69b83cdafc": "Started",
|
||||||
|
"974e400180a99": "New",
|
||||||
|
"edca4be2fcaf8": "New",
|
||||||
|
"e7ded148d6e4a": "1st Draft",
|
||||||
|
"6a2d6d5f4f401": "Notes",
|
||||||
|
"636b6aa9b697b": "1st Draft",
|
||||||
|
"bc0cbd2a407f3": "1st Draft",
|
||||||
|
"ba8a28a246524": "Finished",
|
||||||
|
"96b68994dfa3d": "2nd Draft",
|
||||||
|
"88706ddc78b1b": "1st Draft",
|
||||||
|
"ae7339df26ded": "1st Draft",
|
||||||
|
"f6622b4617424": "None",
|
||||||
|
"f7e2d9f330615": "None",
|
||||||
|
"14298de4d9524": "Minor",
|
||||||
|
"bb2c23b3c42cc": "Major",
|
||||||
|
"15c4492bd5107": "None",
|
||||||
|
"b3e74dbc1f584": "Main",
|
||||||
|
"f1471bef9f2ae": "Minor",
|
||||||
|
"5eaea4e8cdee8": "Major",
|
||||||
|
"98acd8c76c93a": "None",
|
||||||
|
"b8136a5a774a0": "New",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save the project again, which should produce an identical project xml
|
||||||
|
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||||
|
xmlWriter = ProjectXMLWriter(fncDir)
|
||||||
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadLegacy11.nwx")
|
||||||
|
assert cmpFiles(outFile, compFile)
|
||||||
|
|
||||||
|
# END Test testCoreProjectXML_ReadLegacy11
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreProjectXML_ReadLegacy12(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||||
|
"""Test reading the version 1.2 XML file format.
|
||||||
|
"""
|
||||||
|
refFile = os.path.join(filesDir, "nwProject-1.2.nwx")
|
||||||
|
xmlFile = os.path.join(fncDir, "nwProject-1.2.nwx")
|
||||||
|
outFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
|
shutil.copy(refFile, xmlFile)
|
||||||
|
|
||||||
|
xmlReader = ProjectXMLReader(xmlFile)
|
||||||
|
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||||
|
|
||||||
|
data = NWProjectData(MockProject())
|
||||||
|
content = []
|
||||||
|
|
||||||
|
assert xmlReader.read(data, content) is True
|
||||||
|
assert xmlReader.state == XMLReadState.WAS_LEGACY
|
||||||
|
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||||
|
assert xmlReader.xmlVersion == 0x0102
|
||||||
|
assert xmlReader.appVersion == "1.4.2"
|
||||||
|
assert xmlReader.hexVersion == "0x010402f0"
|
||||||
|
|
||||||
|
# Check loaded data
|
||||||
|
assert data.name == "Sample Project"
|
||||||
|
assert data.title == "Sample Project"
|
||||||
|
assert data.authors == ["Jane Smith", "Jay Doh"]
|
||||||
|
assert data.saveCount == 5
|
||||||
|
assert data.autoCount == 10
|
||||||
|
assert data.editTime == 1000
|
||||||
|
|
||||||
|
assert data.doBackup is True
|
||||||
|
assert data.language == "en_GB"
|
||||||
|
assert data.spellCheck is True
|
||||||
|
assert data.spellLang == "en_GB"
|
||||||
|
assert data.initCounts == (840, 376)
|
||||||
|
assert data.currCounts == (840, 376)
|
||||||
|
|
||||||
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
|
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.2
|
||||||
|
assert data.getLastHandle("outline") is None # Doesn't exist in 1.2
|
||||||
|
|
||||||
|
assert data.getTitleFormat("title") == "%title%"
|
||||||
|
assert data.getTitleFormat("chapter") == "Chapter %chw%: %title%"
|
||||||
|
assert data.getTitleFormat("unnumbered") == "%title%"
|
||||||
|
assert data.getTitleFormat("scene") == "Scene %ch%.%sc%: %title%"
|
||||||
|
assert data.getTitleFormat("section") == ""
|
||||||
|
|
||||||
|
assert data.itemStatus.name("s000000") == "New"
|
||||||
|
assert data.itemStatus.name("s000001") == "Notes"
|
||||||
|
assert data.itemStatus.name("s000002") == "Started"
|
||||||
|
assert data.itemStatus.name("s000003") == "1st Draft"
|
||||||
|
assert data.itemStatus.name("s000004") == "2nd Draft"
|
||||||
|
assert data.itemStatus.name("s000005") == "3rd Draft"
|
||||||
|
assert data.itemStatus.name("s000006") == "Finished"
|
||||||
|
|
||||||
|
assert data.itemImport.name("i000007") == "None"
|
||||||
|
assert data.itemImport.name("i000008") == "Minor"
|
||||||
|
assert data.itemImport.name("i000009") == "Major"
|
||||||
|
assert data.itemImport.name("i00000a") == "Main"
|
||||||
|
|
||||||
|
assert data.itemStatus.cols("s000000") == (100, 100, 100)
|
||||||
|
assert data.itemStatus.cols("s000001") == (200, 50, 0)
|
||||||
|
assert data.itemStatus.cols("s000002") == (182, 60, 0)
|
||||||
|
assert data.itemStatus.cols("s000003") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000004") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000005") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000006") == (58, 180, 58)
|
||||||
|
|
||||||
|
assert data.itemImport.cols("i000007") == (100, 100, 100)
|
||||||
|
assert data.itemImport.cols("i000008") == (0, 122, 188)
|
||||||
|
assert data.itemImport.cols("i000009") == (21, 0, 180)
|
||||||
|
assert data.itemImport.cols("i00000a") == (117, 0, 175)
|
||||||
|
|
||||||
|
assert data.itemStatus.count("s000000") == 0
|
||||||
|
assert data.itemStatus.count("s000001") == 0
|
||||||
|
assert data.itemStatus.count("s000002") == 0
|
||||||
|
assert data.itemStatus.count("s000003") == 0
|
||||||
|
assert data.itemStatus.count("s000004") == 0
|
||||||
|
assert data.itemStatus.count("s000005") == 0
|
||||||
|
assert data.itemStatus.count("s000006") == 0
|
||||||
|
|
||||||
|
assert data.itemImport.count("i000007") == 0
|
||||||
|
assert data.itemImport.count("i000008") == 0
|
||||||
|
assert data.itemImport.count("i000009") == 0
|
||||||
|
assert data.itemImport.count("i00000a") == 0
|
||||||
|
|
||||||
|
# Compare content
|
||||||
|
dumpFile = os.path.join(outDir, "projectXML_ReadLegacy12.json")
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadLegacy12.json")
|
||||||
|
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||||
|
json.dump(content, dump, indent=2)
|
||||||
|
assert cmpFiles(dumpFile, compFile)
|
||||||
|
|
||||||
|
packedContent = []
|
||||||
|
mockProject = MockProject()
|
||||||
|
mockProject.__setattr__("data", data)
|
||||||
|
status = {}
|
||||||
|
for entry in content:
|
||||||
|
item = NWItem(mockProject)
|
||||||
|
item.unpack(entry)
|
||||||
|
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||||
|
packedContent.append(item.pack())
|
||||||
|
|
||||||
|
assert status == {
|
||||||
|
"7031beac91f75": "Started",
|
||||||
|
"53b69b83cdafc": "Started",
|
||||||
|
"974e400180a99": "New",
|
||||||
|
"edca4be2fcaf8": "New",
|
||||||
|
"e7ded148d6e4a": "1st Draft",
|
||||||
|
"6a2d6d5f4f401": "Notes",
|
||||||
|
"636b6aa9b697b": "1st Draft",
|
||||||
|
"bc0cbd2a407f3": "1st Draft",
|
||||||
|
"ba8a28a246524": "New",
|
||||||
|
"96b68994dfa3d": "2nd Draft",
|
||||||
|
"88706ddc78b1b": "1st Draft",
|
||||||
|
"ae7339df26ded": "1st Draft",
|
||||||
|
"f6622b4617424": "None",
|
||||||
|
"f7e2d9f330615": "None",
|
||||||
|
"14298de4d9524": "Minor",
|
||||||
|
"bb2c23b3c42cc": "Major",
|
||||||
|
"15c4492bd5107": "None",
|
||||||
|
"b3e74dbc1f584": "Main",
|
||||||
|
"f1471bef9f2ae": "Minor",
|
||||||
|
"5eaea4e8cdee8": "Major",
|
||||||
|
"6827118336ac1": "New", # Is now treated as novel-like
|
||||||
|
"ae9bf3c3ea159": "New", # Is now treated as novel-like
|
||||||
|
"8a5deb88c0e97": "1st Draft",
|
||||||
|
"98acd8c76c93a": "None",
|
||||||
|
"b8136a5a774a0": "New",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save the project again, which should produce an identical project xml
|
||||||
|
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||||
|
xmlWriter = ProjectXMLWriter(fncDir)
|
||||||
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadLegacy12.nwx")
|
||||||
|
assert cmpFiles(outFile, compFile)
|
||||||
|
|
||||||
|
# END Test testCoreProjectXML_ReadLegacy12
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreProjectXML_ReadLegacy13(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||||
|
"""Test reading the version 1.3 XML file format.
|
||||||
|
"""
|
||||||
|
refFile = os.path.join(filesDir, "nwProject-1.3.nwx")
|
||||||
|
xmlFile = os.path.join(fncDir, "nwProject-1.3.nwx")
|
||||||
|
outFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
|
shutil.copy(refFile, xmlFile)
|
||||||
|
|
||||||
|
xmlReader = ProjectXMLReader(xmlFile)
|
||||||
|
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||||
|
|
||||||
|
data = NWProjectData(MockProject())
|
||||||
|
content = []
|
||||||
|
|
||||||
|
assert xmlReader.read(data, content) is True
|
||||||
|
assert xmlReader.state == XMLReadState.WAS_LEGACY
|
||||||
|
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||||
|
assert xmlReader.xmlVersion == 0x0103
|
||||||
|
assert xmlReader.appVersion == "1.6.6"
|
||||||
|
assert xmlReader.hexVersion == "0x010606f0"
|
||||||
|
|
||||||
|
# Check loaded data
|
||||||
|
assert data.name == "Sample Project"
|
||||||
|
assert data.title == "Sample Project"
|
||||||
|
assert data.authors == ["Jane Smith", "Jay Doh"]
|
||||||
|
assert data.saveCount == 5
|
||||||
|
assert data.autoCount == 10
|
||||||
|
assert data.editTime == 1000
|
||||||
|
|
||||||
|
assert data.doBackup is True
|
||||||
|
assert data.language == "en_GB"
|
||||||
|
assert data.spellCheck is True
|
||||||
|
assert data.spellLang == "en_GB"
|
||||||
|
assert data.initCounts == (830, 376)
|
||||||
|
assert data.currCounts == (830, 376)
|
||||||
|
|
||||||
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
|
assert data.getLastHandle("novelTree") is None # Doesn't exist in 1.3
|
||||||
|
assert data.getLastHandle("outline") is None # Doesn't exist in 1.3
|
||||||
|
|
||||||
|
assert data.getTitleFormat("title") == "%title%"
|
||||||
|
assert data.getTitleFormat("chapter") == "Chapter %chw%: %title%"
|
||||||
|
assert data.getTitleFormat("unnumbered") == "%title%"
|
||||||
|
assert data.getTitleFormat("scene") == "Scene %ch%.%sc%: %title%"
|
||||||
|
assert data.getTitleFormat("section") == ""
|
||||||
|
|
||||||
|
assert data.itemStatus.name("s000000") == "New"
|
||||||
|
assert data.itemStatus.name("s000001") == "Notes"
|
||||||
|
assert data.itemStatus.name("s000002") == "Started"
|
||||||
|
assert data.itemStatus.name("s000003") == "1st Draft"
|
||||||
|
assert data.itemStatus.name("s000004") == "2nd Draft"
|
||||||
|
assert data.itemStatus.name("s000005") == "3rd Draft"
|
||||||
|
assert data.itemStatus.name("s000006") == "Finished"
|
||||||
|
|
||||||
|
assert data.itemImport.name("i000007") == "None"
|
||||||
|
assert data.itemImport.name("i000008") == "Minor"
|
||||||
|
assert data.itemImport.name("i000009") == "Major"
|
||||||
|
assert data.itemImport.name("i00000a") == "Main"
|
||||||
|
|
||||||
|
assert data.itemStatus.cols("s000000") == (100, 100, 100)
|
||||||
|
assert data.itemStatus.cols("s000001") == (200, 50, 0)
|
||||||
|
assert data.itemStatus.cols("s000002") == (182, 60, 0)
|
||||||
|
assert data.itemStatus.cols("s000003") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000004") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000005") == (193, 129, 0)
|
||||||
|
assert data.itemStatus.cols("s000006") == (58, 180, 58)
|
||||||
|
|
||||||
|
assert data.itemImport.cols("i000007") == (100, 100, 100)
|
||||||
|
assert data.itemImport.cols("i000008") == (0, 122, 188)
|
||||||
|
assert data.itemImport.cols("i000009") == (21, 0, 180)
|
||||||
|
assert data.itemImport.cols("i00000a") == (117, 0, 175)
|
||||||
|
|
||||||
|
assert data.itemStatus.count("s000000") == 0
|
||||||
|
assert data.itemStatus.count("s000001") == 0
|
||||||
|
assert data.itemStatus.count("s000002") == 0
|
||||||
|
assert data.itemStatus.count("s000003") == 0
|
||||||
|
assert data.itemStatus.count("s000004") == 0
|
||||||
|
assert data.itemStatus.count("s000005") == 0
|
||||||
|
assert data.itemStatus.count("s000006") == 0
|
||||||
|
|
||||||
|
assert data.itemImport.count("i000007") == 0
|
||||||
|
assert data.itemImport.count("i000008") == 0
|
||||||
|
assert data.itemImport.count("i000009") == 0
|
||||||
|
assert data.itemImport.count("i00000a") == 0
|
||||||
|
|
||||||
|
# Compare content
|
||||||
|
dumpFile = os.path.join(outDir, "projectXML_ReadLegacy13.json")
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadLegacy13.json")
|
||||||
|
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||||
|
json.dump(content, dump, indent=2)
|
||||||
|
assert cmpFiles(dumpFile, compFile)
|
||||||
|
|
||||||
|
packedContent = []
|
||||||
|
mockProject = MockProject()
|
||||||
|
mockProject.__setattr__("data", data)
|
||||||
|
status = {}
|
||||||
|
for entry in content:
|
||||||
|
item = NWItem(mockProject)
|
||||||
|
item.unpack(entry)
|
||||||
|
status[item.itemHandle] = item.getImportStatus(incIcon=False)[0]
|
||||||
|
packedContent.append(item.pack())
|
||||||
|
|
||||||
|
assert status == {
|
||||||
|
"7031beac91f75": "Started",
|
||||||
|
"53b69b83cdafc": "Started",
|
||||||
|
"974e400180a99": "New",
|
||||||
|
"edca4be2fcaf8": "New",
|
||||||
|
"e7ded148d6e4a": "1st Draft",
|
||||||
|
"6a2d6d5f4f401": "Notes",
|
||||||
|
"636b6aa9b697b": "1st Draft",
|
||||||
|
"bc0cbd2a407f3": "1st Draft",
|
||||||
|
"ba8a28a246524": "New",
|
||||||
|
"96b68994dfa3d": "2nd Draft",
|
||||||
|
"88706ddc78b1b": "1st Draft",
|
||||||
|
"ae7339df26ded": "1st Draft",
|
||||||
|
"f6622b4617424": "None",
|
||||||
|
"f7e2d9f330615": "None",
|
||||||
|
"14298de4d9524": "Minor",
|
||||||
|
"bb2c23b3c42cc": "Major",
|
||||||
|
"15c4492bd5107": "None",
|
||||||
|
"b3e74dbc1f584": "Main",
|
||||||
|
"f1471bef9f2ae": "Minor",
|
||||||
|
"5eaea4e8cdee8": "Major",
|
||||||
|
"6827118336ac1": "New", # Is now treated as novel-like
|
||||||
|
"ae9bf3c3ea159": "New", # Is now treated as novel-like
|
||||||
|
"8a5deb88c0e97": "1st Draft",
|
||||||
|
"98acd8c76c93a": "None",
|
||||||
|
"b8136a5a774a0": "New",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save the project again, which should produce an identical project xml
|
||||||
|
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||||
|
xmlWriter = ProjectXMLWriter(fncDir)
|
||||||
|
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||||
|
compFile = os.path.join(refDir, "projectXML_ReadLegacy13.nwx")
|
||||||
|
assert cmpFiles(outFile, compFile)
|
||||||
|
|
||||||
|
# END Test testCoreProjectXML_ReadLegacy13
|
||||||
@@ -20,23 +20,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import random
|
|
||||||
|
|
||||||
from lxml import etree
|
from tools import C
|
||||||
|
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
|
|
||||||
from novelwriter.core.status import NWStatus
|
from novelwriter.core.status import NWStatus
|
||||||
|
|
||||||
statusKeys = ["sa3b179", "s1c8031", "s06671a", "sbdd640"]
|
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
|
||||||
importKeys = ["i466852", "i3eb13b", "i392456", "i23b8c1"]
|
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreStatus_Internal():
|
def testCoreStatus_Internal(mockRnd):
|
||||||
"""Test all the internal functions of the NWStatus class.
|
"""Test all the internal functions of the NWStatus class.
|
||||||
"""
|
"""
|
||||||
random.seed(42)
|
|
||||||
theStatus = NWStatus(NWStatus.STATUS)
|
theStatus = NWStatus(NWStatus.STATUS)
|
||||||
theImport = NWStatus(NWStatus.IMPORT)
|
theImport = NWStatus(NWStatus.IMPORT)
|
||||||
|
|
||||||
@@ -87,10 +85,9 @@ def testCoreStatus_Internal():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreStatus_Iterator():
|
def testCoreStatus_Iterator(mockRnd):
|
||||||
"""Test the iterator functions of the NWStatus class.
|
"""Test the iterator functions of the NWStatus class.
|
||||||
"""
|
"""
|
||||||
random.seed(42)
|
|
||||||
theStatus = NWStatus(NWStatus.STATUS)
|
theStatus = NWStatus(NWStatus.STATUS)
|
||||||
|
|
||||||
theStatus.write(None, "New", (100, 100, 100))
|
theStatus.write(None, "New", (100, 100, 100))
|
||||||
@@ -132,10 +129,9 @@ def testCoreStatus_Iterator():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreStatus_Entries():
|
def testCoreStatus_Entries(mockRnd):
|
||||||
"""Test all the simple setters for the NWStatus class.
|
"""Test all the simple setters for the NWStatus class.
|
||||||
"""
|
"""
|
||||||
random.seed(42)
|
|
||||||
theStatus = NWStatus(NWStatus.STATUS)
|
theStatus = NWStatus(NWStatus.STATUS)
|
||||||
|
|
||||||
# Write
|
# Write
|
||||||
@@ -161,14 +157,6 @@ def testCoreStatus_Entries():
|
|||||||
assert theStatus[statusKeys[3]]["name"] == "Entry 4"
|
assert theStatus[statusKeys[3]]["name"] == "Entry 4"
|
||||||
assert theStatus[statusKeys[3]]["cols"] == (100, 100, 100)
|
assert theStatus[statusKeys[3]]["cols"] == (100, 100, 100)
|
||||||
|
|
||||||
# Check reverse map
|
|
||||||
assert theStatus._reverse == {
|
|
||||||
"Entry 1": statusKeys[0],
|
|
||||||
"Entry 2": statusKeys[1],
|
|
||||||
"Entry 3": statusKeys[2],
|
|
||||||
"Entry 4": statusKeys[3],
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check
|
# Check
|
||||||
# =====
|
# =====
|
||||||
|
|
||||||
@@ -176,14 +164,8 @@ def testCoreStatus_Entries():
|
|||||||
for key in statusKeys:
|
for key in statusKeys:
|
||||||
assert theStatus.check(key) == key
|
assert theStatus.check(key) == key
|
||||||
|
|
||||||
# Reverse map lookup
|
|
||||||
assert theStatus.check("Entry 1") == statusKeys[0]
|
|
||||||
assert theStatus.check("Entry 2") == statusKeys[1]
|
|
||||||
assert theStatus.check("Entry 3") == statusKeys[2]
|
|
||||||
assert theStatus.check("Entry 4") == statusKeys[3]
|
|
||||||
|
|
||||||
# Non-existing name
|
# Non-existing name
|
||||||
assert theStatus.check("Entry 5") == statusKeys[0]
|
assert theStatus.check("s987654") == statusKeys[0]
|
||||||
|
|
||||||
# Name Access
|
# Name Access
|
||||||
# ===========
|
# ===========
|
||||||
@@ -314,10 +296,9 @@ def testCoreStatus_Entries():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreStatus_XMLPackUnpack():
|
def testCoreStatus_PackUnpack(mockRnd):
|
||||||
"""Test all the XML pack/unpack of the NWStatus class.
|
"""Test all the pack/unpack of the NWStatus class.
|
||||||
"""
|
"""
|
||||||
random.seed(42)
|
|
||||||
theStatus = NWStatus(NWStatus.STATUS)
|
theStatus = NWStatus(NWStatus.STATUS)
|
||||||
theStatus.write(None, "New", (100, 100, 100))
|
theStatus.write(None, "New", (100, 100, 100))
|
||||||
theStatus.write(None, "Note", (200, 50, 0))
|
theStatus.write(None, "Note", (200, 50, 0))
|
||||||
@@ -329,36 +310,59 @@ def testCoreStatus_XMLPackUnpack():
|
|||||||
for _ in range(n):
|
for _ in range(n):
|
||||||
theStatus.increment(statusKeys[i])
|
theStatus.increment(statusKeys[i])
|
||||||
|
|
||||||
nwXML = etree.Element("novelWriterXML")
|
|
||||||
|
|
||||||
# Pack
|
# Pack
|
||||||
xStatus = etree.SubElement(nwXML, "status")
|
assert list(theStatus.pack()) == [
|
||||||
theStatus.packXML(xStatus)
|
("New", {
|
||||||
assert etree.tostring(xStatus, pretty_print=False, encoding="utf-8") == (
|
"key": statusKeys[0],
|
||||||
b'<status>'
|
"count": "3",
|
||||||
b'<entry key="sa3b179" count="3" red="100" green="100" blue="100">New</entry>'
|
"red": "100",
|
||||||
b'<entry key="s1c8031" count="5" red="200" green="50" blue="0">Note</entry>'
|
"green": "100",
|
||||||
b'<entry key="s06671a" count="7" red="200" green="150" blue="0">Draft</entry>'
|
"blue": "100"
|
||||||
b'<entry key="sbdd640" count="9" red="50" green="200" blue="0">Finished</entry>'
|
}),
|
||||||
b'</status>'
|
("Note", {
|
||||||
)
|
"key": statusKeys[1],
|
||||||
|
"count": "5",
|
||||||
|
"red": "200",
|
||||||
|
"green": "50",
|
||||||
|
"blue": "0"
|
||||||
|
}),
|
||||||
|
("Draft", {
|
||||||
|
"key": statusKeys[2],
|
||||||
|
"count": "7",
|
||||||
|
"red": "200",
|
||||||
|
"green": "150",
|
||||||
|
"blue": "0"
|
||||||
|
}),
|
||||||
|
("Finished", {
|
||||||
|
"key": statusKeys[3],
|
||||||
|
"count": "9",
|
||||||
|
"red": "50",
|
||||||
|
"green": "200",
|
||||||
|
"blue": "0"
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
# Unpack
|
# Unpack
|
||||||
theStatus = NWStatus(NWStatus.STATUS)
|
theStatus = NWStatus(NWStatus.STATUS)
|
||||||
assert theStatus.unpackXML(xStatus)
|
assert theStatus.unpack({
|
||||||
|
statusKeys[0]: {"label": "New0", "colour": (100, 100, 100), "count": countTo[0]},
|
||||||
|
statusKeys[1]: {"label": "New1", "colour": (150, 150, 150), "count": countTo[1]},
|
||||||
|
statusKeys[2]: {"label": "New2", "colour": (200, 200, 200), "count": countTo[2]},
|
||||||
|
statusKeys[3]: {"label": "New3", "colour": (250, 250, 250), "count": countTo[3]},
|
||||||
|
})
|
||||||
assert len(theStatus._store) == 4
|
assert len(theStatus._store) == 4
|
||||||
assert list(theStatus._store.keys()) == statusKeys
|
assert list(theStatus._store.keys()) == statusKeys
|
||||||
assert theStatus._store[statusKeys[0]]["name"] == "New"
|
assert theStatus._store[statusKeys[0]]["name"] == "New0"
|
||||||
assert theStatus._store[statusKeys[1]]["name"] == "Note"
|
assert theStatus._store[statusKeys[1]]["name"] == "New1"
|
||||||
assert theStatus._store[statusKeys[2]]["name"] == "Draft"
|
assert theStatus._store[statusKeys[2]]["name"] == "New2"
|
||||||
assert theStatus._store[statusKeys[3]]["name"] == "Finished"
|
assert theStatus._store[statusKeys[3]]["name"] == "New3"
|
||||||
assert theStatus._store[statusKeys[0]]["cols"] == (100, 100, 100)
|
assert theStatus._store[statusKeys[0]]["cols"] == (100, 100, 100)
|
||||||
assert theStatus._store[statusKeys[1]]["cols"] == (200, 50, 0)
|
assert theStatus._store[statusKeys[1]]["cols"] == (150, 150, 150)
|
||||||
assert theStatus._store[statusKeys[2]]["cols"] == (200, 150, 0)
|
assert theStatus._store[statusKeys[2]]["cols"] == (200, 200, 200)
|
||||||
assert theStatus._store[statusKeys[3]]["cols"] == (50, 200, 0)
|
assert theStatus._store[statusKeys[3]]["cols"] == (250, 250, 250)
|
||||||
assert theStatus._store[statusKeys[0]]["count"] == countTo[0]
|
assert theStatus._store[statusKeys[0]]["count"] == countTo[0]
|
||||||
assert theStatus._store[statusKeys[1]]["count"] == countTo[1]
|
assert theStatus._store[statusKeys[1]]["count"] == countTo[1]
|
||||||
assert theStatus._store[statusKeys[2]]["count"] == countTo[2]
|
assert theStatus._store[statusKeys[2]]["count"] == countTo[2]
|
||||||
assert theStatus._store[statusKeys[3]]["count"] == countTo[3]
|
assert theStatus._store[statusKeys[3]]["count"] == countTo[3]
|
||||||
|
|
||||||
# END Test testCoreStatus_XMLPackUnpack
|
# END Test testCoreStatus_PackUnpack
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import os
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tools import readFile
|
from tools import C, buildTestProject, readFile
|
||||||
|
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.document import NWDoc
|
from novelwriter.core.document import NWDoc
|
||||||
@@ -133,21 +133,20 @@ def testCoreToken_Setters(mockGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
|
def testCoreToken_TextOps(monkeypatch, mockGUI, mockRnd, fncDir):
|
||||||
"""Test handling files and text in the Tokenizer class.
|
"""Test handling files and text in the Tokenizer class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projLang = "en"
|
mockRnd.reset()
|
||||||
|
buildTestProject(theProject, fncDir)
|
||||||
|
|
||||||
|
theProject.data.setLanguage("en")
|
||||||
theProject._loadProjectLocalisation()
|
theProject._loadProjectLocalisation()
|
||||||
|
|
||||||
theToken = BareTokenizer(theProject)
|
theToken = BareTokenizer(theProject)
|
||||||
theToken.setKeepMarkdown(True)
|
theToken.setKeepMarkdown(True)
|
||||||
|
|
||||||
assert theProject.openProject(nwMinimal)
|
|
||||||
sHandle = "8c659a11cd429"
|
|
||||||
|
|
||||||
# Set some content to work with
|
# Set some content to work with
|
||||||
|
|
||||||
docText = (
|
docText = (
|
||||||
"### Scene Six\n\n"
|
"### Scene Six\n\n"
|
||||||
"This is text with _italic text_, some **bold text**, some ~~deleted text~~, "
|
"This is text with _italic text_, some **bold text**, some ~~deleted text~~, "
|
||||||
@@ -157,26 +156,26 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
|
|||||||
)
|
)
|
||||||
docTextR = docText.replace("<A>", "this").replace("<B>", "that")
|
docTextR = docText.replace("<A>", "this").replace("<B>", "that")
|
||||||
|
|
||||||
nDoc = NWDoc(theProject, sHandle)
|
nDoc = NWDoc(theProject, C.hSceneDoc)
|
||||||
assert nDoc.writeDocument(docText)
|
assert nDoc.writeDocument(docText)
|
||||||
|
|
||||||
theProject.setAutoReplace({"A": "this", "B": "that"})
|
theProject.data.setAutoReplace({"A": "this", "B": "that"})
|
||||||
|
|
||||||
assert theProject.saveProject()
|
assert theProject.saveProject()
|
||||||
|
|
||||||
# Root Heading
|
# Root Heading
|
||||||
assert theToken.addRootHeading("stuff") is False
|
assert theToken.addRootHeading("stuff") is False
|
||||||
assert theToken.addRootHeading(sHandle) is False
|
assert theToken.addRootHeading(C.hSceneDoc) is False
|
||||||
|
|
||||||
# First Page
|
# First Page
|
||||||
assert theToken.addRootHeading("7695ce551d265") is True
|
assert theToken.addRootHeading(C.hPlotRoot) is True
|
||||||
assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n"
|
assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n"
|
||||||
assert theToken._theTokens[-1] == (
|
assert theToken._theTokens[-1] == (
|
||||||
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE
|
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE
|
||||||
)
|
)
|
||||||
|
|
||||||
# Not First Page
|
# Not First Page
|
||||||
assert theToken.addRootHeading("7695ce551d265") is True
|
assert theToken.addRootHeading(C.hPlotRoot) is True
|
||||||
assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n"
|
assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n"
|
||||||
assert theToken._theTokens[-1] == (
|
assert theToken._theTokens[-1] == (
|
||||||
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB
|
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB
|
||||||
@@ -184,18 +183,18 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
|
|||||||
|
|
||||||
# Set Text
|
# Set Text
|
||||||
assert theToken.setText("stuff") is False
|
assert theToken.setText("stuff") is False
|
||||||
assert theToken.setText(sHandle) is True
|
assert theToken.setText(C.hSceneDoc) is True
|
||||||
assert theToken._theText == docText
|
assert theToken._theText == docText
|
||||||
|
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("novelwriter.constants.nwConst.MAX_DOCSIZE", 100)
|
mp.setattr("novelwriter.constants.nwConst.MAX_DOCSIZE", 100)
|
||||||
assert theToken.setText(sHandle, docText) is True
|
assert theToken.setText(C.hSceneDoc, docText) is True
|
||||||
assert theToken._theText == (
|
assert theToken._theText == (
|
||||||
"# ERROR\n\n"
|
"# ERROR\n\n"
|
||||||
"Document 'New Scene' is too big (0.00 MB). Skipping.\n\n"
|
"Document 'New Scene' is too big (0.00 MB). Skipping.\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert theToken.setText(sHandle, docText) is True
|
assert theToken.setText(C.hSceneDoc, docText) is True
|
||||||
assert theToken._theText == docText
|
assert theToken._theText == docText
|
||||||
|
|
||||||
assert theToken._isNone is False
|
assert theToken._isNone is False
|
||||||
@@ -212,7 +211,7 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
|
|||||||
assert theToken.theResult == "This is text with escapes: ** ~~ __"
|
assert theToken.theResult == "This is text with escapes: ** ~~ __"
|
||||||
|
|
||||||
# Save File
|
# Save File
|
||||||
savePath = os.path.join(nwMinimal, "dump.nwd")
|
savePath = os.path.join(fncDir, "dump.nwd")
|
||||||
theToken.saveRawMarkdown(savePath)
|
theToken.saveRawMarkdown(savePath)
|
||||||
assert readFile(savePath) == (
|
assert readFile(savePath) == (
|
||||||
"# Notes: Plot\n\n"
|
"# Notes: Plot\n\n"
|
||||||
@@ -884,7 +883,7 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
|||||||
"""Test the header and page parser of the Tokenizer class.
|
"""Test the header and page parser of the Tokenizer class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(mockGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projLang = "en"
|
theProject.data.setLanguage("en")
|
||||||
theProject._loadProjectLocalisation()
|
theProject._loadProjectLocalisation()
|
||||||
theToken = BareTokenizer(theProject)
|
theToken = BareTokenizer(theProject)
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
|
|
||||||
from tools import readFile
|
from tools import readFile
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
@@ -198,7 +196,7 @@ def testCoreTree_BuildTree(mockGUI, mockItems):
|
|||||||
assert len(theTree) == len(mockItems) + 1
|
assert len(theTree) == len(mockItems) + 1
|
||||||
|
|
||||||
theList = theTree.handles()
|
theList = theTree.handles()
|
||||||
nHandle = "0000000000010"
|
nHandle = "0000000000000"
|
||||||
assert theList[-1] == nHandle
|
assert theList[-1] == nHandle
|
||||||
|
|
||||||
# Try to add existing handle
|
# Try to add existing handle
|
||||||
@@ -395,64 +393,6 @@ def testCoreTree_Reorder(mockGUI, mockItems):
|
|||||||
# END Test testCoreTree_Reorder
|
# END Test testCoreTree_Reorder
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testCoreTree_XMLPackUnpack(mockGUI, mockItems):
|
|
||||||
"""Test packing and unpacking the tree to and from XML.
|
|
||||||
"""
|
|
||||||
theProject = NWProject(mockGUI)
|
|
||||||
theTree = NWTree(theProject)
|
|
||||||
|
|
||||||
for tHandle, pHandle, nwItem in mockItems:
|
|
||||||
theTree.append(tHandle, pHandle, nwItem)
|
|
||||||
theTree.updateItemData(tHandle)
|
|
||||||
|
|
||||||
assert len(theTree) == len(mockItems)
|
|
||||||
|
|
||||||
nwXML = etree.Element("novelWriterXML")
|
|
||||||
theTree.packXML(nwXML)
|
|
||||||
assert etree.tostring(nwXML, pretty_print=False, encoding="utf-8") == (
|
|
||||||
b'<novelWriterXML>'
|
|
||||||
b'<content count="8">'
|
|
||||||
b'<item handle="a000000000001" parent="None" root="a000000000001" order="0" type="ROOT" '
|
|
||||||
b'class="NOVEL"><meta expanded="True"/><name status="s000000" '
|
|
||||||
b'import="i000004">Novel</name></item>'
|
|
||||||
b'<item handle="b000000000001" parent="a000000000001" root="a000000000001" order="0" '
|
|
||||||
b'type="FOLDER" class="NOVEL"><meta expanded="True"/><name status="s000000" '
|
|
||||||
b'import="i000004">Act One</name></item>'
|
|
||||||
b'<item handle="c000000000001" parent="b000000000001" root="a000000000001" order="0" '
|
|
||||||
b'type="FILE" class="NOVEL" layout="DOCUMENT"><meta expanded="False" mainHeading="H0" '
|
|
||||||
b'charCount="300" wordCount="50" paraCount="2" cursorPos="0"/><name status="s000000" '
|
|
||||||
b'import="i000004" active="True">Chapter One</name></item>'
|
|
||||||
b'<item handle="c000000000002" parent="b000000000001" root="a000000000001" order="0" '
|
|
||||||
b'type="FILE" class="NOVEL" layout="DOCUMENT"><meta expanded="False" mainHeading="H0" '
|
|
||||||
b'charCount="3000" wordCount="500" paraCount="20" cursorPos="0"/><name status="s000000" '
|
|
||||||
b'import="i000004" active="True">Scene One</name></item>'
|
|
||||||
b'<item handle="a000000000002" parent="None" root="a000000000002" order="0" type="ROOT" '
|
|
||||||
b'class="ARCHIVE"><meta expanded="False"/><name status="s000000" '
|
|
||||||
b'import="i000004">Outtakes</name></item>'
|
|
||||||
b'<item handle="a000000000003" parent="None" root="a000000000003" order="0" type="ROOT" '
|
|
||||||
b'class="TRASH"><meta expanded="False"/><name status="s000000" '
|
|
||||||
b'import="i000004">Trash</name></item>'
|
|
||||||
b'<item handle="a000000000004" parent="None" root="a000000000004" order="0" type="ROOT" '
|
|
||||||
b'class="CHARACTER"><meta expanded="True"/><name status="s000000" '
|
|
||||||
b'import="i000004">Characters</name></item>'
|
|
||||||
b'<item handle="b000000000002" parent="a000000000004" root="a000000000004" order="0" '
|
|
||||||
b'type="FILE" class="CHARACTER" layout="NOTE"><meta expanded="False" mainHeading="H0" '
|
|
||||||
b'charCount="2000" wordCount="400" paraCount="16" cursorPos="0"/><name status="s000000" '
|
|
||||||
b'import="i000004" active="True">Jane Doe</name></item>'
|
|
||||||
b'</content>'
|
|
||||||
b'</novelWriterXML>'
|
|
||||||
)
|
|
||||||
|
|
||||||
theTree.clear()
|
|
||||||
assert len(theTree) == 0
|
|
||||||
assert not theTree.unpackXML(nwXML)
|
|
||||||
assert theTree.unpackXML(nwXML[0])
|
|
||||||
assert len(theTree) == len(mockItems)
|
|
||||||
|
|
||||||
# END Test testCoreTree_XMLPackUnpack
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpDir):
|
def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpDir):
|
||||||
"""Test writing the ToC.txt file.
|
"""Test writing the ToC.txt file.
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, nwLipsum):
|
|||||||
assert projDet.tabMain.wordCountVal.text() == f"{3000:n}"
|
assert projDet.tabMain.wordCountVal.text() == f"{3000:n}"
|
||||||
assert projDet.tabMain.chapCountVal.text() == f"{3:n}"
|
assert projDet.tabMain.chapCountVal.text() == f"{3:n}"
|
||||||
assert projDet.tabMain.sceneCountVal.text() == f"{5:n}"
|
assert projDet.tabMain.sceneCountVal.text() == f"{5:n}"
|
||||||
assert projDet.tabMain.revCountVal.text() == f"{nwGUI.theProject.saveCount:n}"
|
assert projDet.tabMain.revCountVal.text() == f"{nwGUI.theProject.data.saveCount:n}"
|
||||||
|
|
||||||
assert projDet.tabMain.projPathVal.text() == nwLipsum
|
assert projDet.tabMain.projPathVal.text() == nwLipsum
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import pytest
|
import pytest
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from tools import getGuiItem
|
from tools import buildTestProject, getGuiItem
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
@@ -33,10 +33,10 @@ from novelwriter.dialogs.projload import GuiProjectLoad
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, fncProj):
|
||||||
"""Test the load project wizard.
|
"""Test the load project wizard.
|
||||||
"""
|
"""
|
||||||
assert nwGUI.openProject(nwMinimal)
|
buildTestProject(nwGUI, fncProj)
|
||||||
assert nwGUI.closeProject()
|
assert nwGUI.closeProject()
|
||||||
|
|
||||||
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
|
||||||
@@ -87,10 +87,10 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
nwLoad._doDeleteRecent()
|
nwLoad._doDeleteRecent()
|
||||||
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
|
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
|
||||||
|
|
||||||
getFile = os.path.join(nwMinimal, "nwProject.nwx")
|
getFile = os.path.join(fncProj, "nwProject.nwx")
|
||||||
monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: (getFile, None))
|
monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: (getFile, None))
|
||||||
qtbot.mouseClick(nwLoad.browseButton, Qt.LeftButton)
|
qtbot.mouseClick(nwLoad.browseButton, Qt.LeftButton)
|
||||||
assert nwLoad.openPath == nwMinimal
|
assert nwLoad.openPath == fncProj
|
||||||
assert nwLoad.openState == nwLoad.OPEN_STATE
|
assert nwLoad.openState == nwLoad.OPEN_STATE
|
||||||
|
|
||||||
nwLoad.close()
|
nwLoad.close()
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
|
|||||||
|
|
||||||
# Pretend we have a project
|
# Pretend we have a project
|
||||||
nwGUI.hasProject = True
|
nwGUI.hasProject = True
|
||||||
nwGUI.theProject.setSpellLang("en")
|
nwGUI.theProject.data.setSpellLang("en")
|
||||||
|
|
||||||
# Get the dialog object
|
# Get the dialog object
|
||||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
||||||
@@ -95,9 +95,9 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd
|
|||||||
|
|
||||||
# Set some values
|
# Set some values
|
||||||
theProject = nwGUI.theProject
|
theProject = nwGUI.theProject
|
||||||
theProject.setSpellLang("en")
|
theProject.data.setSpellLang("en")
|
||||||
theProject.setBookAuthors("Jane Smith\nJohn Smith")
|
theProject.data.setAuthors("Jane Smith\nJohn Smith")
|
||||||
theProject.setAutoReplace({"A": "B", "C": "D"})
|
theProject.data.setAutoReplace({"A": "B", "C": "D"})
|
||||||
|
|
||||||
# Create Dialog
|
# Create Dialog
|
||||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN)
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN)
|
||||||
@@ -136,9 +136,9 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd
|
|||||||
assert projSettings.spellChanged is False
|
assert projSettings.spellChanged is False
|
||||||
|
|
||||||
projSettings._doSave()
|
projSettings._doSave()
|
||||||
assert theProject.projName == "Project Name"
|
assert theProject.data.name == "Project Name"
|
||||||
assert theProject.bookTitle == "Project Title"
|
assert theProject.data.title == "Project Title"
|
||||||
assert theProject.bookAuthors == ["Jane Doe", "John Doh"]
|
assert theProject.data.authors == ["Jane Doe", "John Doh"]
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
projSettings._doClose()
|
projSettings._doClose()
|
||||||
@@ -330,13 +330,13 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
|
|||||||
# Check Project
|
# Check Project
|
||||||
projSettings._doSave()
|
projSettings._doSave()
|
||||||
|
|
||||||
statusItems = dict(theProject.statusItems.items())
|
statusItems = dict(theProject.data.itemStatus.items())
|
||||||
assert statusItems[C.sNew]["name"] == "New"
|
assert statusItems[C.sNew]["name"] == "New"
|
||||||
assert statusItems[C.sDraft]["name"] == "Draft"
|
assert statusItems[C.sDraft]["name"] == "Draft"
|
||||||
assert statusItems[C.sFinished]["name"] == "Finished"
|
assert statusItems[C.sFinished]["name"] == "Finished"
|
||||||
assert statusItems["s000013"]["name"] == "Final"
|
assert statusItems["s000013"]["name"] == "Final"
|
||||||
|
|
||||||
importItems = dict(theProject.importItems.items())
|
importItems = dict(theProject.data.itemImport.items())
|
||||||
assert importItems[C.iNew]["name"] == "New"
|
assert importItems[C.iNew]["name"] == "New"
|
||||||
assert importItems[C.iMajor]["name"] == "Major"
|
assert importItems[C.iMajor]["name"] == "Major"
|
||||||
assert importItems[C.iMain]["name"] == "Main"
|
assert importItems[C.iMain]["name"] == "Main"
|
||||||
@@ -365,9 +365,9 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mock
|
|||||||
|
|
||||||
# Set some values
|
# Set some values
|
||||||
theProject = nwGUI.theProject
|
theProject = nwGUI.theProject
|
||||||
theProject.autoReplace = {
|
theProject.data.setAutoReplace({
|
||||||
"A": "B", "C": "D"
|
"A": "B", "C": "D"
|
||||||
}
|
})
|
||||||
|
|
||||||
# Create Dialog
|
# Create Dialog
|
||||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_REPLACE)
|
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_REPLACE)
|
||||||
@@ -429,7 +429,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mock
|
|||||||
|
|
||||||
# Check Project
|
# Check Project
|
||||||
projSettings._doSave()
|
projSettings._doSave()
|
||||||
assert theProject.autoReplace == {
|
assert theProject.data.autoReplace == {
|
||||||
"A": "B", "C": "D", "This": "With This Stuff"
|
"A": "B", "C": "D", "This": "With This Stuff"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import pytest
|
|||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QDialog, QAction
|
from PyQt5.QtWidgets import QDialog, QAction
|
||||||
|
|
||||||
from tools import writeFile, readFile, getGuiItem
|
from tools import buildTestProject, writeFile, readFile, getGuiItem
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
|
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
@@ -33,16 +33,18 @@ from novelwriter.dialogs.wordlist import GuiWordList
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
|
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, fncProj):
|
||||||
"""test the word list editor.
|
"""test the word list editor.
|
||||||
"""
|
"""
|
||||||
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
|
||||||
monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None)
|
monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None)
|
||||||
monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted)
|
monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted)
|
||||||
monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
|
monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
|
||||||
|
|
||||||
# Open project
|
# Open project
|
||||||
nwGUI.openProject(nwMinimal)
|
nwGUI.openProject(fncProj)
|
||||||
dictFile = os.path.join(nwMinimal, "meta", nwFiles.PROJ_DICT)
|
dictFile = os.path.join(fncProj, "meta", nwFiles.PROJ_DICT)
|
||||||
|
|
||||||
# Load the dialog
|
# Load the dialog
|
||||||
nwGUI.mainMenu.aEditWordList.activate(QAction.Trigger)
|
nwGUI.mainMenu.aEditWordList.activate(QAction.Trigger)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
|
from tools import C, buildTestProject
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
|
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
|
||||||
@@ -36,12 +37,12 @@ KEY_DELAY = 1
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_Init(qtbot, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_Init(qtbot, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test initialising the editor.
|
"""Test initialising the editor.
|
||||||
"""
|
"""
|
||||||
# Open project
|
# Open project
|
||||||
assert nwGUI.openProject(nwMinimal)
|
buildTestProject(nwGUI, fncProj)
|
||||||
assert nwGUI.openDocument("8c659a11cd429")
|
assert nwGUI.openDocument(C.hSceneDoc)
|
||||||
|
|
||||||
nwGUI.docEditor.setText("### Lorem Ipsum\n\n%s" % ipsumText[0])
|
nwGUI.docEditor.setText("### Lorem Ipsum\n\n%s" % ipsumText[0])
|
||||||
assert nwGUI.saveDocument()
|
assert nwGUI.saveDocument()
|
||||||
@@ -79,13 +80,11 @@ def testGuiEditor_Init(qtbot, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test loading text into the editor.
|
"""Test loading text into the editor.
|
||||||
"""
|
"""
|
||||||
# Open project
|
buildTestProject(nwGUI, fncProj)
|
||||||
sHandle = "8c659a11cd429"
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
longText = "### Lorem Ipsum\n\n%s" % "\n\n".join(ipsumText*20)
|
longText = "### Lorem Ipsum\n\n%s" % "\n\n".join(ipsumText*20)
|
||||||
nwGUI.docEditor.replaceText(longText)
|
nwGUI.docEditor.replaceText(longText)
|
||||||
@@ -101,11 +100,11 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
# Document too big
|
# Document too big
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("novelwriter.constants.nwConst.MAX_DOCSIZE", 100)
|
mp.setattr("novelwriter.constants.nwConst.MAX_DOCSIZE", 100)
|
||||||
assert nwGUI.docEditor.loadText(sHandle) is False
|
assert nwGUI.docEditor.loadText(C.hSceneDoc) is False
|
||||||
assert "The document you are trying to open is too big." in caplog.text
|
assert "The document you are trying to open is too big." in caplog.text
|
||||||
|
|
||||||
# Regular open
|
# Regular open
|
||||||
assert nwGUI.docEditor.loadText(sHandle) is True
|
assert nwGUI.docEditor.loadText(C.hSceneDoc) is True
|
||||||
assert nwGUI.docEditor._bigDoc is False
|
assert nwGUI.docEditor._bigDoc is False
|
||||||
|
|
||||||
# Reload too big text
|
# Reload too big text
|
||||||
@@ -116,18 +115,18 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
|
|
||||||
# Big doc handling
|
# Big doc handling
|
||||||
nwGUI.mainConf.bigDocLimit = 50
|
nwGUI.mainConf.bigDocLimit = 50
|
||||||
assert nwGUI.docEditor.loadText(sHandle) is True
|
assert nwGUI.docEditor.loadText(C.hSceneDoc) is True
|
||||||
assert nwGUI.docEditor._bigDoc is True
|
assert nwGUI.docEditor._bigDoc is True
|
||||||
|
|
||||||
# Regular open, with line number
|
# Regular open, with line number
|
||||||
assert nwGUI.docEditor.loadText(sHandle, tLine=4) is True
|
assert nwGUI.docEditor.loadText(C.hSceneDoc, tLine=4) is True
|
||||||
cursPos = nwGUI.docEditor.getCursorPosition()
|
cursPos = nwGUI.docEditor.getCursorPosition()
|
||||||
assert nwGUI.docEditor.document().findBlock(cursPos).blockNumber() == 4
|
assert nwGUI.docEditor.document().findBlock(cursPos).blockNumber() == 4
|
||||||
|
|
||||||
# Load empty document
|
# Load empty document
|
||||||
nwGUI.docEditor.replaceText("")
|
nwGUI.docEditor.replaceText("")
|
||||||
assert nwGUI.saveDocument() is True
|
assert nwGUI.saveDocument() is True
|
||||||
assert nwGUI.docEditor.loadText(sHandle) is True
|
assert nwGUI.docEditor.loadText(C.hSceneDoc) is True
|
||||||
assert nwGUI.docEditor.toPlainText() == ""
|
assert nwGUI.docEditor.toPlainText() == ""
|
||||||
|
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
@@ -136,13 +135,11 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test saving text from the editor.
|
"""Test saving text from the editor.
|
||||||
"""
|
"""
|
||||||
# Open project
|
buildTestProject(nwGUI, fncProj)
|
||||||
sHandle = "8c659a11cd429"
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
# Save Text
|
# Save Text
|
||||||
# =========
|
# =========
|
||||||
@@ -159,7 +156,7 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
# Unkown handle
|
# Unkown handle
|
||||||
nwGUI.docEditor._docHandle = "0123456789abcdef"
|
nwGUI.docEditor._docHandle = "0123456789abcdef"
|
||||||
assert nwGUI.docEditor.saveText() is False
|
assert nwGUI.docEditor.saveText() is False
|
||||||
nwGUI.docEditor._docHandle = sHandle
|
nwGUI.docEditor._docHandle = C.hSceneDoc
|
||||||
|
|
||||||
# Cause error when saving
|
# Cause error when saving
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
@@ -168,10 +165,10 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
assert "Could not save document." in caplog.text
|
assert "Could not save document." in caplog.text
|
||||||
|
|
||||||
# Change header level
|
# Change header level
|
||||||
assert nwGUI.theProject.tree[sHandle].itemLayout == nwItemLayout.DOCUMENT
|
assert nwGUI.theProject.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT
|
||||||
nwGUI.docEditor.replaceText(longText[1:])
|
nwGUI.docEditor.replaceText(longText[1:])
|
||||||
assert nwGUI.docEditor.saveText() is True
|
assert nwGUI.docEditor.saveText() is True
|
||||||
assert nwGUI.theProject.tree[sHandle].itemLayout == nwItemLayout.DOCUMENT
|
assert nwGUI.theProject.tree[C.hSceneDoc].itemLayout == nwItemLayout.DOCUMENT
|
||||||
|
|
||||||
# Regular save
|
# Regular save
|
||||||
assert nwGUI.docEditor.saveText() is True
|
assert nwGUI.docEditor.saveText() is True
|
||||||
@@ -182,13 +179,11 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
def testGuiEditor_MetaData(qtbot, nwGUI, fncProj, mockRnd):
|
||||||
"""Test extracting various meta data and other values.
|
"""Test extracting various meta data and other values.
|
||||||
"""
|
"""
|
||||||
# Open project
|
buildTestProject(nwGUI, fncProj)
|
||||||
sHandle = "8c659a11cd429"
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
# Get Text
|
# Get Text
|
||||||
# This should replace line and paragraph separators, but preserve
|
# This should replace line and paragraph separators, but preserve
|
||||||
@@ -203,7 +198,7 @@ def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
|||||||
|
|
||||||
# Check Propertoes
|
# Check Propertoes
|
||||||
assert nwGUI.docEditor.docChanged() is True
|
assert nwGUI.docEditor.docChanged() is True
|
||||||
assert nwGUI.docEditor.docHandle() == sHandle
|
assert nwGUI.docEditor.docHandle() == C.hSceneDoc
|
||||||
assert nwGUI.docEditor.lastActive() > 0.0
|
assert nwGUI.docEditor.lastActive() > 0.0
|
||||||
assert nwGUI.docEditor.isEmpty() is False
|
assert nwGUI.docEditor.isEmpty() is False
|
||||||
|
|
||||||
@@ -211,9 +206,9 @@ def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
|||||||
assert nwGUI.docEditor.setCursorPosition(None) is False
|
assert nwGUI.docEditor.setCursorPosition(None) is False
|
||||||
assert nwGUI.docEditor.setCursorPosition(10) is True
|
assert nwGUI.docEditor.setCursorPosition(10) is True
|
||||||
assert nwGUI.docEditor.getCursorPosition() == 10
|
assert nwGUI.docEditor.getCursorPosition() == 10
|
||||||
assert nwGUI.theProject.tree[sHandle].cursorPos != 10
|
assert nwGUI.theProject.tree[C.hSceneDoc].cursorPos != 10
|
||||||
nwGUI.docEditor.saveCursorPosition()
|
nwGUI.docEditor.saveCursorPosition()
|
||||||
assert nwGUI.theProject.tree[sHandle].cursorPos == 10
|
assert nwGUI.theProject.tree[C.hSceneDoc].cursorPos == 10
|
||||||
|
|
||||||
assert nwGUI.docEditor.setCursorLine(None) is False
|
assert nwGUI.docEditor.setCursorLine(None) is False
|
||||||
assert nwGUI.docEditor.setCursorLine(2) is True
|
assert nwGUI.docEditor.setCursorLine(2) is True
|
||||||
@@ -231,16 +226,14 @@ def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_Actions(qtbot, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_Actions(qtbot, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test the document actions. This is not an extensive test of the
|
"""Test the document actions. This is not an extensive test of the
|
||||||
action features, just that the actions are actually called. The
|
action features, just that the actions are actually called. The
|
||||||
various action features are tested when their respective functions
|
various action features are tested when their respective functions
|
||||||
are tested.
|
are tested.
|
||||||
"""
|
"""
|
||||||
# Open project
|
buildTestProject(nwGUI, fncProj)
|
||||||
sHandle = "8c659a11cd429"
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
@@ -452,7 +445,7 @@ def testGuiEditor_Actions(qtbot, nwGUI, nwMinimal, ipsumText):
|
|||||||
# No Document Handle
|
# No Document Handle
|
||||||
nwGUI.docEditor._docHandle = None
|
nwGUI.docEditor._docHandle = None
|
||||||
assert nwGUI.docEditor.docAction(nwDocAction.BLOCK_TXT) is False
|
assert nwGUI.docEditor.docAction(nwDocAction.BLOCK_TXT) is False
|
||||||
nwGUI.docEditor._docHandle = sHandle
|
nwGUI.docEditor._docHandle = C.hSceneDoc
|
||||||
|
|
||||||
# Wrong Action Type
|
# Wrong Action Type
|
||||||
assert nwGUI.docEditor.docAction(None) is False
|
assert nwGUI.docEditor.docAction(None) is False
|
||||||
@@ -466,13 +459,11 @@ def testGuiEditor_Actions(qtbot, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test the document insert functions.
|
"""Test the document insert functions.
|
||||||
"""
|
"""
|
||||||
# Open project
|
buildTestProject(nwGUI, fncProj)
|
||||||
sHandle = "8c659a11cd429"
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
@@ -487,7 +478,7 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
nwGUI.docEditor._docHandle = None
|
nwGUI.docEditor._docHandle = None
|
||||||
assert nwGUI.docEditor.setCursorPosition(24) is True
|
assert nwGUI.docEditor.setCursorPosition(24) is True
|
||||||
assert nwGUI.docEditor.insertText("Stuff") is False
|
assert nwGUI.docEditor.insertText("Stuff") is False
|
||||||
nwGUI.docEditor._docHandle = sHandle
|
nwGUI.docEditor._docHandle = C.hSceneDoc
|
||||||
|
|
||||||
# Insert String
|
# Insert String
|
||||||
assert nwGUI.docEditor.setCursorPosition(24) is True
|
assert nwGUI.docEditor.setCursorPosition(24) is True
|
||||||
@@ -551,13 +542,11 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test the text manipulation functions.
|
"""Test the text manipulation functions.
|
||||||
"""
|
"""
|
||||||
# Open project
|
buildTestProject(nwGUI, fncProj)
|
||||||
sHandle = "8c659a11cd429"
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
@@ -760,13 +749,11 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTe
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test the block formatting function.
|
"""Test the block formatting function.
|
||||||
"""
|
"""
|
||||||
# Open project
|
buildTestProject(nwGUI, fncProj)
|
||||||
sHandle = "8c659a11cd429"
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
@@ -1075,13 +1062,11 @@ def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTex
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_Tags(qtbot, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test the document editor tags functionality.
|
"""Test the document editor tags functionality.
|
||||||
"""
|
"""
|
||||||
# Open project
|
buildTestProject(nwGUI, fncProj)
|
||||||
sHandle = "8c659a11cd429"
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
# Create Scene
|
# Create Scene
|
||||||
theText = "### A Scene\n\n@char: Jane, John\n\n" + ipsumText[0] + "\n\n"
|
theText = "### A Scene\n\n@char: Jane, John\n\n" + ipsumText[0] + "\n\n"
|
||||||
@@ -1089,7 +1074,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
# Create Character
|
# Create Character
|
||||||
theText = "### Jane Doe\n\n@tag: Jane\n\n" + ipsumText[1] + "\n\n"
|
theText = "### Jane Doe\n\n@tag: Jane\n\n" + ipsumText[1] + "\n\n"
|
||||||
cHandle = nwGUI.theProject.newFile("Jane Doe", "afb3043c7b2b3")
|
cHandle = nwGUI.theProject.newFile("Jane Doe", C.hCharRoot)
|
||||||
assert nwGUI.openDocument(cHandle) is True
|
assert nwGUI.openDocument(cHandle) is True
|
||||||
assert nwGUI.docEditor.replaceText(theText) is True
|
assert nwGUI.docEditor.replaceText(theText) is True
|
||||||
assert nwGUI.saveDocument() is True
|
assert nwGUI.saveDocument() is True
|
||||||
@@ -1098,7 +1083,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
# Follow Tag
|
# Follow Tag
|
||||||
# ==========
|
# ==========
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
|
|
||||||
# Empty Block
|
# Empty Block
|
||||||
assert nwGUI.docEditor.setCursorLine(1) is True
|
assert nwGUI.docEditor.setCursorLine(1) is True
|
||||||
@@ -1136,7 +1121,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, fncProj, ipsumText, mockRnd):
|
||||||
"""Test saving text from the editor.
|
"""Test saving text from the editor.
|
||||||
"""
|
"""
|
||||||
class MockThreadPool:
|
class MockThreadPool:
|
||||||
@@ -1153,7 +1138,8 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
nwGUI.threadPool = MockThreadPool()
|
nwGUI.threadPool = MockThreadPool()
|
||||||
nwGUI.docEditor.wcTimerDoc.blockSignals(True)
|
nwGUI.docEditor.wcTimerDoc.blockSignals(True)
|
||||||
nwGUI.docEditor.wcTimerSel.blockSignals(True)
|
nwGUI.docEditor.wcTimerSel.blockSignals(True)
|
||||||
assert nwGUI.openProject(nwMinimal) is True
|
|
||||||
|
buildTestProject(nwGUI, fncProj)
|
||||||
|
|
||||||
# Run on an empty document
|
# Run on an empty document
|
||||||
nwGUI.docEditor._runDocCounter()
|
nwGUI.docEditor._runDocCounter()
|
||||||
@@ -1167,10 +1153,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
assert nwGUI.docEditor.docFooter.wordsText.text() == "Words: 0 (+0)"
|
assert nwGUI.docEditor.docFooter.wordsText.text() == "Words: 0 (+0)"
|
||||||
|
|
||||||
# Open a document and populate it
|
# Open a document and populate it
|
||||||
sHandle = "8c659a11cd429"
|
nwGUI.theProject.tree[C.hSceneDoc]._initCount = 0 # Clear item's count
|
||||||
nwGUI.theProject.tree[sHandle]._initCount = 0 # Clear item's count
|
nwGUI.theProject.tree[C.hSceneDoc]._wordCount = 0 # Clear item's count
|
||||||
nwGUI.theProject.tree[sHandle]._wordCount = 0 # Clear item's count
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
assert nwGUI.openDocument(sHandle) is True
|
|
||||||
|
|
||||||
theText = "\n\n".join(ipsumText)
|
theText = "\n\n".join(ipsumText)
|
||||||
cC, wC, pC = countWords(theText)
|
cC, wC, pC = countWords(theText)
|
||||||
@@ -1193,9 +1178,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
|||||||
|
|
||||||
nwGUI.docEditor.wCounterDoc.run()
|
nwGUI.docEditor.wCounterDoc.run()
|
||||||
# nwGUI.docEditor._updateDocCounts(cC, wC, pC)
|
# nwGUI.docEditor._updateDocCounts(cC, wC, pC)
|
||||||
assert nwGUI.theProject.tree[sHandle]._charCount == cC
|
assert nwGUI.theProject.tree[C.hSceneDoc]._charCount == cC
|
||||||
assert nwGUI.theProject.tree[sHandle]._wordCount == wC
|
assert nwGUI.theProject.tree[C.hSceneDoc]._wordCount == wC
|
||||||
assert nwGUI.theProject.tree[sHandle]._paraCount == pC
|
assert nwGUI.theProject.tree[C.hSceneDoc]._paraCount == pC
|
||||||
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})"
|
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})"
|
||||||
|
|
||||||
# Select all text
|
# Select all text
|
||||||
|
|||||||
@@ -172,10 +172,10 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
assert nwGUI.theProject.tree.trashRoot() is None
|
assert nwGUI.theProject.tree.trashRoot() is None
|
||||||
assert nwGUI.theProject.projPath is None
|
assert nwGUI.theProject.projPath is None
|
||||||
assert nwGUI.theProject.projMeta is None
|
assert nwGUI.theProject.projMeta is None
|
||||||
assert nwGUI.theProject.projName == ""
|
assert nwGUI.theProject.data.name == ""
|
||||||
assert nwGUI.theProject.bookTitle == ""
|
assert nwGUI.theProject.data.title == ""
|
||||||
assert len(nwGUI.theProject.bookAuthors) == 0
|
assert nwGUI.theProject.data.authors == []
|
||||||
assert not nwGUI.theProject.spellCheck
|
assert nwGUI.theProject.data.spellCheck is False
|
||||||
|
|
||||||
# Check the files
|
# Check the files
|
||||||
projFile = os.path.join(fncProj, "nwProject.nwx")
|
projFile = os.path.join(fncProj, "nwProject.nwx")
|
||||||
@@ -194,10 +194,10 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
assert nwGUI.theProject.tree.trashRoot() is None
|
assert nwGUI.theProject.tree.trashRoot() is None
|
||||||
assert nwGUI.theProject.projPath == fncProj
|
assert nwGUI.theProject.projPath == fncProj
|
||||||
assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta")
|
assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta")
|
||||||
assert nwGUI.theProject.projName == "New Project"
|
assert nwGUI.theProject.data.name == "New Project"
|
||||||
assert nwGUI.theProject.bookTitle == "New Novel"
|
assert nwGUI.theProject.data.title == "New Novel"
|
||||||
assert len(nwGUI.theProject.bookAuthors) == 1
|
assert nwGUI.theProject.data.authors == ["Jane Doe"]
|
||||||
assert nwGUI.theProject.spellCheck is False
|
assert nwGUI.theProject.data.spellCheck is False
|
||||||
|
|
||||||
# Check that tree items have been created
|
# Check that tree items have been created
|
||||||
assert nwGUI.projView.projTree._getTreeItem(C.hNovelRoot) is not None
|
assert nwGUI.projView.projTree._getTreeItem(C.hNovelRoot) is not None
|
||||||
@@ -489,11 +489,12 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
# Check a Quick Create and Delete
|
# Check a Quick Create and Delete
|
||||||
assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
|
assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
|
||||||
newHandle = nwGUI.projView.getSelectedHandle()
|
newHandle = nwGUI.projView.getSelectedHandle()
|
||||||
assert nwGUI.theProject.tree["0000000000020"] is not None
|
assert newHandle == "0000000000013"
|
||||||
|
assert nwGUI.theProject.tree[newHandle] is not None
|
||||||
assert nwGUI.projView.requestDeleteItem()
|
assert nwGUI.projView.requestDeleteItem()
|
||||||
assert nwGUI.projView.setSelectedHandle(newHandle)
|
assert nwGUI.projView.setSelectedHandle(newHandle)
|
||||||
assert nwGUI.projView.requestDeleteItem()
|
assert nwGUI.projView.requestDeleteItem()
|
||||||
assert nwGUI.theProject.tree["0000000000024"] is not None # Trash
|
assert nwGUI.theProject.tree["0000000000014"] is not None # Trash
|
||||||
assert nwGUI.saveProject()
|
assert nwGUI.saveProject()
|
||||||
|
|
||||||
# Check the files
|
# Check the files
|
||||||
@@ -509,21 +510,21 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
|||||||
copyfile(projFile, testFile)
|
copyfile(projFile, testFile)
|
||||||
assert cmpFiles(testFile, compFile)
|
assert cmpFiles(testFile, compFile)
|
||||||
|
|
||||||
projFile = os.path.join(fncProj, "content", "0000000000020.nwd")
|
projFile = os.path.join(fncProj, "content", "0000000000010.nwd")
|
||||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000020.nwd")
|
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000010.nwd")
|
||||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000020.nwd")
|
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000010.nwd")
|
||||||
copyfile(projFile, testFile)
|
copyfile(projFile, testFile)
|
||||||
assert cmpFiles(testFile, compFile)
|
assert cmpFiles(testFile, compFile)
|
||||||
|
|
||||||
projFile = os.path.join(fncProj, "content", "0000000000021.nwd")
|
projFile = os.path.join(fncProj, "content", "0000000000011.nwd")
|
||||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000021.nwd")
|
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000011.nwd")
|
||||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000021.nwd")
|
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000011.nwd")
|
||||||
copyfile(projFile, testFile)
|
copyfile(projFile, testFile)
|
||||||
assert cmpFiles(testFile, compFile)
|
assert cmpFiles(testFile, compFile)
|
||||||
|
|
||||||
projFile = os.path.join(fncProj, "content", "0000000000022.nwd")
|
projFile = os.path.join(fncProj, "content", "0000000000012.nwd")
|
||||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000022.nwd")
|
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000012.nwd")
|
||||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000022.nwd")
|
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000012.nwd")
|
||||||
copyfile(projFile, testFile)
|
copyfile(projFile, testFile)
|
||||||
assert cmpFiles(testFile, compFile)
|
assert cmpFiles(testFile, compFile)
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tools import getGuiItem
|
from tools import buildTestProject, getGuiItem
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QFileDialog, QWizard, QDialog
|
from PyQt5.QtWidgets import QFileDialog, QWizard, QDialog
|
||||||
@@ -37,7 +37,7 @@ from novelwriter.tools.projwizard import (
|
|||||||
|
|
||||||
@pytest.mark.gui
|
@pytest.mark.gui
|
||||||
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin")
|
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin")
|
||||||
def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, fncProj):
|
||||||
"""Test the launch of the project wizard.
|
"""Test the launch of the project wizard.
|
||||||
Disabled for macOS because the test segfaults on QWizard.show()
|
Disabled for macOS because the test segfaults on QWizard.show()
|
||||||
"""
|
"""
|
||||||
@@ -45,7 +45,7 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
# ========================
|
# ========================
|
||||||
|
|
||||||
# New with a project open should cause an error
|
# New with a project open should cause an error
|
||||||
assert nwGUI.openProject(nwMinimal)
|
buildTestProject(nwGUI, fncProj)
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(nwGUI, "closeProject", lambda *a: False)
|
mp.setattr(nwGUI, "closeProject", lambda *a: False)
|
||||||
assert nwGUI.newProject() is False
|
assert nwGUI.newProject() is False
|
||||||
@@ -61,7 +61,7 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
assert nwGUI.newProject() is False
|
assert nwGUI.newProject() is False
|
||||||
|
|
||||||
# Now, with a non-empty folder
|
# Now, with a non-empty folder
|
||||||
mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": nwMinimal})
|
mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": fncProj})
|
||||||
assert nwGUI.newProject() is False
|
assert nwGUI.newProject() is False
|
||||||
|
|
||||||
# Test the Wizard Launching
|
# Test the Wizard Launching
|
||||||
|
|||||||
+14
-4
@@ -167,9 +167,19 @@ def buildTestProject(theObject, projPath):
|
|||||||
|
|
||||||
theProject.clearProject()
|
theProject.clearProject()
|
||||||
theProject.setProjectPath(projPath, newProject=True)
|
theProject.setProjectPath(projPath, newProject=True)
|
||||||
theProject.setProjectName("New Project")
|
|
||||||
theProject.setBookTitle("New Novel")
|
theProject.data.itemStatus.write(None, "New", (100, 100, 100))
|
||||||
theProject.setBookAuthors("Jane Doe")
|
theProject.data.itemStatus.write(None, "Note", (200, 50, 0))
|
||||||
|
theProject.data.itemStatus.write(None, "Draft", (200, 150, 0))
|
||||||
|
theProject.data.itemStatus.write(None, "Finished", (50, 200, 0))
|
||||||
|
theProject.data.itemImport.write(None, "New", (100, 100, 100))
|
||||||
|
theProject.data.itemImport.write(None, "Minor", (200, 50, 0))
|
||||||
|
theProject.data.itemImport.write(None, "Major", (200, 150, 0))
|
||||||
|
theProject.data.itemImport.write(None, "Main", (50, 200, 0))
|
||||||
|
|
||||||
|
theProject.data.setName("New Project")
|
||||||
|
theProject.data.setTitle("New Novel")
|
||||||
|
theProject.data.setAuthors("Jane Doe")
|
||||||
|
|
||||||
# Creating a minimal project with a few root folders and a
|
# Creating a minimal project with a few root folders and a
|
||||||
# single chapter folder with a single file.
|
# single chapter folder with a single file.
|
||||||
@@ -195,7 +205,7 @@ def buildTestProject(theObject, projPath):
|
|||||||
aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene"))
|
aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene"))
|
||||||
theProject.index.reIndexHandle(xHandle[8])
|
theProject.index.reIndexHandle(xHandle[8])
|
||||||
|
|
||||||
theProject.projOpened = time.time()
|
theProject._projOpened = time.time()
|
||||||
theProject.setProjectChanged(True)
|
theProject.setProjectChanged(True)
|
||||||
theProject.saveProject(autoSave=True)
|
theProject.saveProject(autoSave=True)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user