Refactor project XML handling (#1221)
This commit is contained in:
@@ -75,7 +75,7 @@ class NWIndex:
|
||||
return
|
||||
|
||||
def __repr__(self):
|
||||
return f"<NWIndex project='{self.theProject.projName}'>"
|
||||
return f"<NWIndex project='{self.theProject.data.name}'>"
|
||||
|
||||
##
|
||||
# Properties
|
||||
|
||||
+82
-122
@@ -25,8 +25,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
||||
from novelwriter.common import (
|
||||
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified
|
||||
@@ -148,107 +146,70 @@ class NWItem:
|
||||
return self._cursorPos
|
||||
|
||||
##
|
||||
# XML Pack/Unpack
|
||||
# Pack/Unpack Data
|
||||
##
|
||||
|
||||
def packXML(self, xParent):
|
||||
"""Pack all the data in the class instance into an XML object.
|
||||
def pack(self):
|
||||
"""Pack all the data in the class instance into a dictionary.
|
||||
"""
|
||||
itemAttrib = {}
|
||||
itemAttrib["handle"] = str(self._handle)
|
||||
itemAttrib["parent"] = str(self._parent)
|
||||
itemAttrib["root"] = str(self._root)
|
||||
itemAttrib["order"] = str(self._order)
|
||||
itemAttrib["type"] = str(self._type.name)
|
||||
itemAttrib["class"] = str(self._class.name)
|
||||
item = {}
|
||||
meta = {}
|
||||
name = {}
|
||||
|
||||
item["handle"] = str(self._handle)
|
||||
item["parent"] = str(self._parent)
|
||||
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:
|
||||
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 = {}
|
||||
metaAttrib["expanded"] = str(self._expanded)
|
||||
if self._type == nwItemType.FILE:
|
||||
metaAttrib["mainHeading"] = str(self._heading)
|
||||
metaAttrib["charCount"] = str(self._charCount)
|
||||
metaAttrib["wordCount"] = str(self._wordCount)
|
||||
metaAttrib["paraCount"] = str(self._paraCount)
|
||||
metaAttrib["cursorPos"] = str(self._cursorPos)
|
||||
data = {
|
||||
"name": str(self._name),
|
||||
"itemAttr": item,
|
||||
"metaAttr": meta,
|
||||
"nameAttr": name,
|
||||
}
|
||||
|
||||
nameAttrib = {}
|
||||
nameAttrib["status"] = str(self._status)
|
||||
nameAttrib["import"] = str(self._import)
|
||||
if self._type == nwItemType.FILE:
|
||||
nameAttrib["active"] = str(self._active)
|
||||
return data
|
||||
|
||||
xPack = etree.SubElement(xParent, "item", attrib=itemAttrib)
|
||||
self._subPack(xPack, "meta", attrib=metaAttrib)
|
||||
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'.
|
||||
def unpack(self, data):
|
||||
"""Set the values from a data dictionary.
|
||||
"""
|
||||
if xItem.tag != "item":
|
||||
logger.error("XML entry is not an NWItem")
|
||||
return False
|
||||
|
||||
if "handle" in xItem.attrib:
|
||||
self.setHandle(xItem.attrib["handle"])
|
||||
if "handle" in data:
|
||||
self.setHandle(data["handle"])
|
||||
else:
|
||||
logger.error("XML item entry does not have a handle")
|
||||
logger.error("Item does not have a handle")
|
||||
return False
|
||||
|
||||
self.setParent(xItem.attrib.get("parent", None))
|
||||
self.setRoot(xItem.attrib.get("root", None))
|
||||
self.setOrder(xItem.attrib.get("order", 0))
|
||||
self.setType(xItem.attrib.get("type", nwItemType.NO_TYPE))
|
||||
self.setClass(xItem.attrib.get("class", nwItemClass.NO_CLASS))
|
||||
self.setLayout(xItem.attrib.get("layout", nwItemLayout.NO_LAYOUT))
|
||||
self.setName(data.get("label", ""))
|
||||
self.setParent(data.get("parent", None))
|
||||
self.setRoot(data.get("root", None))
|
||||
self.setOrder(data.get("order", 0))
|
||||
self.setType(data.get("type", nwItemType.NO_TYPE))
|
||||
self.setClass(data.get("class", nwItemClass.NO_CLASS))
|
||||
self.setLayout(data.get("layout", nwItemLayout.NO_LAYOUT))
|
||||
|
||||
for xValue in xItem:
|
||||
if xValue.tag == "meta":
|
||||
self.setExpanded(xValue.attrib.get("expanded", False))
|
||||
self.setMainHeading(xValue.attrib.get("mainHeading", "H0"))
|
||||
self.setCharCount(xValue.attrib.get("charCount", 0))
|
||||
self.setWordCount(xValue.attrib.get("wordCount", 0))
|
||||
self.setParaCount(xValue.attrib.get("paraCount", 0))
|
||||
self.setCursorPos(xValue.attrib.get("cursorPos", 0))
|
||||
elif xValue.tag == "name":
|
||||
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)
|
||||
self.setExpanded(data.get("expanded", False))
|
||||
self.setStatus(data.get("status", None))
|
||||
self.setImport(data.get("import", None))
|
||||
self.setMainHeading(data.get("heading", "H0"))
|
||||
self.setCharCount(data.get("charCount", 0))
|
||||
self.setWordCount(data.get("wordCount", 0))
|
||||
self.setParaCount(data.get("paraCount", 0))
|
||||
self.setCursorPos(data.get("cursorPos", 0))
|
||||
self.setActive(data.get("active", True))
|
||||
|
||||
# Make some checks to ensure consistency
|
||||
if self._type == nwItemType.ROOT:
|
||||
@@ -256,26 +217,17 @@ class NWItem:
|
||||
self._parent = None # Root items cannot have a parent
|
||||
|
||||
if self._type != nwItemType.FILE:
|
||||
self._charCount = 0 # Only set for files
|
||||
self._wordCount = 0 # Only set for files
|
||||
self._paraCount = 0 # Only set for files
|
||||
self._cursorPos = 0 # Only set for files
|
||||
# Reset values that should only be set for files
|
||||
self._layout = nwItemLayout.NO_LAYOUT
|
||||
self._heading = "H0"
|
||||
self._active = False
|
||||
self._charCount = 0
|
||||
self._wordCount = 0
|
||||
self._paraCount = 0
|
||||
self._cursorPos = 0
|
||||
|
||||
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
|
||||
##
|
||||
@@ -310,11 +262,11 @@ class NWItem:
|
||||
the current item based on its class.
|
||||
"""
|
||||
if self.isNovelLike():
|
||||
stName = self.theProject.statusItems.name(self._status)
|
||||
stIcon = self.theProject.statusItems.icon(self._status) if incIcon else None
|
||||
stName = self.theProject.data.itemStatus.name(self._status)
|
||||
stIcon = self.theProject.data.itemStatus.icon(self._status) if incIcon else None
|
||||
else:
|
||||
stName = self.theProject.importItems.name(self._import)
|
||||
stIcon = self.theProject.importItems.icon(self._import) if incIcon else None
|
||||
stName = self.theProject.data.itemImport.name(self._import)
|
||||
stIcon = self.theProject.data.itemImport.icon(self._import) if incIcon else None
|
||||
return stName, stIcon
|
||||
|
||||
##
|
||||
@@ -451,8 +403,6 @@ class NWItem:
|
||||
self._type = value
|
||||
elif isItemType(value):
|
||||
self._type = nwItemType[value]
|
||||
elif value == "TRASH":
|
||||
self._type = nwItemType.ROOT
|
||||
else:
|
||||
logger.error("Unrecognised item type '%s'", value)
|
||||
self._type = nwItemType.NO_TYPE
|
||||
@@ -479,8 +429,6 @@ class NWItem:
|
||||
self._layout = value
|
||||
elif isItemLayout(value):
|
||||
self._layout = nwItemLayout[value]
|
||||
elif value in ("TITLE", "PAGE", "BOOK", "PARTITION", "UNNUMBERED", "CHAPTER", "SCENE"):
|
||||
self._layout = nwItemLayout.DOCUMENT
|
||||
else:
|
||||
logger.error("Unrecognised item layout '%s'", value)
|
||||
self._layout = nwItemLayout.NO_LAYOUT
|
||||
@@ -490,14 +438,14 @@ class NWItem:
|
||||
"""Set the item status by looking it up in the valid status
|
||||
items of the current project.
|
||||
"""
|
||||
self._status = self.theProject.statusItems.check(value)
|
||||
self._status = self.theProject.data.itemStatus.check(value)
|
||||
return
|
||||
|
||||
def setImport(self, value):
|
||||
"""Set the item importance by looking it up in the valid import
|
||||
items of the current project.
|
||||
"""
|
||||
self._import = self.theProject.importItems.check(value)
|
||||
self._import = self.theProject.data.itemImport.check(value)
|
||||
return
|
||||
|
||||
def setActive(self, state):
|
||||
@@ -532,25 +480,37 @@ class NWItem:
|
||||
def setCharCount(self, count):
|
||||
"""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
|
||||
|
||||
def setWordCount(self, count):
|
||||
"""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
|
||||
|
||||
def setParaCount(self, count):
|
||||
"""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
|
||||
|
||||
def setCursorPos(self, position):
|
||||
"""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
|
||||
|
||||
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 novelwriter
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from PyQt5.QtGui import QIcon, QPainter, QPainterPath, QPixmap, QColor
|
||||
from PyQt5.QtCore import QRectF, Qt
|
||||
|
||||
from novelwriter.common import checkInt, minmax, simplified
|
||||
from novelwriter.common import minmax, simplified
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -47,7 +45,6 @@ class NWStatus:
|
||||
|
||||
self._type = type
|
||||
self._store = {}
|
||||
self._reverse = {}
|
||||
self._default = None
|
||||
|
||||
self._iPX = novelwriter.CONFIG.pxInt(24)
|
||||
@@ -58,7 +55,7 @@ class NWStatus:
|
||||
self._iconPath = QPainterPath()
|
||||
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:
|
||||
self._prefix = "s"
|
||||
@@ -80,17 +77,19 @@ class NWStatus:
|
||||
if len(col) != 3:
|
||||
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)
|
||||
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] = {
|
||||
"name": name,
|
||||
"icon": self._createIcon(col),
|
||||
"cols": col,
|
||||
"icon": self._createIcon(cR, cG, cB),
|
||||
"cols": (cR, cG, cB),
|
||||
"count": count,
|
||||
}
|
||||
self._reverse[name] = key
|
||||
|
||||
if self._default is None:
|
||||
self._default = key
|
||||
@@ -106,7 +105,6 @@ class NWStatus:
|
||||
if self._store[key]["count"] > 0:
|
||||
return False
|
||||
|
||||
del self._reverse[self._store[key]["name"]]
|
||||
del self._store[key]
|
||||
|
||||
keys = list(self._store.keys())
|
||||
@@ -123,8 +121,6 @@ class NWStatus:
|
||||
"""
|
||||
if self._isKey(value) and value in self._store:
|
||||
return value
|
||||
elif value in self._reverse:
|
||||
return self._reverse[value]
|
||||
elif self._default is not None:
|
||||
return self._default
|
||||
else:
|
||||
@@ -206,37 +202,30 @@ class NWStatus:
|
||||
self._store[key]["count"] += 1
|
||||
return
|
||||
|
||||
def packXML(self, xParent):
|
||||
"""Pack the status entries into an XML object for saving to the
|
||||
main project file.
|
||||
def pack(self):
|
||||
"""Pack the status entries into a dictionary.
|
||||
"""
|
||||
for key, data in self._store.items():
|
||||
xSub = etree.SubElement(xParent, "entry", attrib={
|
||||
yield (data["name"], {
|
||||
"key": key,
|
||||
"count": str(data["count"]),
|
||||
"red": str(data["cols"][0]),
|
||||
"green": str(data["cols"][1]),
|
||||
"blue": str(data["cols"][2]),
|
||||
})
|
||||
xSub.text = data["name"]
|
||||
return
|
||||
|
||||
return True
|
||||
|
||||
def unpackXML(self, xParent):
|
||||
"""Unpack an XML tree and set the class values.
|
||||
def unpack(self, data):
|
||||
"""Unpack a data dictionary and set the class values.
|
||||
"""
|
||||
self._store = {}
|
||||
self._reverse = {}
|
||||
self._default = None
|
||||
|
||||
for xChild in xParent:
|
||||
key = xChild.attrib.get("key", None)
|
||||
name = xChild.text.strip()
|
||||
count = max(checkInt(xChild.attrib.get("count", 0), 0), 0)
|
||||
red = minmax(checkInt(xChild.attrib.get("red", 100), 100), 0, 255)
|
||||
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)
|
||||
for key, entry in data.items():
|
||||
label = entry.get("label", "")
|
||||
colour = entry.get("colour", (100, 100, 100))
|
||||
count = entry.get("count", 0)
|
||||
self.write(key, label, colour, count)
|
||||
|
||||
return True
|
||||
|
||||
@@ -270,7 +259,7 @@ class NWStatus:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _createIcon(self, col):
|
||||
def _createIcon(self, red, green, blue):
|
||||
"""Generate an icon for a status label.
|
||||
"""
|
||||
pixmap = QPixmap(self._iPX, self._iPX)
|
||||
@@ -278,7 +267,7 @@ class NWStatus:
|
||||
|
||||
painter = QPainter(pixmap)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
painter.fillPath(self._iconPath, QColor(*col))
|
||||
painter.fillPath(self._iconPath, QColor(red, green, blue))
|
||||
painter.end()
|
||||
|
||||
return QIcon(pixmap)
|
||||
|
||||
@@ -315,7 +315,7 @@ class ToHtml(Tokenizer):
|
||||
"</body>\n"
|
||||
"</html>\n"
|
||||
).format(
|
||||
projTitle=self.theProject.projName,
|
||||
projTitle=self.theProject.data.name,
|
||||
htmlStyle="\n".join(theStyle),
|
||||
bodyText=bodyText,
|
||||
)
|
||||
|
||||
@@ -327,9 +327,10 @@ class Tokenizer(ABC):
|
||||
"""Run trough the various replace doctionaries.
|
||||
"""
|
||||
# Process the user's auto-replace dictionary
|
||||
if len(self.theProject.autoReplace) > 0:
|
||||
autoReplace = self.theProject.data.autoReplace
|
||||
if len(autoReplace) > 0:
|
||||
repDict = {}
|
||||
for aKey, aVal in self.theProject.autoReplace.items():
|
||||
for aKey, aVal in autoReplace.items():
|
||||
repDict[f"<{aKey}>"] = aVal
|
||||
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)
|
||||
|
||||
@@ -261,8 +261,8 @@ class ToOdt(Tokenizer):
|
||||
# ===============
|
||||
|
||||
if self._headerText == "":
|
||||
theTitle = self.theProject.bookTitle
|
||||
theAuth = self.theProject.getAuthors()
|
||||
theTitle = self.theProject.data.title
|
||||
theAuth = self.theProject.getFormattedAuthors()
|
||||
self._headerText = f"{theTitle} / {theAuth} /"
|
||||
|
||||
# Create Roots
|
||||
|
||||
+10
-17
@@ -27,8 +27,6 @@ import os
|
||||
import random
|
||||
import logging
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import checkHandle
|
||||
@@ -114,30 +112,25 @@ class NWTree:
|
||||
|
||||
return True
|
||||
|
||||
def packXML(self, xParent):
|
||||
def pack(self):
|
||||
"""Pack the content of the tree into the provided XML object. In
|
||||
the order defined by the _treeOrder list.
|
||||
"""
|
||||
xContent = etree.SubElement(xParent, "content", attrib={
|
||||
"count": str(len(self._treeOrder))}
|
||||
)
|
||||
tree = []
|
||||
for tHandle in self._treeOrder:
|
||||
tItem = self.__getitem__(tHandle)
|
||||
tItem.packXML(xContent)
|
||||
return
|
||||
if tItem:
|
||||
tree.append(tItem.pack())
|
||||
return tree
|
||||
|
||||
def unpackXML(self, xContent):
|
||||
"""Iterate through all items of a content XML object and add
|
||||
them to the project tree.
|
||||
def unpack(self, data):
|
||||
"""Iterate through all items of a list and add them to the
|
||||
project tree.
|
||||
"""
|
||||
if xContent.tag != "content":
|
||||
logger.error("XML entry is not a NWTree")
|
||||
return False
|
||||
|
||||
self.clear()
|
||||
for xItem in xContent:
|
||||
for item in data:
|
||||
nwItem = NWItem(self.theProject)
|
||||
if nwItem.unpackXML(xItem):
|
||||
if nwItem.unpack(item):
|
||||
self.append(nwItem.itemHandle, nwItem.itemParent, nwItem)
|
||||
nwItem.saveInitialCount()
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ class GuiProjectDetailsMain(QWidget):
|
||||
# Header
|
||||
# ======
|
||||
|
||||
self.bookTitle = QLabel(self.theProject.bookTitle)
|
||||
self.bookTitle = QLabel(self.theProject.data.title)
|
||||
bookFont = self.bookTitle.font()
|
||||
bookFont.setPointSizeF(2.2*fPt)
|
||||
bookFont.setWeight(QFont.Bold)
|
||||
@@ -166,7 +166,7 @@ class GuiProjectDetailsMain(QWidget):
|
||||
self.bookTitle.setWordWrap(True)
|
||||
|
||||
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.setPointSizeF(0.8*fPt)
|
||||
@@ -175,7 +175,9 @@ class GuiProjectDetailsMain(QWidget):
|
||||
self.projName.setAlignment(Qt.AlignHCenter)
|
||||
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.setPointSizeF(1.2*fPt)
|
||||
self.bookAuthors.setFont(authFont)
|
||||
@@ -255,7 +257,7 @@ class GuiProjectDetailsMain(QWidget):
|
||||
self.wordCountVal.setText(f"{nwCount:n}")
|
||||
self.chapCountVal.setText(f"{hCounts[2]: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.projPathVal.setText(self.theProject.projPath)
|
||||
|
||||
@@ -114,13 +114,13 @@ class GuiProjectSettings(PagedDialog):
|
||||
spellLang = self.tabMain.spellLang.currentData()
|
||||
doBackup = not self.tabMain.doBackup.isChecked()
|
||||
|
||||
self.theProject.setProjectName(projName)
|
||||
self.theProject.setBookTitle(bookTitle)
|
||||
self.theProject.setBookAuthors(bookAuthors)
|
||||
self.theProject.setProjBackup(doBackup)
|
||||
self.theProject.data.setName(projName)
|
||||
self.theProject.data.setTitle(bookTitle)
|
||||
self.theProject.data.setAuthors(bookAuthors)
|
||||
self.theProject.data.setDoBackup(doBackup)
|
||||
|
||||
# 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:
|
||||
newList, delList = self.tabStatus.getNewList()
|
||||
@@ -135,7 +135,7 @@ class GuiProjectSettings(PagedDialog):
|
||||
|
||||
if self.tabReplace.arChanged:
|
||||
newList = self.tabReplace.getNewList()
|
||||
self.theProject.setAutoReplace(newList)
|
||||
self.theProject.data.setAutoReplace(newList)
|
||||
|
||||
self._saveGuiSettings()
|
||||
self.accept()
|
||||
@@ -209,7 +209,7 @@ class GuiProjectEditMain(QWidget):
|
||||
self.editName = QLineEdit()
|
||||
self.editName.setMaxLength(200)
|
||||
self.editName.setMaximumWidth(xW)
|
||||
self.editName.setText(self.theProject.projName)
|
||||
self.editName.setText(self.theProject.data.name)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Project name"),
|
||||
self.editName,
|
||||
@@ -219,7 +219,7 @@ class GuiProjectEditMain(QWidget):
|
||||
self.editTitle = QLineEdit()
|
||||
self.editTitle.setMaxLength(200)
|
||||
self.editTitle.setMaximumWidth(xW)
|
||||
self.editTitle.setText(self.theProject.bookTitle)
|
||||
self.editTitle.setText(self.theProject.data.title)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Novel title"),
|
||||
self.editTitle,
|
||||
@@ -229,7 +229,7 @@ class GuiProjectEditMain(QWidget):
|
||||
self.editAuthors = QPlainTextEdit()
|
||||
self.editAuthors.setMaximumHeight(xH)
|
||||
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.tr("Author(s)"),
|
||||
self.editAuthors,
|
||||
@@ -252,13 +252,13 @@ class GuiProjectEditMain(QWidget):
|
||||
)
|
||||
|
||||
spellIdx = 0
|
||||
if self.theProject.projSpell is not None:
|
||||
spellIdx = self.spellLang.findData(self.theProject.projSpell)
|
||||
if self.theProject.data.spellLang is not None:
|
||||
spellIdx = self.spellLang.findData(self.theProject.data.spellLang)
|
||||
if spellIdx != -1:
|
||||
self.spellLang.setCurrentIndex(spellIdx)
|
||||
|
||||
self.doBackup = QSwitch(self)
|
||||
self.doBackup.setChecked(not self.theProject.doBackup)
|
||||
self.doBackup.setChecked(not self.theProject.data.doBackup)
|
||||
self.mainForm.addRow(
|
||||
self.tr("No backup on close"),
|
||||
self.doBackup,
|
||||
@@ -288,11 +288,11 @@ class GuiProjectEditStatus(QWidget):
|
||||
self.mainTheme = projGui.mainGui.mainTheme
|
||||
|
||||
if isStatus:
|
||||
self.theStatus = self.theProject.statusItems
|
||||
self.theStatus = self.theProject.data.itemStatus
|
||||
pageLabel = self.tr("Novel File Status Levels")
|
||||
colSetting = "statusColW"
|
||||
else:
|
||||
self.theStatus = self.theProject.importItems
|
||||
self.theStatus = self.theProject.data.itemImport
|
||||
pageLabel = self.tr("Note File Importance Levels")
|
||||
colSetting = "importColW"
|
||||
|
||||
@@ -578,7 +578,7 @@ class GuiProjectEditReplace(QWidget):
|
||||
self.listBox.setColumnWidth(self.COL_KEY, wCol0)
|
||||
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])
|
||||
self.listBox.addTopLevelItem(newItem)
|
||||
|
||||
|
||||
@@ -684,10 +684,10 @@ class GuiDocEditor(QTextEdit):
|
||||
"""Set the spell checker dictionary language, and emit the
|
||||
dictionary changed signal.
|
||||
"""
|
||||
if self.theProject.projSpell is None:
|
||||
if self.theProject.data.spellLang is None:
|
||||
theLang = self.mainConf.spellLanguage
|
||||
else:
|
||||
theLang = self.theProject.projSpell
|
||||
theLang = self.theProject.data.spellLang
|
||||
|
||||
self.spEnchant.setLanguage(theLang, self.theProject.projDict)
|
||||
_, theProvider = self.spEnchant.describeDict()
|
||||
@@ -721,7 +721,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
self._spellCheck = theMode
|
||||
self.mainGui.mainMenu.setSpellCheck(theMode)
|
||||
self.theProject.setSpellCheck(theMode)
|
||||
self.theProject.data.setSpellCheck(theMode)
|
||||
self.highLight.setSpellCheck(theMode)
|
||||
if not self._bigDoc:
|
||||
self.spellCheckDocument()
|
||||
|
||||
@@ -217,7 +217,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
self.verticalScrollBar().setValue(sPos)
|
||||
|
||||
self._docHandle = tHandle
|
||||
self.theProject.setLastViewed(tHandle)
|
||||
self.theProject._data.setLastHandle(tHandle, "viewer")
|
||||
self.docHeader.setTitleFromHandle(self._docHandle)
|
||||
self.updateDocMargins()
|
||||
|
||||
|
||||
@@ -794,7 +794,7 @@ class GuiMainMenu(QMenuBar):
|
||||
# Tools > Check Spelling
|
||||
self.aSpellCheck = QAction(self.tr("Check Spelling"), self)
|
||||
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.setShortcut("Ctrl+F7")
|
||||
self.toolsMenu.addAction(self.aSpellCheck)
|
||||
|
||||
@@ -109,7 +109,7 @@ class GuiNovelView(QWidget):
|
||||
def refreshTree(self):
|
||||
"""Refresh the current tree.
|
||||
"""
|
||||
self.novelTree.refreshTree(rootHandle=self.theProject.lastNovel)
|
||||
self.novelTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("novelTree"))
|
||||
return
|
||||
|
||||
def clearProject(self):
|
||||
@@ -122,7 +122,7 @@ class GuiNovelView(QWidget):
|
||||
def openProjectTasks(self):
|
||||
"""Run open project tasks.
|
||||
"""
|
||||
lastNovel = self.theProject.lastNovel
|
||||
lastNovel = self.theProject.data.getLastHandle("novelTree")
|
||||
if lastNovel not in self.theProject.tree:
|
||||
lastNovel = self.theProject.tree.findRoot(nwItemClass.NOVEL)
|
||||
|
||||
@@ -319,7 +319,7 @@ class GuiNovelToolBar(QWidget):
|
||||
def _refreshNovelTree(self):
|
||||
"""Rebuild the current tree.
|
||||
"""
|
||||
rootHandle = self.theProject.lastNovel
|
||||
rootHandle = self.theProject.data.getLastHandle("novelTree")
|
||||
self.novelView.novelTree.refreshTree(rootHandle=rootHandle, overRide=True)
|
||||
return
|
||||
|
||||
@@ -485,7 +485,7 @@ class GuiNovelTree(QTreeWidget):
|
||||
titleKey = selItem[0].data(self.C_TITLE, self.D_KEY)
|
||||
|
||||
self._populateTree(rootHandle)
|
||||
self.theProject.setLastNovelViewed(rootHandle)
|
||||
self.theProject.data.setLastHandle(rootHandle, "novelTree")
|
||||
|
||||
if titleKey is not None and titleKey in self._treeMap:
|
||||
self._treeMap[titleKey].setSelected(True)
|
||||
@@ -523,7 +523,8 @@ class GuiNovelTree(QTreeWidget):
|
||||
self._lastCol = colType
|
||||
self.setColumnHidden(self.C_EXTRA, colType == NovelTreeColumn.HIDDEN)
|
||||
if doRefresh:
|
||||
self.refreshTree(rootHandle=self.theProject.lastNovel, overRide=True)
|
||||
lastNovel = self.theProject.data.getLastHandle("novelTree")
|
||||
self.refreshTree(rootHandle=lastNovel, overRide=True)
|
||||
return
|
||||
|
||||
def setActiveHandle(self, tHandle):
|
||||
|
||||
@@ -114,7 +114,7 @@ class GuiOutlineView(QWidget):
|
||||
def refreshTree(self):
|
||||
"""Refresh the current tree.
|
||||
"""
|
||||
self.outlineTree.refreshTree(rootHandle=self.theProject.lastOutline)
|
||||
self.outlineTree.refreshTree(rootHandle=self.theProject.data.getLastHandle("outline"))
|
||||
return
|
||||
|
||||
def clearProject(self):
|
||||
@@ -126,7 +126,7 @@ class GuiOutlineView(QWidget):
|
||||
def openProjectTasks(self):
|
||||
"""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):
|
||||
lastOutline = self.theProject.tree.findRoot(nwItemClass.NOVEL)
|
||||
|
||||
@@ -504,7 +504,7 @@ class GuiOutlineTree(QTreeWidget):
|
||||
return
|
||||
|
||||
self._populateTree(rootHandle)
|
||||
self.theProject.setLastOutlineViewed(rootHandle or None)
|
||||
self.theProject.data.setLastHandle(rootHandle or None, "outline")
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -1208,7 +1208,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
checkMark = f" ({nwUnicode.U_CHECK})"
|
||||
if tItem.isNovelLike():
|
||||
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 "")
|
||||
aStatus = mStatus.addAction(entry["icon"], entryName)
|
||||
aStatus.triggered.connect(
|
||||
@@ -1221,7 +1221,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
)
|
||||
else:
|
||||
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 "")
|
||||
aImport = mImport.addAction(entry["icon"], entryName)
|
||||
aImport.triggered.connect(
|
||||
|
||||
+23
-19
@@ -197,6 +197,8 @@ class GuiMain(QMainWindow):
|
||||
# Connect Signals
|
||||
# ===============
|
||||
|
||||
self.theProject.projectStatusChanged.connect(self.mainStatus.doUpdateProjectStatus)
|
||||
|
||||
self.viewsBar.viewChangeRequested.connect(self._changeView)
|
||||
|
||||
self.projView.selectedItemChanged.connect(self.itemDetails.updateViewBox)
|
||||
@@ -383,7 +385,7 @@ class GuiMain(QMainWindow):
|
||||
self.mainStatus.setDocumentStatus(nwState.NONE)
|
||||
self.mainStatus.setStatus(self.tr("New project created ..."))
|
||||
|
||||
self._updateWindowTitle(self.theProject.projName)
|
||||
self._updateWindowTitle(self.theProject.data.name)
|
||||
|
||||
else:
|
||||
self.theProject.clearProject()
|
||||
@@ -417,7 +419,7 @@ class GuiMain(QMainWindow):
|
||||
if self.theProject.projAltered:
|
||||
saveOK = self.saveProject()
|
||||
doBackup = False
|
||||
if self.theProject.doBackup and self.mainConf.backupOnClose:
|
||||
if self.theProject.data.doBackup and self.mainConf.backupOnClose:
|
||||
doBackup = True
|
||||
if self.mainConf.askBeforeBackup:
|
||||
msgYes = self.askQuestion(
|
||||
@@ -521,10 +523,10 @@ class GuiMain(QMainWindow):
|
||||
self.theProject.index.loadIndex()
|
||||
|
||||
# Update GUI
|
||||
self._updateWindowTitle(self.theProject.projName)
|
||||
self._updateWindowTitle(self.theProject.data.name)
|
||||
self.rebuildTrees()
|
||||
self.docEditor.setDictionaries()
|
||||
self.docEditor.toggleSpellCheck(self.theProject.spellCheck)
|
||||
self.docEditor.toggleSpellCheck(self.theProject.data.spellCheck)
|
||||
self.mainStatus.setRefTime(self.theProject.projOpened)
|
||||
self.projView.openProjectTasks()
|
||||
self.novelView.openProjectTasks()
|
||||
@@ -532,11 +534,13 @@ class GuiMain(QMainWindow):
|
||||
self._updateStatusWordCount()
|
||||
|
||||
# Restore previously open documents, if any
|
||||
if self.theProject.lastEdited is not None:
|
||||
self.openDocument(self.theProject.lastEdited, doScroll=True)
|
||||
lastEdited = self.theProject.data.getLastHandle("editor")
|
||||
if lastEdited is not None:
|
||||
self.openDocument(lastEdited, doScroll=True)
|
||||
|
||||
if self.theProject.lastViewed is not None:
|
||||
self.viewDocument(self.theProject.lastViewed)
|
||||
lastViewed = self.theProject.data.getLastHandle("viewer")
|
||||
if lastViewed is not None:
|
||||
self.viewDocument(lastViewed)
|
||||
|
||||
# Check if we need to rebuild the index
|
||||
if self.theProject.index.indexBroken:
|
||||
@@ -607,7 +611,7 @@ class GuiMain(QMainWindow):
|
||||
if self.docEditor.loadText(tHandle, tLine):
|
||||
if changeFocus:
|
||||
self.docEditor.setFocus()
|
||||
self.theProject.setLastEdited(tHandle)
|
||||
self.theProject.data.setLastHandle(tHandle, "editor")
|
||||
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
|
||||
self.novelView.setActiveHandle(tHandle)
|
||||
else:
|
||||
@@ -676,7 +680,7 @@ class GuiMain(QMainWindow):
|
||||
tHandle = self.projView.getSelectedHandle()
|
||||
|
||||
if tHandle is None:
|
||||
tHandle = self.theProject.lastViewed
|
||||
tHandle = self.theProject.data.getLastHandle("viewer")
|
||||
|
||||
if tHandle is None:
|
||||
logger.debug("No document to view, giving up")
|
||||
@@ -960,7 +964,7 @@ class GuiMain(QMainWindow):
|
||||
if dlgProj.spellChanged:
|
||||
self.docEditor.setDictionaries()
|
||||
self.itemDetails.refreshDetails()
|
||||
self._updateWindowTitle(self.theProject.projName)
|
||||
self._updateWindowTitle(self.theProject.data.name)
|
||||
|
||||
return True
|
||||
|
||||
@@ -1223,14 +1227,14 @@ class GuiMain(QMainWindow):
|
||||
"""Close the document edit panel. This does not hide the editor.
|
||||
"""
|
||||
self.closeDocument()
|
||||
self.theProject.setLastEdited(None)
|
||||
self.theProject.data.setLastHandle(None, "editor")
|
||||
return
|
||||
|
||||
def closeDocViewer(self):
|
||||
"""Close the document view panel.
|
||||
"""
|
||||
self.docViewer.clearViewer()
|
||||
self.theProject.setLastViewed(None)
|
||||
self.theProject.data.setLastHandle(None, "viewer")
|
||||
bPos = self.splitMain.sizes()
|
||||
self.splitView.setVisible(False)
|
||||
self.splitDocs.setSizes([bPos[1], 0])
|
||||
@@ -1555,13 +1559,13 @@ class GuiMain(QMainWindow):
|
||||
|
||||
self.theProject.updateWordCounts()
|
||||
if self.mainConf.incNotesWCount:
|
||||
currWords = self.theProject.currWCount
|
||||
diffWords = currWords - self.theProject.lastWCount
|
||||
iTotal = sum(self.theProject.data.initCounts)
|
||||
cTotal = sum(self.theProject.data.currCounts)
|
||||
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
||||
else:
|
||||
currWords = self.theProject.currNovelWC
|
||||
diffWords = currWords - self.theProject.lastNovelWC
|
||||
|
||||
self.mainStatus.setProjectStats(currWords, diffWords)
|
||||
iNovel, _ = self.theProject.data.initCounts
|
||||
cNovel, _ = self.theProject.data.currCounts
|
||||
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
||||
|
||||
return
|
||||
|
||||
|
||||
+11
-11
@@ -126,7 +126,7 @@ class GuiBuildNovel(QDialog):
|
||||
self.fmtTitle.setMinimumWidth(xFmt)
|
||||
self.fmtTitle.setToolTip(fmtHelp)
|
||||
self.fmtTitle.setText(
|
||||
self._reFmtCodes(self.theProject.titleFormat["title"])
|
||||
self._reFmtCodes(self.theProject.data.getTitleFormat("title"))
|
||||
)
|
||||
|
||||
self.fmtChapter = QLineEdit()
|
||||
@@ -134,7 +134,7 @@ class GuiBuildNovel(QDialog):
|
||||
self.fmtChapter.setMinimumWidth(xFmt)
|
||||
self.fmtChapter.setToolTip(fmtHelp)
|
||||
self.fmtChapter.setText(
|
||||
self._reFmtCodes(self.theProject.titleFormat["chapter"])
|
||||
self._reFmtCodes(self.theProject.data.getTitleFormat("chapter"))
|
||||
)
|
||||
|
||||
self.fmtUnnumbered = QLineEdit()
|
||||
@@ -142,7 +142,7 @@ class GuiBuildNovel(QDialog):
|
||||
self.fmtUnnumbered.setMinimumWidth(xFmt)
|
||||
self.fmtUnnumbered.setToolTip(fmtHelp)
|
||||
self.fmtUnnumbered.setText(
|
||||
self._reFmtCodes(self.theProject.titleFormat["unnumbered"])
|
||||
self._reFmtCodes(self.theProject.data.getTitleFormat("unnumbered"))
|
||||
)
|
||||
|
||||
self.fmtScene = QLineEdit()
|
||||
@@ -150,7 +150,7 @@ class GuiBuildNovel(QDialog):
|
||||
self.fmtScene.setMinimumWidth(xFmt)
|
||||
self.fmtScene.setToolTip(fmtHelp + fmtScHelp)
|
||||
self.fmtScene.setText(
|
||||
self._reFmtCodes(self.theProject.titleFormat["scene"])
|
||||
self._reFmtCodes(self.theProject.data.getTitleFormat("scene"))
|
||||
)
|
||||
|
||||
self.fmtSection = QLineEdit()
|
||||
@@ -158,7 +158,7 @@ class GuiBuildNovel(QDialog):
|
||||
self.fmtSection.setMinimumWidth(xFmt)
|
||||
self.fmtSection.setToolTip(fmtHelp + fmtScHelp)
|
||||
self.fmtSection.setText(
|
||||
self._reFmtCodes(self.theProject.titleFormat["section"])
|
||||
self._reFmtCodes(self.theProject.data.getTitleFormat("section"))
|
||||
)
|
||||
|
||||
self.buildLang = QComboBox()
|
||||
@@ -168,7 +168,7 @@ class GuiBuildNovel(QDialog):
|
||||
for langID, langName in theLangs:
|
||||
self.buildLang.addItem(langName, langID)
|
||||
|
||||
langIdx = self.buildLang.findData(self.theProject.projLang)
|
||||
langIdx = self.buildLang.findData(self.theProject.data.language)
|
||||
if langIdx != -1:
|
||||
self.buildLang.setCurrentIndex(langIdx)
|
||||
|
||||
@@ -888,7 +888,7 @@ class GuiBuildNovel(QDialog):
|
||||
# Generate File Name
|
||||
# ==================
|
||||
|
||||
cleanName = makeFileNameSafe(self.theProject.projName)
|
||||
cleanName = makeFileNameSafe(self.theProject.data.name)
|
||||
fileName = "%s.%s" % (cleanName, fileExt)
|
||||
saveDir = self.mainConf.lastPath
|
||||
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:
|
||||
jsonData = {
|
||||
"meta": {
|
||||
"workingTitle": self.theProject.projName,
|
||||
"novelTitle": self.theProject.bookTitle,
|
||||
"authors": self.theProject.bookAuthors,
|
||||
"workingTitle": self.theProject.data.name,
|
||||
"novelTitle": self.theProject.data.title,
|
||||
"authors": self.theProject.data.authors,
|
||||
"buildTime": self.buildTime,
|
||||
}
|
||||
}
|
||||
@@ -1159,7 +1159,7 @@ class GuiBuildNovel(QDialog):
|
||||
logger.debug("Saving GuiBuildNovel settings")
|
||||
|
||||
# Formatting
|
||||
self.theProject.setTitleFormat({
|
||||
self.theProject.data.setTitleFormat({
|
||||
"title": self.fmtTitle.text().strip(),
|
||||
"chapter": self.fmtChapter.text().strip(),
|
||||
"unnumbered": self.fmtUnnumbered.text().strip(),
|
||||
|
||||
+33
-32
@@ -1,37 +1,38 @@
|
||||
<?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>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
<saveCount>1386</saveCount>
|
||||
<saveCount>1409</saveCount>
|
||||
<autoCount>236</autoCount>
|
||||
<editTime>69352</editTime>
|
||||
<editTime>69427</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>636b6aa9b697b</lastEdited>
|
||||
<lastViewed>636b6aa9b697b</lastViewed>
|
||||
<lastNovel>7031beac91f75</lastNovel>
|
||||
<lastOutline>7031beac91f75</lastOutline>
|
||||
<lastWordCount>1363</lastWordCount>
|
||||
<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>
|
||||
<title>%title%</title>
|
||||
<chapter>Chapter %chw%: %title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>Scene %ch%.%sc%: %title%</scene>
|
||||
<section></section>
|
||||
<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>
|
||||
@@ -55,43 +56,43 @@
|
||||
<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" 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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||
@@ -99,11 +100,11 @@
|
||||
<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" 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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||
@@ -127,15 +128,15 @@
|
||||
<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" 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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||
@@ -147,7 +148,7 @@
|
||||
<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" 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>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
||||
@@ -155,7 +156,7 @@
|
||||
<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" 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>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -214,27 +214,6 @@ def mockRnd(monkeypatch):
|
||||
# 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")
|
||||
def nwLipsum(tmpDir):
|
||||
"""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'?>
|
||||
<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>
|
||||
<name>Lorem Ipsum</name>
|
||||
<title>Lorem Ipsum</title>
|
||||
<author>lipsum.com</author>
|
||||
<saveCount>28</saveCount>
|
||||
<saveCount>32</saveCount>
|
||||
<autoCount>24</autoCount>
|
||||
<editTime>1874</editTime>
|
||||
<editTime>1889</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>7a992350f3eb6</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>3847</lastWordCount>
|
||||
<totalWordCount>3847</totalWordCount>
|
||||
<novelWordCount>3109</novelWordCount>
|
||||
<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>
|
||||
<entry key="Rep1">Replace Text 1</entry>
|
||||
<entry key="Rep2">Replace Text 2</entry>
|
||||
</autoReplace>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
<chapter>Chapter %ch%: %title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<entry key="title">%title%</entry>
|
||||
<entry key="chapter">Chapter %ch%: %title%</entry>
|
||||
<entry key="unnumbered">%title%</entry>
|
||||
<entry key="scene">* * *</entry>
|
||||
<entry key="section"></entry>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="sbaa94f" count="3" red="100" green="100" blue="100">New</entry>
|
||||
@@ -50,19 +52,19 @@
|
||||
<name status="sbaa94f" import="i613591">Novel</name>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<item handle="67a8707f2f249" parent="None" root="67a8707f2f249" order="1" type="ROOT" class="CHARACTER">
|
||||
@@ -110,7 +112,7 @@
|
||||
<name status="sbaa94f" import="i613591">Characters</name>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<item handle="6c6afb1247750" parent="None" root="6c6afb1247750" order="2" type="ROOT" class="PLOT">
|
||||
@@ -118,7 +120,7 @@
|
||||
<name status="sbaa94f" import="i613591">Plot</name>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<item handle="60bdf227455cc" parent="None" root="60bdf227455cc" order="3" type="ROOT" class="WORLD">
|
||||
@@ -126,7 +128,7 @@
|
||||
<name status="sbaa94f" import="i613591">World</name>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
</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/>.
|
||||
"""
|
||||
|
||||
from PyQt5.QtCore import QObject
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
# Mock GUI
|
||||
# =========================================================================== #
|
||||
|
||||
class MockGuiMain:
|
||||
class MockGuiMain(QObject):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.mainConf = None
|
||||
self.hasProject = True
|
||||
self.theProject = None
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>Test Custom</name>
|
||||
<title>Test Novel</title>
|
||||
@@ -14,122 +14,123 @@
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<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/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
<chapter>%title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<entry key="title">%title%</entry>
|
||||
<entry key="chapter">%title%</entry>
|
||||
<entry key="unnumbered">%title%</entry>
|
||||
<entry key="scene">* * *</entry>
|
||||
<entry key="section"></entry>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="s000008" count="15" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="15" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i00000c" count="7" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="7" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<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"/>
|
||||
<name status="s000008" import="i00000c">Novel</name>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Title Page</name>
|
||||
<item handle="0000000000009" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||
</item>
|
||||
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Chapter 1</name>
|
||||
<item handle="000000000000a" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Chapter 1</name>
|
||||
</item>
|
||||
<item handle="0000000000013" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 1.1</name>
|
||||
<item handle="000000000000b" parent="000000000000a" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 1.1</name>
|
||||
</item>
|
||||
<item handle="0000000000014" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 1.2</name>
|
||||
<item handle="000000000000c" parent="000000000000a" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 1.2</name>
|
||||
</item>
|
||||
<item handle="0000000000015" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 1.3</name>
|
||||
<item handle="000000000000d" parent="000000000000a" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 1.3</name>
|
||||
</item>
|
||||
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Chapter 2</name>
|
||||
<item handle="000000000000e" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Chapter 2</name>
|
||||
</item>
|
||||
<item handle="0000000000017" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 2.1</name>
|
||||
<item handle="000000000000f" parent="000000000000e" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 2.1</name>
|
||||
</item>
|
||||
<item handle="0000000000018" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 2.2</name>
|
||||
<item handle="0000000000010" parent="000000000000e" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 2.2</name>
|
||||
</item>
|
||||
<item handle="0000000000019" parent="0000000000016" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 2.3</name>
|
||||
<item handle="0000000000011" parent="000000000000e" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 2.3</name>
|
||||
</item>
|
||||
<item handle="000000000001a" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Chapter 3</name>
|
||||
<item handle="0000000000012" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Chapter 3</name>
|
||||
</item>
|
||||
<item handle="000000000001b" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 3.1</name>
|
||||
<item handle="0000000000013" parent="0000000000012" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 3.1</name>
|
||||
</item>
|
||||
<item handle="000000000001c" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 3.2</name>
|
||||
<item handle="0000000000014" parent="0000000000012" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 3.2</name>
|
||||
</item>
|
||||
<item handle="000000000001d" parent="000000000001a" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 3.3</name>
|
||||
<item handle="0000000000015" parent="0000000000012" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 3.3</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Plot</name>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<item handle="000000000001f" parent="000000000001e" root="000000000001e" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Main Plot</name>
|
||||
<item handle="0000000000017" parent="0000000000016" root="0000000000016" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Main Plot</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Characters</name>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<item handle="0000000000021" parent="0000000000020" root="0000000000020" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Protagonist</name>
|
||||
<item handle="0000000000019" parent="0000000000018" root="0000000000018" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Protagonist</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Locations</name>
|
||||
<name status="s000000" import="i000004">Locations</name>
|
||||
</item>
|
||||
<item handle="0000000000023" parent="0000000000022" root="0000000000022" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Main Location</name>
|
||||
<item handle="000000000001b" parent="000000000001a" root="000000000001a" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Main Location</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Archive</name>
|
||||
<name status="s000000" import="i000004">Archive</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Trash</name>
|
||||
<name status="s000000" import="i000004">Trash</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>Test Custom</name>
|
||||
<title>Test Novel</title>
|
||||
@@ -14,98 +14,99 @@
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<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/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
<chapter>%title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<entry key="title">%title%</entry>
|
||||
<entry key="chapter">%title%</entry>
|
||||
<entry key="unnumbered">%title%</entry>
|
||||
<entry key="scene">* * *</entry>
|
||||
<entry key="section"></entry>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="s000008" count="9" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
<entry key="s000000" count="9" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i00000c" count="7" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
<entry key="i000004" count="7" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<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"/>
|
||||
<name status="s000008" import="i00000c">Novel</name>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Title Page</name>
|
||||
<item handle="0000000000009" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||
</item>
|
||||
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 1</name>
|
||||
<item handle="000000000000a" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 1</name>
|
||||
</item>
|
||||
<item handle="0000000000013" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 2</name>
|
||||
<item handle="000000000000b" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 2</name>
|
||||
</item>
|
||||
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 3</name>
|
||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 3</name>
|
||||
</item>
|
||||
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 4</name>
|
||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 4</name>
|
||||
</item>
|
||||
<item handle="0000000000016" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 5</name>
|
||||
<item handle="000000000000e" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 5</name>
|
||||
</item>
|
||||
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Scene 6</name>
|
||||
<item handle="000000000000f" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Scene 6</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Plot</name>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<item handle="0000000000019" parent="0000000000018" root="0000000000018" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Main Plot</name>
|
||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Main Plot</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Characters</name>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<item handle="000000000001b" parent="000000000001a" root="000000000001a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Protagonist</name>
|
||||
<item handle="0000000000013" parent="0000000000012" root="0000000000012" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Protagonist</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Locations</name>
|
||||
<name status="s000000" import="i000004">Locations</name>
|
||||
</item>
|
||||
<item handle="000000000001d" parent="000000000001c" root="000000000001c" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Main Location</name>
|
||||
<item handle="0000000000015" parent="0000000000014" root="0000000000014" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Main Location</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Archive</name>
|
||||
<name status="s000000" import="i000004">Archive</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Trash</name>
|
||||
<name status="s000000" import="i000004">Trash</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
@@ -13,20 +13,21 @@
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>13</lastWordCount>
|
||||
<novelWordCount>10</novelWordCount>
|
||||
<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/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
<chapter>%title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<entry key="title">%title%</entry>
|
||||
<entry key="chapter">%title%</entry>
|
||||
<entry key="unnumbered">%title%</entry>
|
||||
<entry key="scene">* * *</entry>
|
||||
<entry key="section"></entry>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
||||
@@ -59,7 +60,7 @@
|
||||
<name status="s000000" import="i000004">World</name>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Stuff</name>
|
||||
</item>
|
||||
<item handle="0000000000021" parent="0000000000020" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H2" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
||||
<item handle="0000000000011" parent="0000000000010" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H2" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Hello</name>
|
||||
</item>
|
||||
<item handle="0000000000022" 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"/>
|
||||
<item handle="0000000000012" parent="000000000000a" root="000000000000a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="False" heading="H1" charCount="11" wordCount="3" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Jane</name>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -1,8 +1,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>
|
||||
<name>New Project</name>
|
||||
<title></title>
|
||||
<title>None</title>
|
||||
<saveCount>2</saveCount>
|
||||
<autoCount>1</autoCount>
|
||||
<editTime>0</editTime>
|
||||
@@ -12,66 +12,67 @@
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>0</lastWordCount>
|
||||
<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/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
<chapter>%title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<entry key="title">%title%</entry>
|
||||
<entry key="chapter">%title%</entry>
|
||||
<entry key="unnumbered">%title%</entry>
|
||||
<entry key="scene">* * *</entry>
|
||||
<entry key="section"></entry>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="s000008" 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="s00000a" 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="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
|
||||
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry key="i00000c" 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="i00000e" 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="i000004" count="3" red="100" green="100" blue="100">New</entry>
|
||||
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
|
||||
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<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"/>
|
||||
<name status="s000008" import="i00000c">Novel</name>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<item handle="0000000000011" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">Title Page</name>
|
||||
<item handle="0000000000009" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||
</item>
|
||||
<item handle="0000000000012" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">New Chapter</name>
|
||||
<item handle="000000000000a" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||
</item>
|
||||
<item handle="0000000000013" parent="0000000000012" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" mainHeading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000008" import="i00000c" active="True">New Scene</name>
|
||||
<item handle="000000000000b" parent="000000000000a" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Plot</name>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Characters</name>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Locations</name>
|
||||
<name status="s000000" import="i000004">Locations</name>
|
||||
</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"/>
|
||||
<name status="s000008" import="i00000c">Archive</name>
|
||||
<name status="s000000" import="i000004">Archive</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
@@ -13,20 +13,21 @@
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>9</lastWordCount>
|
||||
<novelWordCount>9</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/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
<chapter>%title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<entry key="title">%title%</entry>
|
||||
<entry key="chapter">%title%</entry>
|
||||
<entry key="unnumbered">%title%</entry>
|
||||
<entry key="scene">* * *</entry>
|
||||
<entry key="section"></entry>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="s000000" count="6" red="100" green="100" blue="100">New</entry>
|
||||
@@ -59,7 +60,7 @@
|
||||
<name status="s000000" import="i000004">World</name>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Locations</name>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Timeline</name>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Objects</name>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Custom</name>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Custom</name>
|
||||
</item>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
%%~name: New Note
|
||||
%%~path: 000000000000a/0000000000020
|
||||
%%~path: 000000000000a/0000000000010
|
||||
%%~kind: CHARACTER/NOTE
|
||||
# Jane Doe
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
%%~name: New Note
|
||||
%%~path: 0000000000009/0000000000021
|
||||
%%~path: 0000000000009/0000000000011
|
||||
%%~kind: PLOT/NOTE
|
||||
# Main Plot
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
%%~name: New Note
|
||||
%%~path: 000000000000b/0000000000022
|
||||
%%~path: 000000000000b/0000000000012
|
||||
%%~kind: WORLD/NOTE
|
||||
# Main Location
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
<author>Jane Doe</author>
|
||||
<saveCount>4</saveCount>
|
||||
<autoCount>2</autoCount>
|
||||
<editTime>4</editTime>
|
||||
<editTime>3</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>000000000000f</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>0000000000008</lastNovel>
|
||||
<lastOutline>0000000000008</lastOutline>
|
||||
<lastWordCount>163</lastWordCount>
|
||||
<novelWordCount>136</novelWordCount>
|
||||
<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/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
<chapter>%title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<entry key="title">%title%</entry>
|
||||
<entry key="chapter">%title%</entry>
|
||||
<entry key="unnumbered">%title%</entry>
|
||||
<entry key="scene">* * *</entry>
|
||||
<entry key="section"></entry>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||
@@ -47,7 +48,7 @@
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" 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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
||||
<meta expanded="True"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<item handle="0000000000021" 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"/>
|
||||
<item handle="0000000000011" parent="0000000000009" root="0000000000009" order="0" type="FILE" class="PLOT" layout="NOTE">
|
||||
<meta expanded="False" heading="H1" charCount="48" wordCount="10" paraCount="1" cursorPos="69"/>
|
||||
<name status="s000000" import="i000004" active="True">New Note</name>
|
||||
</item>
|
||||
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<item handle="0000000000020" 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"/>
|
||||
<item handle="0000000000010" parent="000000000000a" root="000000000000a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="False" heading="H1" charCount="34" wordCount="8" paraCount="1" cursorPos="51"/>
|
||||
<name status="s000000" import="i000004" active="True">New Note</name>
|
||||
</item>
|
||||
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
|
||||
<meta expanded="True"/>
|
||||
<name status="s000000" import="i000004">World</name>
|
||||
</item>
|
||||
<item handle="0000000000022" 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"/>
|
||||
<item handle="0000000000012" parent="000000000000b" root="000000000000b" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="False" heading="H1" charCount="51" wordCount="9" paraCount="1" cursorPos="68"/>
|
||||
<name status="s000000" import="i000004" active="True">New Note</name>
|
||||
</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"/>
|
||||
<name status="s000000" import="i000004">Trash</name>
|
||||
</item>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
@@ -13,20 +13,21 @@
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<lastEdited>None</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastNovel>None</lastNovel>
|
||||
<lastOutline>None</lastOutline>
|
||||
<lastWordCount>9</lastWordCount>
|
||||
<novelWordCount>9</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/>
|
||||
<titleFormat>
|
||||
<title>%title%</title>
|
||||
<chapter>%title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>* * *</scene>
|
||||
<section></section>
|
||||
<entry key="title">%title%</entry>
|
||||
<entry key="chapter">%title%</entry>
|
||||
<entry key="unnumbered">%title%</entry>
|
||||
<entry key="scene">* * *</entry>
|
||||
<entry key="section"></entry>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||
@@ -47,7 +48,7 @@
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" 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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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>
|
||||
</item>
|
||||
<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
|
||||
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, "NotNone") == "NotNone"
|
||||
assert checkString(1, "NotNone") == "NotNone"
|
||||
assert checkString(1.0, "NotNone") == "NotNone"
|
||||
assert checkString(True, "NotNone") == "NotNone"
|
||||
assert checkString("None", "default") == "None"
|
||||
assert checkString("Text", "default") == "Text"
|
||||
assert checkString(None, "default") == "default"
|
||||
assert checkString(1, "default") == "default"
|
||||
assert checkString(1.0, "default") == "default"
|
||||
assert checkString(True, "default") == "default"
|
||||
|
||||
# END Test testBaseCommon_CheckString
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
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.0, 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
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
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.0, 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
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
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("False", True) is False
|
||||
assert checkBool("Boo", False) is False
|
||||
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(1, False) is True
|
||||
assert checkBool(2, True) is True
|
||||
|
||||
@@ -23,7 +23,7 @@ import os
|
||||
import pytest
|
||||
|
||||
from mock import causeOSError
|
||||
from tools import readFile, writeFile
|
||||
from tools import C, buildTestProject, readFile, writeFile
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||
from novelwriter.core.project import NWProject
|
||||
@@ -31,14 +31,12 @@ from novelwriter.core.document import NWDoc
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
assert theProject.openProject(nwMinimal) is True
|
||||
assert theProject.projPath == nwMinimal
|
||||
|
||||
sHandle = "8c659a11cd429"
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
# Read Document
|
||||
# =============
|
||||
@@ -49,25 +47,23 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
|
||||
assert theDoc.readDocument() is None
|
||||
|
||||
# Non-existent handle
|
||||
theDoc = NWDoc(theProject, "0000000000000")
|
||||
theDoc = NWDoc(theProject, C.hInvalid)
|
||||
assert theDoc.readDocument() is None
|
||||
assert theDoc._currHash is None
|
||||
|
||||
# Cause open() to fail while loading
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeOSError)
|
||||
theDoc = NWDoc(theProject, sHandle)
|
||||
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||
assert theDoc.readDocument() is None
|
||||
assert theDoc.getError() == "OSError: Mock OSError"
|
||||
|
||||
# Load the text
|
||||
theDoc = NWDoc(theProject, sHandle)
|
||||
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||
assert theDoc.readDocument() == "### New Scene\n\n"
|
||||
|
||||
# Try to open a new (non-existent) file
|
||||
nHandle = theProject.tree.findRoot(nwItemClass.NOVEL)
|
||||
assert nHandle is not None
|
||||
xHandle = theProject.newFile("New File", nHandle)
|
||||
xHandle = theProject.newFile("New File", C.hNovelRoot)
|
||||
theDoc = NWDoc(theProject, xHandle)
|
||||
assert bool(theDoc) is True
|
||||
assert repr(theDoc) == f"<NWDoc handle={xHandle}>"
|
||||
@@ -86,10 +82,10 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
|
||||
assert theDoc.writeDocument(theText)
|
||||
|
||||
# Check file content
|
||||
docPath = os.path.join(nwMinimal, "content", xHandle+".nwd")
|
||||
docPath = os.path.join(fncDir, "content", xHandle+".nwd")
|
||||
assert readFile(docPath) == (
|
||||
"%%~name: New File\n"
|
||||
f"%%~path: a508bb932959c/{xHandle}\n"
|
||||
f"%%~path: {C.hNovelRoot}/{xHandle}\n"
|
||||
"%%~kind: NOVEL/DOCUMENT\n"
|
||||
"### Test File\n\n"
|
||||
"Text ...\n\n"
|
||||
@@ -153,16 +149,15 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_Methods(mockGUI, nwMinimal):
|
||||
def testCoreDocument_Methods(mockGUI, fncDir, mockRnd):
|
||||
"""Test other methods of the NWDoc class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
assert theProject.projPath == nwMinimal
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
sHandle = "8c659a11cd429"
|
||||
theDoc = NWDoc(theProject, sHandle)
|
||||
docPath = os.path.join(nwMinimal, "content", sHandle+".nwd")
|
||||
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||
docPath = os.path.join(fncDir, "content", C.hSceneDoc+".nwd")
|
||||
|
||||
assert theDoc.readDocument() == "### New Scene\n\n"
|
||||
|
||||
@@ -171,12 +166,12 @@ def testCoreDocument_Methods(mockGUI, nwMinimal):
|
||||
|
||||
# Check the item
|
||||
assert theDoc.getCurrentItem() is not None
|
||||
assert theDoc.getCurrentItem().itemHandle == sHandle
|
||||
assert theDoc.getCurrentItem().itemHandle == C.hSceneDoc
|
||||
|
||||
# Check the meta
|
||||
theName, theParent, theClass, theLayout = theDoc.getMeta()
|
||||
assert theName == "New Scene"
|
||||
assert theParent == "a6d311a93600a"
|
||||
assert theParent == C.hChapterDir
|
||||
assert theClass == nwItemClass.NOVEL
|
||||
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 readFile(docPath) == (
|
||||
"%%~name: New Scene\n"
|
||||
f"%%~path: a6d311a93600a/{sHandle}\n"
|
||||
f"%%~path: {C.hChapterDir}/{C.hSceneDoc}\n"
|
||||
"%%~kind: NOVEL/DOCUMENT\n"
|
||||
"%%~ stuff\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
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from PyQt5.QtGui import QIcon
|
||||
|
||||
from tools import C, buildTestProject
|
||||
|
||||
from novelwriter.core.item import NWItem
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_Setters(mockGUI, mockRnd):
|
||||
def testCoreItem_Setters(mockGUI, mockRnd, fncDir):
|
||||
"""Test all the simple setters for the NWItem class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
|
||||
@@ -153,7 +155,7 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
||||
theItem.setCharCount(None)
|
||||
assert theItem.charCount == 0
|
||||
theItem.setCharCount("1")
|
||||
assert theItem.charCount == 1
|
||||
assert theItem.charCount == 0
|
||||
theItem.setCharCount(1)
|
||||
assert theItem.charCount == 1
|
||||
|
||||
@@ -161,7 +163,7 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
||||
theItem.setWordCount(None)
|
||||
assert theItem.wordCount == 0
|
||||
theItem.setWordCount("1")
|
||||
assert theItem.wordCount == 1
|
||||
assert theItem.wordCount == 0
|
||||
theItem.setWordCount(1)
|
||||
assert theItem.wordCount == 1
|
||||
|
||||
@@ -169,7 +171,7 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
||||
theItem.setParaCount(None)
|
||||
assert theItem.paraCount == 0
|
||||
theItem.setParaCount("1")
|
||||
assert theItem.paraCount == 1
|
||||
assert theItem.paraCount == 0
|
||||
theItem.setParaCount(1)
|
||||
assert theItem.paraCount == 1
|
||||
|
||||
@@ -177,7 +179,7 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
||||
theItem.setCursorPos(None)
|
||||
assert theItem.cursorPos == 0
|
||||
theItem.setCursorPos("1")
|
||||
assert theItem.cursorPos == 1
|
||||
assert theItem.cursorPos == 0
|
||||
theItem.setCursorPos(1)
|
||||
assert theItem.cursorPos == 1
|
||||
|
||||
@@ -190,10 +192,12 @@ def testCoreItem_Setters(mockGUI, mockRnd):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_Methods(mockGUI):
|
||||
def testCoreItem_Methods(mockGUI, mockRnd, fncDir):
|
||||
"""Test the simple methods of the NWItem class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Describe Me
|
||||
@@ -250,15 +254,15 @@ def testCoreItem_Methods(mockGUI):
|
||||
# =============
|
||||
|
||||
theItem.setType("FILE")
|
||||
theItem.setStatus("Note")
|
||||
theItem.setImport("Minor")
|
||||
theItem.setStatus(C.sNote)
|
||||
theItem.setImport(C.iMinor)
|
||||
|
||||
theItem.setClass("NOVEL")
|
||||
stT, stI = theItem.getImportStatus()
|
||||
assert stT == "Note"
|
||||
assert isinstance(stI, QIcon)
|
||||
|
||||
theItem.setImportStatus("Draft")
|
||||
theItem.setImportStatus(C.sDraft)
|
||||
stT, stI = theItem.getImportStatus()
|
||||
assert stT == "Draft"
|
||||
|
||||
@@ -267,7 +271,7 @@ def testCoreItem_Methods(mockGUI):
|
||||
assert stT == "Minor"
|
||||
assert isinstance(stI, QIcon)
|
||||
|
||||
theItem.setImportStatus("Major")
|
||||
theItem.setImportStatus(C.iMajor)
|
||||
stT, stI = theItem.getImportStatus()
|
||||
assert stT == "Major"
|
||||
|
||||
@@ -491,254 +495,199 @@ def testCoreItem_ClassDefaults(mockGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_XMLPackUnpack(mockGUI, caplog, mockRnd):
|
||||
"""Test packing and unpacking XML objects for the NWItem class.
|
||||
def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||
"""Test packing and unpacking entries for the NWItem class.
|
||||
"""
|
||||
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"]
|
||||
importKeys = ["i000004", "i000005", "i000006", "i000007"]
|
||||
# Invalid
|
||||
theItem = NWItem(theProject)
|
||||
assert theItem.unpack({}) is False
|
||||
|
||||
# File
|
||||
# ====
|
||||
|
||||
theItem = NWItem(theProject)
|
||||
theItem.setHandle("0123456789abc")
|
||||
theItem.setParent("0123456789abc")
|
||||
theItem.setRoot("0123456789abc")
|
||||
theItem.setOrder(1)
|
||||
theItem.setName("A Name")
|
||||
theItem.setClass("NOVEL")
|
||||
theItem.setType("FILE")
|
||||
theItem.setImport(importKeys[3])
|
||||
theItem.setLayout("NOTE")
|
||||
theItem.setActive(False)
|
||||
theItem.setParaCount(3)
|
||||
theItem.setWordCount(5)
|
||||
theItem.setCharCount(7)
|
||||
theItem.setCursorPos(11)
|
||||
assert theItem.unpack({
|
||||
"label": "A File",
|
||||
"handle": "0000000000003",
|
||||
"parent": "0000000000002",
|
||||
"root": "0000000000001",
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT",
|
||||
"expanded": True,
|
||||
"status": None,
|
||||
"import": None,
|
||||
"heading": "H1",
|
||||
"charCount": 100,
|
||||
"wordCount": 20,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 50,
|
||||
"active": False,
|
||||
}) is True
|
||||
|
||||
# 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="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.itemName == "A File"
|
||||
assert theItem.itemHandle == "0000000000003"
|
||||
assert theItem.itemParent == "0000000000002"
|
||||
assert theItem.itemRoot == "0000000000001"
|
||||
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.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.isActive is True
|
||||
assert theItem.paraCount == 0
|
||||
assert theItem.wordCount == 0
|
||||
assert theItem.charCount == 0
|
||||
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.mainHeading == "H1"
|
||||
assert theItem.charCount == 100
|
||||
assert theItem.wordCount == 20
|
||||
assert theItem.paraCount == 2
|
||||
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.itemType == nwItemType.FILE
|
||||
assert theItem.itemLayout == nwItemLayout.DOCUMENT
|
||||
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
|
||||
|
||||
# Deprecated Type
|
||||
theItem.setType("TRASH")
|
||||
assert theItem.pack() == {
|
||||
"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.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 shutil
|
||||
import pytest
|
||||
|
||||
from lxml import etree
|
||||
from shutil import copyfile
|
||||
from zipfile import ZipFile
|
||||
|
||||
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE, C
|
||||
from mock import causeOSError
|
||||
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE, C
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
from novelwriter.common import formatTimeStamp
|
||||
@@ -37,6 +37,7 @@ from novelwriter.core.index import NWIndex
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.options import OptionState
|
||||
from novelwriter.core.document import NWDoc
|
||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
@@ -202,7 +203,7 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, mockGUI, tmpDir):
|
||||
|
||||
assert theProject.newProject(projData) 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.closeProject() is True
|
||||
os.unlink(dstSample)
|
||||
@@ -236,7 +237,7 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir):
|
||||
monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx")
|
||||
assert theProject.newProject(projData) 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.closeProject() is True
|
||||
|
||||
@@ -264,14 +265,14 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
assert theProject.closeProject() is True
|
||||
assert theProject.openProject(projFile) is True
|
||||
|
||||
assert theProject.newRoot(nwItemClass.NOVEL) == "0000000000020"
|
||||
assert theProject.newRoot(nwItemClass.PLOT) == "0000000000021"
|
||||
assert theProject.newRoot(nwItemClass.CHARACTER) == "0000000000022"
|
||||
assert theProject.newRoot(nwItemClass.WORLD) == "0000000000023"
|
||||
assert theProject.newRoot(nwItemClass.TIMELINE) == "0000000000024"
|
||||
assert theProject.newRoot(nwItemClass.OBJECT) == "0000000000025"
|
||||
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000026"
|
||||
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000027"
|
||||
assert theProject.newRoot(nwItemClass.NOVEL) == "0000000000010"
|
||||
assert theProject.newRoot(nwItemClass.PLOT) == "0000000000011"
|
||||
assert theProject.newRoot(nwItemClass.CHARACTER) == "0000000000012"
|
||||
assert theProject.newRoot(nwItemClass.WORLD) == "0000000000013"
|
||||
assert theProject.newRoot(nwItemClass.TIMELINE) == "0000000000014"
|
||||
assert theProject.newRoot(nwItemClass.OBJECT) == "0000000000015"
|
||||
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000016"
|
||||
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000017"
|
||||
|
||||
assert theProject.projChanged is True
|
||||
assert theProject.saveProject() is True
|
||||
@@ -282,23 +283,23 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
assert theProject.projChanged is False
|
||||
|
||||
# Delete the new items
|
||||
assert theProject.removeItem("0000000000020") is True
|
||||
assert theProject.removeItem("0000000000021") is True
|
||||
assert theProject.removeItem("0000000000022") is True
|
||||
assert theProject.removeItem("0000000000023") is True
|
||||
assert theProject.removeItem("0000000000024") is True
|
||||
assert theProject.removeItem("0000000000025") is True
|
||||
assert theProject.removeItem("0000000000026") is True
|
||||
assert theProject.removeItem("0000000000027") is True
|
||||
assert theProject.removeItem("0000000000010") is True
|
||||
assert theProject.removeItem("0000000000011") is True
|
||||
assert theProject.removeItem("0000000000012") is True
|
||||
assert theProject.removeItem("0000000000013") is True
|
||||
assert theProject.removeItem("0000000000014") is True
|
||||
assert theProject.removeItem("0000000000015") is True
|
||||
assert theProject.removeItem("0000000000016") is True
|
||||
assert theProject.removeItem("0000000000017") is True
|
||||
|
||||
assert "0000000000020" not in theProject.tree
|
||||
assert "0000000000021" not in theProject.tree
|
||||
assert "0000000000022" not in theProject.tree
|
||||
assert "0000000000023" not in theProject.tree
|
||||
assert "0000000000024" not in theProject.tree
|
||||
assert "0000000000025" not in theProject.tree
|
||||
assert "0000000000026" not in theProject.tree
|
||||
assert "0000000000027" not in theProject.tree
|
||||
assert "0000000000010" not in theProject.tree
|
||||
assert "0000000000011" not in theProject.tree
|
||||
assert "0000000000012" not in theProject.tree
|
||||
assert "0000000000013" not in theProject.tree
|
||||
assert "0000000000014" not in theProject.tree
|
||||
assert "0000000000015" not in theProject.tree
|
||||
assert "0000000000016" not in theProject.tree
|
||||
assert "0000000000017" not in theProject.tree
|
||||
|
||||
# END Test testCoreProject_NewRoot
|
||||
|
||||
@@ -325,26 +326,26 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
|
||||
assert theProject.newFile("New File", "1234567890abc") is None
|
||||
|
||||
# Add files properly
|
||||
assert theProject.newFolder("Stuff", C.hNovelRoot) == "0000000000020"
|
||||
assert theProject.newFile("Hello", "0000000000020") == "0000000000021"
|
||||
assert theProject.newFile("Jane", C.hCharRoot) == "0000000000022"
|
||||
assert theProject.newFolder("Stuff", C.hNovelRoot) == "0000000000010"
|
||||
assert theProject.newFile("Hello", "0000000000010") == "0000000000011"
|
||||
assert theProject.newFile("Jane", C.hCharRoot) == "0000000000012"
|
||||
|
||||
assert "0000000000020" in theProject.tree
|
||||
assert "0000000000021" in theProject.tree
|
||||
assert "0000000000022" in theProject.tree
|
||||
assert "0000000000010" in theProject.tree
|
||||
assert "0000000000011" in theProject.tree
|
||||
assert "0000000000012" in theProject.tree
|
||||
|
||||
# Write to file, failed
|
||||
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
|
||||
|
||||
# Write to file, success
|
||||
assert theProject.writeNewFile("0000000000021", 2, True) is True
|
||||
assert NWDoc(theProject, "0000000000021").readDocument() == "## Hello\n\n"
|
||||
assert theProject.writeNewFile("0000000000011", 2, True) is True
|
||||
assert NWDoc(theProject, "0000000000011").readDocument() == "## Hello\n\n"
|
||||
|
||||
# Write to file with additional text, success
|
||||
assert theProject.writeNewFile("0000000000022", 1, False, "Hi Jane\n\n") is True
|
||||
assert NWDoc(theProject, "0000000000022").readDocument() == "# Jane\n\nHi Jane\n\n"
|
||||
assert theProject.writeNewFile("0000000000012", 1, False, "Hi Jane\n\n") is True
|
||||
assert NWDoc(theProject, "0000000000012").readDocument() == "# Jane\n\nHi Jane\n\n"
|
||||
|
||||
# Save, close and check
|
||||
assert theProject.projChanged is True
|
||||
@@ -357,23 +358,23 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
|
||||
# Delete new file, but block access
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("os.unlink", causeOSError)
|
||||
assert theProject.removeItem("0000000000021") is False
|
||||
assert "0000000000021" in theProject.tree
|
||||
assert theProject.removeItem("0000000000011") is False
|
||||
assert "0000000000011" in theProject.tree
|
||||
|
||||
# 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", "0000000000021.nwd"))
|
||||
assert os.path.isfile(os.path.join(fncDir, "content", "0000000000012.nwd"))
|
||||
assert os.path.isfile(os.path.join(fncDir, "content", "0000000000011.nwd"))
|
||||
|
||||
assert theProject.removeItem("0000000000022") is True
|
||||
assert theProject.removeItem("0000000000021") is True
|
||||
assert theProject.removeItem("0000000000020") is True
|
||||
assert theProject.removeItem("0000000000012") is True
|
||||
assert theProject.removeItem("0000000000011") 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", "0000000000021.nwd"))
|
||||
assert not os.path.isfile(os.path.join(fncDir, "content", "0000000000012.nwd"))
|
||||
assert not os.path.isfile(os.path.join(fncDir, "content", "0000000000011.nwd"))
|
||||
|
||||
assert "0000000000020" not in theProject.tree
|
||||
assert "0000000000021" not in theProject.tree
|
||||
assert "0000000000022" not in theProject.tree
|
||||
assert "0000000000010" not in theProject.tree
|
||||
assert "0000000000011" not in theProject.tree
|
||||
assert "0000000000012" not in theProject.tree
|
||||
|
||||
assert theProject.closeProject() is True
|
||||
|
||||
@@ -381,139 +382,90 @@ def testCoreProject_NewFileFolder(monkeypatch, fncDir, outDir, refDir, mockGUI,
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
|
||||
def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncDir, mockRnd):
|
||||
"""Test opening a project.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
# Rename the project file to check handling
|
||||
rName = os.path.join(nwMinimal, nwFiles.PROJ_FILE)
|
||||
wName = os.path.join(nwMinimal, nwFiles.PROJ_FILE+"_sdfghj")
|
||||
rName = os.path.join(fncDir, nwFiles.PROJ_FILE)
|
||||
wName = os.path.join(fncDir, nwFiles.PROJ_FILE+"_sdfghj")
|
||||
os.rename(rName, wName)
|
||||
assert theProject.openProject(nwMinimal) is False
|
||||
assert theProject.openProject(fncDir) is False
|
||||
os.rename(wName, rName)
|
||||
|
||||
# Fail on folder structure check
|
||||
with monkeypatch.context() as mp:
|
||||
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
|
||||
theProject.setProjectPath(nwMinimal)
|
||||
theProject.setProjectPath(fncDir)
|
||||
assert theProject._writeLockFile()
|
||||
assert theProject.openProject(nwMinimal) is False
|
||||
assert theProject.openProject(fncDir) is False
|
||||
|
||||
# Fail to read lockfile (which still opens the project)
|
||||
with monkeypatch.context() as mp:
|
||||
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()
|
||||
|
||||
# Force open with lockfile
|
||||
theProject.setProjectPath(nwMinimal)
|
||||
theProject.setProjectPath(fncDir)
|
||||
assert theProject._writeLockFile()
|
||||
assert theProject.openProject(nwMinimal, overrideLock=True) is True
|
||||
assert theProject.openProject(fncDir, overrideLock=True) is True
|
||||
assert theProject.closeProject()
|
||||
|
||||
# Make a junk XML file
|
||||
oName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"orig")
|
||||
bName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"bak")
|
||||
os.rename(rName, oName)
|
||||
writeFile(rName, "stuff")
|
||||
assert theProject.openProject(nwMinimal) is False
|
||||
# Not a novelwriter XML file
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ProjectXMLReader, "read", lambda *a: False)
|
||||
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.NOT_NWX_FILE))
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert "Project file does not appear" in mockGUI.lastAlert
|
||||
|
||||
# Also write a jun XML backup file
|
||||
writeFile(bName, "stuff")
|
||||
assert theProject.openProject(nwMinimal) is False
|
||||
# Unknown project file version
|
||||
with monkeypatch.context() as mp:
|
||||
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
|
||||
writeFile(rName, "<not_novelWriterXML></not_novelWriterXML>\n")
|
||||
assert theProject.openProject(nwMinimal) is False
|
||||
# Other parse error
|
||||
with monkeypatch.context() as mp:
|
||||
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
|
||||
writeFile(rName, (
|
||||
"<?xml version='0.0' encoding='utf-8'?>\n"
|
||||
"<novelWriterXML "
|
||||
"appVersion=\"1.0\" "
|
||||
"hexVersion=\"0x01000000\" "
|
||||
"fileVersion=\"1.0\" "
|
||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
||||
"</novelWriterXML>\n"
|
||||
))
|
||||
mockGUI.askResponse = False
|
||||
assert theProject.openProject(nwMinimal) is False
|
||||
mockGUI.undo()
|
||||
# Won't convert legacy file
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY))
|
||||
mockGUI.askResponse = False
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert "The file format of your project is about to be" in mockGUI.lastQuestion[1]
|
||||
mockGUI.askResponse = True
|
||||
|
||||
# Future file version
|
||||
writeFile(rName, (
|
||||
"<?xml version='1.0' encoding='utf-8'?>\n"
|
||||
"<novelWriterXML "
|
||||
"appVersion=\"1.0\" "
|
||||
"hexVersion=\"0x01000000\" "
|
||||
"fileVersion=\"99.99\" "
|
||||
"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)
|
||||
# Won't convert legacy file
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ProjectXMLReader, "hexVersion", property(lambda *a: "0x99999999"))
|
||||
mockGUI.askResponse = False
|
||||
assert theProject.openProject(fncDir) is False
|
||||
assert "This project was saved by a newer version" in mockGUI.lastQuestion[1]
|
||||
mockGUI.askResponse = True
|
||||
|
||||
# Add some legacy stuff that cannot be removed
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(theProject, "_legacyDataFolder", causeOSError)
|
||||
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
||||
writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.nwd"), "stuff")
|
||||
writeFile(os.path.join(nwMinimal, "data_0", "123456789abc_main.bak"), "stuff")
|
||||
os.mkdir(os.path.join(fncDir, "data_0"))
|
||||
writeFile(os.path.join(fncDir, "data_0", "123456789abc_main.nwd"), "stuff")
|
||||
writeFile(os.path.join(fncDir, "data_0", "123456789abc_main.bak"), "stuff")
|
||||
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 theProject.closeProject()
|
||||
@@ -522,56 +474,31 @@ def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
|
||||
def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncDir, refDir):
|
||||
"""Test saving a project.
|
||||
"""
|
||||
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
|
||||
assert theProject.saveProject() is False
|
||||
|
||||
# Open test project
|
||||
assert theProject.openProject(nwMinimal)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
# Fail on folder structure check
|
||||
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
|
||||
|
||||
# Fail on open file
|
||||
# Fail writing
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeOSError)
|
||||
mp.setattr(ProjectXMLWriter, "write", lambda *a: False)
|
||||
assert theProject.saveProject() is False
|
||||
|
||||
# Fail on creating .bak file
|
||||
with monkeypatch.context() as mp:
|
||||
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
|
||||
# Save with and without autosave
|
||||
assert theProject.saveProject(autoSave=False) 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()
|
||||
|
||||
# END Test testCoreProject_Save
|
||||
@@ -682,11 +609,11 @@ def testCoreProject_Helpers(monkeypatch, fncDir, mockGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
||||
def testCoreProject_AccessItems(mockGUI, fncDir, mockRnd):
|
||||
"""Test helper functions for the project folder.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.openProject(nwMinimal)
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
# Storage Objects
|
||||
assert isinstance(theProject.index, NWIndex)
|
||||
@@ -695,34 +622,34 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
||||
|
||||
# Move Novel ROOT to after its files
|
||||
oldOrder = [
|
||||
"a508bb932959c", # ROOT: Novel
|
||||
"a35baf2e93843", # FILE: Title Page
|
||||
"a6d311a93600a", # FOLDER: New Chapter
|
||||
"f5ab3e30151e1", # FILE: New Chapter
|
||||
"8c659a11cd429", # FILE: New Scene
|
||||
"7695ce551d265", # ROOT: Plot
|
||||
"afb3043c7b2b3", # ROOT: Characters
|
||||
"9d5247ab588e0", # ROOT: World
|
||||
C.hNovelRoot,
|
||||
C.hPlotRoot,
|
||||
C.hCharRoot,
|
||||
C.hWorldRoot,
|
||||
C.hTitlePage,
|
||||
C.hChapterDir,
|
||||
C.hChapterDoc,
|
||||
C.hSceneDoc,
|
||||
]
|
||||
newOrder = [
|
||||
"a35baf2e93843", # FILE: Title Page
|
||||
"f5ab3e30151e1", # FILE: New Chapter
|
||||
"8c659a11cd429", # FILE: New Scene
|
||||
"a6d311a93600a", # FOLDER: New Chapter
|
||||
"a508bb932959c", # ROOT: Novel
|
||||
"7695ce551d265", # ROOT: Plot
|
||||
"afb3043c7b2b3", # ROOT: Characters
|
||||
"9d5247ab588e0", # ROOT: World
|
||||
C.hTitlePage,
|
||||
C.hChapterDoc,
|
||||
C.hSceneDoc,
|
||||
C.hChapterDir,
|
||||
C.hNovelRoot,
|
||||
C.hPlotRoot,
|
||||
C.hCharRoot,
|
||||
C.hWorldRoot,
|
||||
]
|
||||
assert theProject.tree.handles() == oldOrder
|
||||
assert theProject.setTreeOrder(newOrder)
|
||||
assert theProject.tree.handles() == newOrder
|
||||
|
||||
# Add a non-existing item
|
||||
theProject.tree._treeOrder.append("01234567789abc")
|
||||
theProject.tree._treeOrder.append(C.hInvalid)
|
||||
|
||||
# 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")
|
||||
assert theProject.tree[nHandle].itemParent == "cba9876543210"
|
||||
|
||||
@@ -731,15 +658,15 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
||||
retOrder.append(tItem.itemHandle)
|
||||
|
||||
assert retOrder == [
|
||||
"a508bb932959c", # ROOT: Novel
|
||||
"7695ce551d265", # ROOT: Plot
|
||||
"afb3043c7b2b3", # ROOT: Characters
|
||||
"9d5247ab588e0", # ROOT: World
|
||||
nHandle, # FILE: Test File
|
||||
"a35baf2e93843", # FILE: Title Page
|
||||
"a6d311a93600a", # FOLDER: New Chapter
|
||||
"f5ab3e30151e1", # FILE: New Chapter
|
||||
"8c659a11cd429", # FILE: New Scene
|
||||
C.hNovelRoot,
|
||||
C.hPlotRoot,
|
||||
C.hCharRoot,
|
||||
C.hWorldRoot,
|
||||
nHandle,
|
||||
C.hTitlePage,
|
||||
C.hChapterDir,
|
||||
C.hChapterDoc,
|
||||
C.hSceneDoc,
|
||||
]
|
||||
assert theProject.tree[nHandle].itemParent is None
|
||||
|
||||
@@ -751,23 +678,24 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
||||
"""Test the status and importance flag handling.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
statusKeys = ["s000008", "s000009", "s00000a", "s00000b"]
|
||||
importKeys = ["i00000c", "i00000d", "i00000e", "i00000f"]
|
||||
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
|
||||
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
|
||||
|
||||
# Change Status
|
||||
# =============
|
||||
|
||||
theProject.tree["0000000000014"].setStatus("Finished")
|
||||
theProject.tree["0000000000015"].setStatus("Draft")
|
||||
theProject.tree["0000000000016"].setStatus("Note")
|
||||
theProject.tree["0000000000017"].setStatus("Finished")
|
||||
theProject.tree[C.hNovelRoot].setStatus(statusKeys[3])
|
||||
theProject.tree[C.hPlotRoot].setStatus(statusKeys[2])
|
||||
theProject.tree[C.hCharRoot].setStatus(statusKeys[1])
|
||||
theProject.tree[C.hWorldRoot].setStatus(statusKeys[3])
|
||||
|
||||
assert theProject.tree["0000000000014"].itemStatus == statusKeys[3]
|
||||
assert theProject.tree["0000000000015"].itemStatus == statusKeys[2]
|
||||
assert theProject.tree["0000000000016"].itemStatus == statusKeys[1]
|
||||
assert theProject.tree["0000000000017"].itemStatus == statusKeys[3]
|
||||
assert theProject.tree[C.hNovelRoot].itemStatus == statusKeys[3]
|
||||
assert theProject.tree[C.hPlotRoot].itemStatus == statusKeys[2]
|
||||
assert theProject.tree[C.hCharRoot].itemStatus == statusKeys[1]
|
||||
assert theProject.tree[C.hWorldRoot].itemStatus == statusKeys[3]
|
||||
|
||||
newList = [
|
||||
{"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(newList, []) is True
|
||||
|
||||
assert theProject.statusItems.name(statusKeys[0]) == "New"
|
||||
assert theProject.statusItems.name(statusKeys[1]) == "Draft"
|
||||
assert theProject.statusItems.name(statusKeys[2]) == "Note"
|
||||
assert theProject.statusItems.name(statusKeys[3]) == "Edited"
|
||||
assert theProject.statusItems.cols(statusKeys[0]) == (1, 1, 1)
|
||||
assert theProject.statusItems.cols(statusKeys[1]) == (2, 2, 2)
|
||||
assert theProject.statusItems.cols(statusKeys[2]) == (3, 3, 3)
|
||||
assert theProject.statusItems.cols(statusKeys[3]) == (4, 4, 4)
|
||||
assert theProject.data.itemStatus.name(statusKeys[0]) == "New"
|
||||
assert theProject.data.itemStatus.name(statusKeys[1]) == "Draft"
|
||||
assert theProject.data.itemStatus.name(statusKeys[2]) == "Note"
|
||||
assert theProject.data.itemStatus.name(statusKeys[3]) == "Edited"
|
||||
assert theProject.data.itemStatus.cols(statusKeys[0]) == (1, 1, 1)
|
||||
assert theProject.data.itemStatus.cols(statusKeys[1]) == (2, 2, 2)
|
||||
assert theProject.data.itemStatus.cols(statusKeys[2]) == (3, 3, 3)
|
||||
assert theProject.data.itemStatus.cols(statusKeys[3]) == (4, 4, 4)
|
||||
|
||||
# Check the new entry
|
||||
lastKey = theProject.statusItems.check("Finished")
|
||||
assert lastKey == "s000018"
|
||||
assert theProject.statusItems.name(lastKey) == "Finished"
|
||||
assert theProject.statusItems.cols(lastKey) == (5, 5, 5)
|
||||
lastKey = theProject.data.itemStatus.check("s000010")
|
||||
assert lastKey == "s000010"
|
||||
assert theProject.data.itemStatus.name(lastKey) == "Finished"
|
||||
assert theProject.data.itemStatus.cols(lastKey) == (5, 5, 5)
|
||||
|
||||
# Delete last entry
|
||||
assert theProject.setStatusColours([], [lastKey]) is True
|
||||
assert theProject.statusItems.name(lastKey) == "New"
|
||||
assert theProject.data.itemStatus.name(lastKey) == "New"
|
||||
|
||||
# Change Importance
|
||||
# =================
|
||||
|
||||
fHandle = theProject.newFile("Jane Doe", "0000000000012")
|
||||
theProject.tree[fHandle].setImport("Main")
|
||||
fHandle = theProject.newFile("Jane Doe", C.hCharRoot)
|
||||
theProject.tree[fHandle].setImport(importKeys[3])
|
||||
|
||||
assert theProject.tree[fHandle].itemImport == importKeys[3]
|
||||
newList = [
|
||||
@@ -817,53 +745,41 @@ def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
|
||||
assert theProject.setImportColours([], []) is False
|
||||
assert theProject.setImportColours(newList, []) is True
|
||||
|
||||
assert theProject.importItems.name(importKeys[0]) == "New"
|
||||
assert theProject.importItems.name(importKeys[1]) == "Minor"
|
||||
assert theProject.importItems.name(importKeys[2]) == "Major"
|
||||
assert theProject.importItems.name(importKeys[3]) == "Min"
|
||||
assert theProject.importItems.cols(importKeys[0]) == (1, 1, 1)
|
||||
assert theProject.importItems.cols(importKeys[1]) == (2, 2, 2)
|
||||
assert theProject.importItems.cols(importKeys[2]) == (3, 3, 3)
|
||||
assert theProject.importItems.cols(importKeys[3]) == (4, 4, 4)
|
||||
assert theProject.data.itemImport.name(importKeys[0]) == "New"
|
||||
assert theProject.data.itemImport.name(importKeys[1]) == "Minor"
|
||||
assert theProject.data.itemImport.name(importKeys[2]) == "Major"
|
||||
assert theProject.data.itemImport.name(importKeys[3]) == "Min"
|
||||
assert theProject.data.itemImport.cols(importKeys[0]) == (1, 1, 1)
|
||||
assert theProject.data.itemImport.cols(importKeys[1]) == (2, 2, 2)
|
||||
assert theProject.data.itemImport.cols(importKeys[2]) == (3, 3, 3)
|
||||
assert theProject.data.itemImport.cols(importKeys[3]) == (4, 4, 4)
|
||||
|
||||
# Check the new entry
|
||||
lastKey = theProject.importItems.check("Max")
|
||||
assert lastKey == "i00001a"
|
||||
assert theProject.importItems.name(lastKey) == "Max"
|
||||
assert theProject.importItems.cols(lastKey) == (5, 5, 5)
|
||||
lastKey = theProject.data.itemImport.check("i000012")
|
||||
assert lastKey == "i000012"
|
||||
assert theProject.data.itemImport.name(lastKey) == "Max"
|
||||
assert theProject.data.itemImport.cols(lastKey) == (5, 5, 5)
|
||||
|
||||
# Delete last entry
|
||||
assert theProject.setImportColours([], [lastKey]) is True
|
||||
assert theProject.importItems.name(lastKey) == "New"
|
||||
assert theProject.data.itemImport.name(lastKey) == "New"
|
||||
|
||||
# Delete Status/Import
|
||||
# ====================
|
||||
|
||||
theProject.statusItems.resetCounts()
|
||||
for key in list(theProject.statusItems.keys()):
|
||||
assert theProject.statusItems.remove(key) is True
|
||||
theProject.data.itemStatus.resetCounts()
|
||||
for key in list(theProject.data.itemStatus.keys()):
|
||||
assert theProject.data.itemStatus.remove(key) is True
|
||||
|
||||
theProject.importItems.resetCounts()
|
||||
for key in list(theProject.importItems.keys()):
|
||||
assert theProject.importItems.remove(key) is True
|
||||
theProject.data.itemImport.resetCounts()
|
||||
for key in list(theProject.data.itemImport.keys()):
|
||||
assert theProject.data.itemImport.remove(key) is True
|
||||
|
||||
assert len(theProject.statusItems) == 0
|
||||
assert len(theProject.importItems) == 0
|
||||
assert len(theProject.data.itemStatus) == 0
|
||||
assert len(theProject.data.itemImport) == 0
|
||||
assert theProject.saveProject() 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
|
||||
|
||||
|
||||
@@ -895,117 +811,104 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
|
||||
assert theProject.setProjectPath(fncDir)
|
||||
|
||||
# Project Name
|
||||
assert theProject.setProjectName(" A Name ")
|
||||
assert theProject.projName == "A Name"
|
||||
theProject.data.setName(" A Name ")
|
||||
assert theProject.data.name == "A Name"
|
||||
|
||||
# Project Title
|
||||
assert theProject.setBookTitle(" A Title ")
|
||||
assert theProject.bookTitle == "A Title"
|
||||
theProject.data.setTitle(" A Title ")
|
||||
assert theProject.data.title == "A Title"
|
||||
|
||||
# Project Authors
|
||||
# Check that the list is cleaned up and that it can be extracted as
|
||||
# a properly formatted string, depending on number of names
|
||||
assert not theProject.setBookAuthors([])
|
||||
assert theProject.setBookAuthors(" Jane Doe \n John Doh \n ")
|
||||
assert theProject.bookAuthors == ["Jane Doe", "John Doh"]
|
||||
theProject.data.setAuthors([])
|
||||
assert theProject.data.authors == []
|
||||
theProject.data.setAuthors(" Jane Doe \n John Doh \n ")
|
||||
assert theProject.data.authors == ["Jane Doe", "John Doh"]
|
||||
|
||||
assert theProject.setBookAuthors("")
|
||||
assert theProject.getAuthors() == ""
|
||||
theProject.data.setAuthors("")
|
||||
assert theProject.getFormattedAuthors() == ""
|
||||
|
||||
assert theProject.setBookAuthors("Jane Doe")
|
||||
assert theProject.getAuthors() == "Jane Doe"
|
||||
theProject.data.setAuthors("Jane Doe")
|
||||
assert theProject.getFormattedAuthors() == "Jane Doe"
|
||||
|
||||
assert theProject.setBookAuthors("Jane Doe\nJohn Doh")
|
||||
assert theProject.getAuthors() == "Jane Doe and John Doh"
|
||||
theProject.data.setAuthors("Jane Doe\nJohn Doh")
|
||||
assert theProject.getFormattedAuthors() == "Jane Doe and John Doh"
|
||||
|
||||
assert theProject.setBookAuthors("Jane Doe\nJohn Doh\nBod Owens")
|
||||
assert theProject.getAuthors() == "Jane Doe, John Doh and Bod Owens"
|
||||
theProject.data.setAuthors("Jane Doe\nJohn Doh\nBod Owens")
|
||||
assert theProject.getFormattedAuthors() == "Jane Doe, John Doh and Bod Owens"
|
||||
|
||||
# Edit Time
|
||||
theProject.editTime = 1234
|
||||
theProject.projOpened = 1600000000
|
||||
theProject.data.setEditTime(1234)
|
||||
theProject._projOpened = 1600000000
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.project.time", lambda: 1600005600)
|
||||
assert theProject.getCurrentEditTime() == 6834
|
||||
|
||||
# Trash folder
|
||||
# Should create on first call, and just returned on later calls
|
||||
hTrash = "0000000000018"
|
||||
hTrash = "0000000000010"
|
||||
assert theProject.tree[hTrash] is None
|
||||
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
|
||||
theProject.projChanged = False
|
||||
assert theProject.setSpellCheck(True)
|
||||
assert not theProject.setSpellCheck(False)
|
||||
theProject.setProjectChanged(False)
|
||||
theProject.data.setSpellCheck(True)
|
||||
theProject.data.setSpellCheck(False)
|
||||
assert theProject.projChanged
|
||||
|
||||
# Spell language
|
||||
theProject.projChanged = False
|
||||
assert theProject.projSpell is None
|
||||
assert theProject.setSpellLang(None) is False
|
||||
assert theProject.projSpell is None
|
||||
assert theProject.setSpellLang("None") is False # Should be interpreded as None
|
||||
assert theProject.projSpell is None
|
||||
assert theProject.setSpellLang("en_GB")
|
||||
assert theProject.projSpell == "en_GB"
|
||||
theProject.setProjectChanged(False)
|
||||
assert theProject.data.spellLang is None
|
||||
theProject.data.setSpellLang(None)
|
||||
assert theProject.data.spellLang is None
|
||||
theProject.data.setSpellLang("None") # Should be interpreded as None
|
||||
assert theProject.data.spellLang is None
|
||||
theProject.data.setSpellLang("en_GB")
|
||||
assert theProject.data.spellLang == "en_GB"
|
||||
assert theProject.projChanged
|
||||
|
||||
# Project Language
|
||||
theProject.projChanged = False
|
||||
theProject.projLang = "en"
|
||||
theProject.setProjectChanged(False)
|
||||
theProject.data.setLanguage("en")
|
||||
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.projLang == "en_GB"
|
||||
assert theProject.data.language == "en_GB"
|
||||
|
||||
# Language Lookup
|
||||
assert theProject.localLookup(1) == "One"
|
||||
assert theProject.localLookup(10) == "Ten"
|
||||
|
||||
# Last edited
|
||||
theProject.projChanged = False
|
||||
assert theProject.setLastEdited("0123456789abc")
|
||||
assert theProject.lastEdited == "0123456789abc"
|
||||
theProject.setProjectChanged(False)
|
||||
theProject._data.setLastHandle("0123456789abc", "editor")
|
||||
assert theProject._data.getLastHandle("editor") == "0123456789abc"
|
||||
assert theProject.projChanged
|
||||
|
||||
# Last viewed
|
||||
theProject.projChanged = False
|
||||
assert theProject.setLastViewed("0123456789abc")
|
||||
assert theProject.lastViewed == "0123456789abc"
|
||||
theProject.setProjectChanged(False)
|
||||
theProject._data.setLastHandle("0123456789abc", "viewer")
|
||||
assert theProject._data.getLastHandle("viewer") == "0123456789abc"
|
||||
assert theProject.projChanged
|
||||
|
||||
# Autoreplace
|
||||
theProject.projChanged = False
|
||||
assert theProject.setAutoReplace({"A": "B", "C": "D"})
|
||||
assert theProject.autoReplace == {"A": "B", "C": "D"}
|
||||
theProject.setProjectChanged(False)
|
||||
theProject.data.setAutoReplace({"A": "B", "C": "D"})
|
||||
assert theProject.data.autoReplace == {"A": "B", "C": "D"}
|
||||
assert theProject.projChanged
|
||||
|
||||
# Change project tree order
|
||||
oldOrder = [
|
||||
"0000000000010", "0000000000011", "0000000000012",
|
||||
"0000000000013", "0000000000014", "0000000000015",
|
||||
"0000000000016", "0000000000017", "0000000000018",
|
||||
"0000000000008", "0000000000009", "000000000000a",
|
||||
"000000000000b", "000000000000c", "000000000000d",
|
||||
"000000000000e", "000000000000f", "0000000000010",
|
||||
]
|
||||
newOrder = [
|
||||
"0000000000013", "0000000000014", "0000000000015",
|
||||
"0000000000010", "0000000000011", "0000000000012",
|
||||
"0000000000016", "0000000000017",
|
||||
"000000000000b", "000000000000c", "000000000000d",
|
||||
"0000000000008", "0000000000009", "000000000000a",
|
||||
"000000000000e", "000000000000f",
|
||||
]
|
||||
assert theProject.tree.handles() == oldOrder
|
||||
assert theProject.setTreeOrder(newOrder)
|
||||
@@ -1014,8 +917,8 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
|
||||
assert theProject.tree.handles() == oldOrder
|
||||
|
||||
# Session stats
|
||||
theProject.currWCount = 200
|
||||
theProject.lastWCount = 100
|
||||
theProject._data._initCounts = [50, 50]
|
||||
theProject._data._currCounts = [100, 100]
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("os.path.isdir", lambda *a, **k: False)
|
||||
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")
|
||||
statsFile = os.path.join(theProject.projMeta, nwFiles.SESS_STATS)
|
||||
|
||||
theProject.projOpened = 1600002000
|
||||
theProject.currNovelWC = 200
|
||||
theProject.currNotesWC = 100
|
||||
theProject._projOpened = 1600002000
|
||||
theProject._data._currCounts = [200, 100]
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
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"
|
||||
) % (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
|
||||
|
||||
|
||||
@@ -1305,14 +1182,14 @@ def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
|
||||
|
||||
|
||||
@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
|
||||
creates a backup of the Minimal test project, and then unzips the
|
||||
backupd file and checks that the project XML file is identical to
|
||||
the original file.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
# Test faulty settings
|
||||
|
||||
@@ -1327,20 +1204,20 @@ def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
|
||||
|
||||
# Missing project name
|
||||
theProject.mainConf.backupPath = tmpDir
|
||||
theProject.projName = ""
|
||||
theProject.data.setName("")
|
||||
assert theProject.zipIt(doNotify=False) is False
|
||||
|
||||
# Non-existent folder
|
||||
theProject.mainConf.backupPath = os.path.join(tmpDir, "nonexistent")
|
||||
theProject.projName = "Test Minimal"
|
||||
theProject.data.setName("Test Minimal")
|
||||
assert theProject.zipIt(doNotify=False) is False
|
||||
|
||||
# Same folder as project (causes infinite loop in zipping)
|
||||
theProject.mainConf.backupPath = nwMinimal
|
||||
theProject.mainConf.backupPath = fncDir
|
||||
assert theProject.zipIt(doNotify=False) is False
|
||||
|
||||
# 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
|
||||
|
||||
# Set a valid folder
|
||||
@@ -1372,7 +1249,7 @@ def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
|
||||
|
||||
# Check that the main project file was restored
|
||||
assert cmpFiles(
|
||||
os.path.join(nwMinimal, "nwProject.nwx"),
|
||||
os.path.join(fncDir, "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 random
|
||||
|
||||
from lxml import etree
|
||||
from tools import C
|
||||
|
||||
from PyQt5.QtGui import QIcon
|
||||
|
||||
from novelwriter.core.status import NWStatus
|
||||
|
||||
statusKeys = ["sa3b179", "s1c8031", "s06671a", "sbdd640"]
|
||||
importKeys = ["i466852", "i3eb13b", "i392456", "i23b8c1"]
|
||||
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
|
||||
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Internal():
|
||||
def testCoreStatus_Internal(mockRnd):
|
||||
"""Test all the internal functions of the NWStatus class.
|
||||
"""
|
||||
random.seed(42)
|
||||
theStatus = NWStatus(NWStatus.STATUS)
|
||||
theImport = NWStatus(NWStatus.IMPORT)
|
||||
|
||||
@@ -87,10 +85,9 @@ def testCoreStatus_Internal():
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Iterator():
|
||||
def testCoreStatus_Iterator(mockRnd):
|
||||
"""Test the iterator functions of the NWStatus class.
|
||||
"""
|
||||
random.seed(42)
|
||||
theStatus = NWStatus(NWStatus.STATUS)
|
||||
|
||||
theStatus.write(None, "New", (100, 100, 100))
|
||||
@@ -132,10 +129,9 @@ def testCoreStatus_Iterator():
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Entries():
|
||||
def testCoreStatus_Entries(mockRnd):
|
||||
"""Test all the simple setters for the NWStatus class.
|
||||
"""
|
||||
random.seed(42)
|
||||
theStatus = NWStatus(NWStatus.STATUS)
|
||||
|
||||
# Write
|
||||
@@ -161,14 +157,6 @@ def testCoreStatus_Entries():
|
||||
assert theStatus[statusKeys[3]]["name"] == "Entry 4"
|
||||
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
|
||||
# =====
|
||||
|
||||
@@ -176,14 +164,8 @@ def testCoreStatus_Entries():
|
||||
for key in statusKeys:
|
||||
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
|
||||
assert theStatus.check("Entry 5") == statusKeys[0]
|
||||
assert theStatus.check("s987654") == statusKeys[0]
|
||||
|
||||
# Name Access
|
||||
# ===========
|
||||
@@ -314,10 +296,9 @@ def testCoreStatus_Entries():
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_XMLPackUnpack():
|
||||
"""Test all the XML pack/unpack of the NWStatus class.
|
||||
def testCoreStatus_PackUnpack(mockRnd):
|
||||
"""Test all the pack/unpack of the NWStatus class.
|
||||
"""
|
||||
random.seed(42)
|
||||
theStatus = NWStatus(NWStatus.STATUS)
|
||||
theStatus.write(None, "New", (100, 100, 100))
|
||||
theStatus.write(None, "Note", (200, 50, 0))
|
||||
@@ -329,36 +310,59 @@ def testCoreStatus_XMLPackUnpack():
|
||||
for _ in range(n):
|
||||
theStatus.increment(statusKeys[i])
|
||||
|
||||
nwXML = etree.Element("novelWriterXML")
|
||||
|
||||
# Pack
|
||||
xStatus = etree.SubElement(nwXML, "status")
|
||||
theStatus.packXML(xStatus)
|
||||
assert etree.tostring(xStatus, pretty_print=False, encoding="utf-8") == (
|
||||
b'<status>'
|
||||
b'<entry key="sa3b179" count="3" red="100" green="100" blue="100">New</entry>'
|
||||
b'<entry key="s1c8031" count="5" red="200" green="50" blue="0">Note</entry>'
|
||||
b'<entry key="s06671a" count="7" red="200" green="150" blue="0">Draft</entry>'
|
||||
b'<entry key="sbdd640" count="9" red="50" green="200" blue="0">Finished</entry>'
|
||||
b'</status>'
|
||||
)
|
||||
assert list(theStatus.pack()) == [
|
||||
("New", {
|
||||
"key": statusKeys[0],
|
||||
"count": "3",
|
||||
"red": "100",
|
||||
"green": "100",
|
||||
"blue": "100"
|
||||
}),
|
||||
("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
|
||||
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 list(theStatus._store.keys()) == statusKeys
|
||||
assert theStatus._store[statusKeys[0]]["name"] == "New"
|
||||
assert theStatus._store[statusKeys[1]]["name"] == "Note"
|
||||
assert theStatus._store[statusKeys[2]]["name"] == "Draft"
|
||||
assert theStatus._store[statusKeys[3]]["name"] == "Finished"
|
||||
assert theStatus._store[statusKeys[0]]["name"] == "New0"
|
||||
assert theStatus._store[statusKeys[1]]["name"] == "New1"
|
||||
assert theStatus._store[statusKeys[2]]["name"] == "New2"
|
||||
assert theStatus._store[statusKeys[3]]["name"] == "New3"
|
||||
assert theStatus._store[statusKeys[0]]["cols"] == (100, 100, 100)
|
||||
assert theStatus._store[statusKeys[1]]["cols"] == (200, 50, 0)
|
||||
assert theStatus._store[statusKeys[2]]["cols"] == (200, 150, 0)
|
||||
assert theStatus._store[statusKeys[3]]["cols"] == (50, 200, 0)
|
||||
assert theStatus._store[statusKeys[1]]["cols"] == (150, 150, 150)
|
||||
assert theStatus._store[statusKeys[2]]["cols"] == (200, 200, 200)
|
||||
assert theStatus._store[statusKeys[3]]["cols"] == (250, 250, 250)
|
||||
assert theStatus._store[statusKeys[0]]["count"] == countTo[0]
|
||||
assert theStatus._store[statusKeys[1]]["count"] == countTo[1]
|
||||
assert theStatus._store[statusKeys[2]]["count"] == countTo[2]
|
||||
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 pytest
|
||||
|
||||
from tools import readFile
|
||||
from tools import C, buildTestProject, readFile
|
||||
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.document import NWDoc
|
||||
@@ -133,21 +133,20 @@ def testCoreToken_Setters(mockGUI):
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projLang = "en"
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
|
||||
theProject.data.setLanguage("en")
|
||||
theProject._loadProjectLocalisation()
|
||||
|
||||
theToken = BareTokenizer(theProject)
|
||||
theToken.setKeepMarkdown(True)
|
||||
|
||||
assert theProject.openProject(nwMinimal)
|
||||
sHandle = "8c659a11cd429"
|
||||
|
||||
# Set some content to work with
|
||||
|
||||
docText = (
|
||||
"### Scene Six\n\n"
|
||||
"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")
|
||||
|
||||
nDoc = NWDoc(theProject, sHandle)
|
||||
nDoc = NWDoc(theProject, C.hSceneDoc)
|
||||
assert nDoc.writeDocument(docText)
|
||||
|
||||
theProject.setAutoReplace({"A": "this", "B": "that"})
|
||||
theProject.data.setAutoReplace({"A": "this", "B": "that"})
|
||||
|
||||
assert theProject.saveProject()
|
||||
|
||||
# Root Heading
|
||||
assert theToken.addRootHeading("stuff") is False
|
||||
assert theToken.addRootHeading(sHandle) is False
|
||||
assert theToken.addRootHeading(C.hSceneDoc) is False
|
||||
|
||||
# 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._theTokens[-1] == (
|
||||
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE
|
||||
)
|
||||
|
||||
# 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._theTokens[-1] == (
|
||||
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
|
||||
assert theToken.setText("stuff") is False
|
||||
assert theToken.setText(sHandle) is True
|
||||
assert theToken.setText(C.hSceneDoc) is True
|
||||
assert theToken._theText == docText
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
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 == (
|
||||
"# ERROR\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._isNone is False
|
||||
@@ -212,7 +211,7 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
|
||||
assert theToken.theResult == "This is text with escapes: ** ~~ __"
|
||||
|
||||
# Save File
|
||||
savePath = os.path.join(nwMinimal, "dump.nwd")
|
||||
savePath = os.path.join(fncDir, "dump.nwd")
|
||||
theToken.saveRawMarkdown(savePath)
|
||||
assert readFile(savePath) == (
|
||||
"# Notes: Plot\n\n"
|
||||
@@ -884,7 +883,7 @@ def testCoreToken_ProcessHeaders(mockGUI):
|
||||
"""Test the header and page parser of the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projLang = "en"
|
||||
theProject.data.setLanguage("en")
|
||||
theProject._loadProjectLocalisation()
|
||||
theToken = BareTokenizer(theProject)
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ import os
|
||||
import pytest
|
||||
import random
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from tools import readFile
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
@@ -198,7 +196,7 @@ def testCoreTree_BuildTree(mockGUI, mockItems):
|
||||
assert len(theTree) == len(mockItems) + 1
|
||||
|
||||
theList = theTree.handles()
|
||||
nHandle = "0000000000010"
|
||||
nHandle = "0000000000000"
|
||||
assert theList[-1] == nHandle
|
||||
|
||||
# Try to add existing handle
|
||||
@@ -395,64 +393,6 @@ def testCoreTree_Reorder(mockGUI, mockItems):
|
||||
# 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
|
||||
def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpDir):
|
||||
"""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.chapCountVal.text() == f"{3: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
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import pytest
|
||||
import os
|
||||
|
||||
from tools import getGuiItem
|
||||
from tools import buildTestProject, getGuiItem
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
@@ -33,10 +33,10 @@ from novelwriter.dialogs.projload import GuiProjectLoad
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||
def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, fncProj):
|
||||
"""Test the load project wizard.
|
||||
"""
|
||||
assert nwGUI.openProject(nwMinimal)
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.closeProject()
|
||||
|
||||
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *a: None)
|
||||
@@ -87,10 +87,10 @@ def testDlgLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||
nwLoad._doDeleteRecent()
|
||||
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))
|
||||
qtbot.mouseClick(nwLoad.browseButton, Qt.LeftButton)
|
||||
assert nwLoad.openPath == nwMinimal
|
||||
assert nwLoad.openPath == fncProj
|
||||
assert nwLoad.openState == nwLoad.OPEN_STATE
|
||||
|
||||
nwLoad.close()
|
||||
|
||||
@@ -50,7 +50,7 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
|
||||
|
||||
# Pretend we have a project
|
||||
nwGUI.hasProject = True
|
||||
nwGUI.theProject.setSpellLang("en")
|
||||
nwGUI.theProject.data.setSpellLang("en")
|
||||
|
||||
# Get the dialog object
|
||||
nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger)
|
||||
@@ -95,9 +95,9 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd
|
||||
|
||||
# Set some values
|
||||
theProject = nwGUI.theProject
|
||||
theProject.setSpellLang("en")
|
||||
theProject.setBookAuthors("Jane Smith\nJohn Smith")
|
||||
theProject.setAutoReplace({"A": "B", "C": "D"})
|
||||
theProject.data.setSpellLang("en")
|
||||
theProject.data.setAuthors("Jane Smith\nJohn Smith")
|
||||
theProject.data.setAutoReplace({"A": "B", "C": "D"})
|
||||
|
||||
# Create Dialog
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN)
|
||||
@@ -136,9 +136,9 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd
|
||||
assert projSettings.spellChanged is False
|
||||
|
||||
projSettings._doSave()
|
||||
assert theProject.projName == "Project Name"
|
||||
assert theProject.bookTitle == "Project Title"
|
||||
assert theProject.bookAuthors == ["Jane Doe", "John Doh"]
|
||||
assert theProject.data.name == "Project Name"
|
||||
assert theProject.data.title == "Project Title"
|
||||
assert theProject.data.authors == ["Jane Doe", "John Doh"]
|
||||
|
||||
# Clean up
|
||||
projSettings._doClose()
|
||||
@@ -330,13 +330,13 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncDir, fncProj,
|
||||
# Check Project
|
||||
projSettings._doSave()
|
||||
|
||||
statusItems = dict(theProject.statusItems.items())
|
||||
statusItems = dict(theProject.data.itemStatus.items())
|
||||
assert statusItems[C.sNew]["name"] == "New"
|
||||
assert statusItems[C.sDraft]["name"] == "Draft"
|
||||
assert statusItems[C.sFinished]["name"] == "Finished"
|
||||
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.iMajor]["name"] == "Major"
|
||||
assert importItems[C.iMain]["name"] == "Main"
|
||||
@@ -365,9 +365,9 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mock
|
||||
|
||||
# Set some values
|
||||
theProject = nwGUI.theProject
|
||||
theProject.autoReplace = {
|
||||
theProject.data.setAutoReplace({
|
||||
"A": "B", "C": "D"
|
||||
}
|
||||
})
|
||||
|
||||
# Create Dialog
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_REPLACE)
|
||||
@@ -429,7 +429,7 @@ def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mock
|
||||
|
||||
# Check Project
|
||||
projSettings._doSave()
|
||||
assert theProject.autoReplace == {
|
||||
assert theProject.data.autoReplace == {
|
||||
"A": "B", "C": "D", "This": "With This Stuff"
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import pytest
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QDialog, QAction
|
||||
|
||||
from tools import writeFile, readFile, getGuiItem
|
||||
from tools import buildTestProject, writeFile, readFile, getGuiItem
|
||||
from mock import causeOSError
|
||||
|
||||
from novelwriter.constants import nwFiles
|
||||
@@ -33,16 +33,18 @@ from novelwriter.dialogs.wordlist import GuiWordList
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||
def testDlgWordList_Dialog(qtbot, monkeypatch, nwGUI, fncProj):
|
||||
"""test the word list editor.
|
||||
"""
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
|
||||
monkeypatch.setattr(GuiWordList, "exec_", lambda *a: None)
|
||||
monkeypatch.setattr(GuiWordList, "result", lambda *a: QDialog.Accepted)
|
||||
monkeypatch.setattr(GuiWordList, "accept", lambda *a: None)
|
||||
|
||||
# Open project
|
||||
nwGUI.openProject(nwMinimal)
|
||||
dictFile = os.path.join(nwMinimal, "meta", nwFiles.PROJ_DICT)
|
||||
nwGUI.openProject(fncProj)
|
||||
dictFile = os.path.join(fncProj, "meta", nwFiles.PROJ_DICT)
|
||||
|
||||
# Load the dialog
|
||||
nwGUI.mainMenu.aEditWordList.activate(QAction.Trigger)
|
||||
|
||||
@@ -22,6 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import pytest
|
||||
|
||||
from mock import causeOSError
|
||||
from tools import C, buildTestProject
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
|
||||
@@ -36,12 +37,12 @@ KEY_DELAY = 1
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiEditor_Init(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||
def testGuiEditor_Init(qtbot, nwGUI, fncProj, ipsumText, mockRnd):
|
||||
"""Test initialising the editor.
|
||||
"""
|
||||
# Open project
|
||||
assert nwGUI.openProject(nwMinimal)
|
||||
assert nwGUI.openDocument("8c659a11cd429")
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc)
|
||||
|
||||
nwGUI.docEditor.setText("### Lorem Ipsum\n\n%s" % ipsumText[0])
|
||||
assert nwGUI.saveDocument()
|
||||
@@ -79,13 +80,11 @@ def testGuiEditor_Init(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
# Open project
|
||||
sHandle = "8c659a11cd429"
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
longText = "### Lorem Ipsum\n\n%s" % "\n\n".join(ipsumText*20)
|
||||
nwGUI.docEditor.replaceText(longText)
|
||||
@@ -101,11 +100,11 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
||||
# Document too big
|
||||
with monkeypatch.context() as mp:
|
||||
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
|
||||
|
||||
# Regular open
|
||||
assert nwGUI.docEditor.loadText(sHandle) is True
|
||||
assert nwGUI.docEditor.loadText(C.hSceneDoc) is True
|
||||
assert nwGUI.docEditor._bigDoc is False
|
||||
|
||||
# Reload too big text
|
||||
@@ -116,18 +115,18 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
||||
|
||||
# Big doc handling
|
||||
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
|
||||
|
||||
# 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()
|
||||
assert nwGUI.docEditor.document().findBlock(cursPos).blockNumber() == 4
|
||||
|
||||
# Load empty document
|
||||
nwGUI.docEditor.replaceText("")
|
||||
assert nwGUI.saveDocument() is True
|
||||
assert nwGUI.docEditor.loadText(sHandle) is True
|
||||
assert nwGUI.docEditor.loadText(C.hSceneDoc) is True
|
||||
assert nwGUI.docEditor.toPlainText() == ""
|
||||
|
||||
# qtbot.stop()
|
||||
@@ -136,13 +135,11 @@ def testGuiEditor_LoadText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
# Open project
|
||||
sHandle = "8c659a11cd429"
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
# Save Text
|
||||
# =========
|
||||
@@ -159,7 +156,7 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
||||
# Unkown handle
|
||||
nwGUI.docEditor._docHandle = "0123456789abcdef"
|
||||
assert nwGUI.docEditor.saveText() is False
|
||||
nwGUI.docEditor._docHandle = sHandle
|
||||
nwGUI.docEditor._docHandle = C.hSceneDoc
|
||||
|
||||
# Cause error when saving
|
||||
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
|
||||
|
||||
# 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:])
|
||||
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
|
||||
assert nwGUI.docEditor.saveText() is True
|
||||
@@ -182,13 +179,11 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, nwMinimal, ipsumTe
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
||||
def testGuiEditor_MetaData(qtbot, nwGUI, fncProj, mockRnd):
|
||||
"""Test extracting various meta data and other values.
|
||||
"""
|
||||
# Open project
|
||||
sHandle = "8c659a11cd429"
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
# Get Text
|
||||
# This should replace line and paragraph separators, but preserve
|
||||
@@ -203,7 +198,7 @@ def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
||||
|
||||
# Check Propertoes
|
||||
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.isEmpty() is False
|
||||
|
||||
@@ -211,9 +206,9 @@ def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
||||
assert nwGUI.docEditor.setCursorPosition(None) is False
|
||||
assert nwGUI.docEditor.setCursorPosition(10) is True
|
||||
assert nwGUI.docEditor.getCursorPosition() == 10
|
||||
assert nwGUI.theProject.tree[sHandle].cursorPos != 10
|
||||
assert nwGUI.theProject.tree[C.hSceneDoc].cursorPos != 10
|
||||
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(2) is True
|
||||
@@ -231,16 +226,14 @@ def testGuiEditor_MetaData(qtbot, nwGUI, nwMinimal):
|
||||
|
||||
|
||||
@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
|
||||
action features, just that the actions are actually called. The
|
||||
various action features are tested when their respective functions
|
||||
are tested.
|
||||
"""
|
||||
# Open project
|
||||
sHandle = "8c659a11cd429"
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||
assert nwGUI.docEditor.replaceText(theText) is True
|
||||
@@ -452,7 +445,7 @@ def testGuiEditor_Actions(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||
# No Document Handle
|
||||
nwGUI.docEditor._docHandle = None
|
||||
assert nwGUI.docEditor.docAction(nwDocAction.BLOCK_TXT) is False
|
||||
nwGUI.docEditor._docHandle = sHandle
|
||||
nwGUI.docEditor._docHandle = C.hSceneDoc
|
||||
|
||||
# Wrong Action Type
|
||||
assert nwGUI.docEditor.docAction(None) is False
|
||||
@@ -466,13 +459,11 @@ def testGuiEditor_Actions(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
# Open project
|
||||
sHandle = "8c659a11cd429"
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||
assert nwGUI.docEditor.replaceText(theText) is True
|
||||
@@ -487,7 +478,7 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||
nwGUI.docEditor._docHandle = None
|
||||
assert nwGUI.docEditor.setCursorPosition(24) is True
|
||||
assert nwGUI.docEditor.insertText("Stuff") is False
|
||||
nwGUI.docEditor._docHandle = sHandle
|
||||
nwGUI.docEditor._docHandle = C.hSceneDoc
|
||||
|
||||
# Insert String
|
||||
assert nwGUI.docEditor.setCursorPosition(24) is True
|
||||
@@ -551,13 +542,11 @@ def testGuiEditor_Insert(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
# Open project
|
||||
sHandle = "8c659a11cd429"
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||
assert nwGUI.docEditor.replaceText(theText) is True
|
||||
@@ -760,13 +749,11 @@ def testGuiEditor_TextManipulation(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTe
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
# Open project
|
||||
sHandle = "8c659a11cd429"
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
theText = "### A Scene\n\n%s" % "\n\n".join(ipsumText)
|
||||
assert nwGUI.docEditor.replaceText(theText) is True
|
||||
@@ -1075,13 +1062,11 @@ def testGuiEditor_BlockFormatting(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumTex
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||
def testGuiEditor_Tags(qtbot, nwGUI, fncProj, ipsumText, mockRnd):
|
||||
"""Test the document editor tags functionality.
|
||||
"""
|
||||
# Open project
|
||||
sHandle = "8c659a11cd429"
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
# Create Scene
|
||||
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
|
||||
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.docEditor.replaceText(theText) is True
|
||||
assert nwGUI.saveDocument() is True
|
||||
@@ -1098,7 +1083,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||
|
||||
# Follow Tag
|
||||
# ==========
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
# Empty Block
|
||||
assert nwGUI.docEditor.setCursorLine(1) is True
|
||||
@@ -1136,7 +1121,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, nwMinimal, ipsumText):
|
||||
|
||||
|
||||
@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.
|
||||
"""
|
||||
class MockThreadPool:
|
||||
@@ -1153,7 +1138,8 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||
nwGUI.threadPool = MockThreadPool()
|
||||
nwGUI.docEditor.wcTimerDoc.blockSignals(True)
|
||||
nwGUI.docEditor.wcTimerSel.blockSignals(True)
|
||||
assert nwGUI.openProject(nwMinimal) is True
|
||||
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
|
||||
# Run on an empty document
|
||||
nwGUI.docEditor._runDocCounter()
|
||||
@@ -1167,10 +1153,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||
assert nwGUI.docEditor.docFooter.wordsText.text() == "Words: 0 (+0)"
|
||||
|
||||
# Open a document and populate it
|
||||
sHandle = "8c659a11cd429"
|
||||
nwGUI.theProject.tree[sHandle]._initCount = 0 # Clear item's count
|
||||
nwGUI.theProject.tree[sHandle]._wordCount = 0 # Clear item's count
|
||||
assert nwGUI.openDocument(sHandle) is True
|
||||
nwGUI.theProject.tree[C.hSceneDoc]._initCount = 0 # Clear item's count
|
||||
nwGUI.theProject.tree[C.hSceneDoc]._wordCount = 0 # Clear item's count
|
||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||
|
||||
theText = "\n\n".join(ipsumText)
|
||||
cC, wC, pC = countWords(theText)
|
||||
@@ -1193,9 +1178,9 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, nwMinimal, ipsumText):
|
||||
|
||||
nwGUI.docEditor.wCounterDoc.run()
|
||||
# nwGUI.docEditor._updateDocCounts(cC, wC, pC)
|
||||
assert nwGUI.theProject.tree[sHandle]._charCount == cC
|
||||
assert nwGUI.theProject.tree[sHandle]._wordCount == wC
|
||||
assert nwGUI.theProject.tree[sHandle]._paraCount == pC
|
||||
assert nwGUI.theProject.tree[C.hSceneDoc]._charCount == cC
|
||||
assert nwGUI.theProject.tree[C.hSceneDoc]._wordCount == wC
|
||||
assert nwGUI.theProject.tree[C.hSceneDoc]._paraCount == pC
|
||||
assert nwGUI.docEditor.docFooter.wordsText.text() == f"Words: {wC} (+{wC})"
|
||||
|
||||
# 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.projPath is None
|
||||
assert nwGUI.theProject.projMeta is None
|
||||
assert nwGUI.theProject.projName == ""
|
||||
assert nwGUI.theProject.bookTitle == ""
|
||||
assert len(nwGUI.theProject.bookAuthors) == 0
|
||||
assert not nwGUI.theProject.spellCheck
|
||||
assert nwGUI.theProject.data.name == ""
|
||||
assert nwGUI.theProject.data.title == ""
|
||||
assert nwGUI.theProject.data.authors == []
|
||||
assert nwGUI.theProject.data.spellCheck is False
|
||||
|
||||
# Check the files
|
||||
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.projPath == fncProj
|
||||
assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta")
|
||||
assert nwGUI.theProject.projName == "New Project"
|
||||
assert nwGUI.theProject.bookTitle == "New Novel"
|
||||
assert len(nwGUI.theProject.bookAuthors) == 1
|
||||
assert nwGUI.theProject.spellCheck is False
|
||||
assert nwGUI.theProject.data.name == "New Project"
|
||||
assert nwGUI.theProject.data.title == "New Novel"
|
||||
assert nwGUI.theProject.data.authors == ["Jane Doe"]
|
||||
assert nwGUI.theProject.data.spellCheck is False
|
||||
|
||||
# Check that tree items have been created
|
||||
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
|
||||
assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None)
|
||||
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.setSelectedHandle(newHandle)
|
||||
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()
|
||||
|
||||
# Check the files
|
||||
@@ -509,21 +510,21 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
copyfile(projFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
projFile = os.path.join(fncProj, "content", "0000000000020.nwd")
|
||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000020.nwd")
|
||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000020.nwd")
|
||||
projFile = os.path.join(fncProj, "content", "0000000000010.nwd")
|
||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000010.nwd")
|
||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000010.nwd")
|
||||
copyfile(projFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
projFile = os.path.join(fncProj, "content", "0000000000021.nwd")
|
||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000021.nwd")
|
||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000021.nwd")
|
||||
projFile = os.path.join(fncProj, "content", "0000000000011.nwd")
|
||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000011.nwd")
|
||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000011.nwd")
|
||||
copyfile(projFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
projFile = os.path.join(fncProj, "content", "0000000000022.nwd")
|
||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000022.nwd")
|
||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000022.nwd")
|
||||
projFile = os.path.join(fncProj, "content", "0000000000012.nwd")
|
||||
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000012.nwd")
|
||||
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000012.nwd")
|
||||
copyfile(projFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
from tools import getGuiItem
|
||||
from tools import buildTestProject, getGuiItem
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QFileDialog, QWizard, QDialog
|
||||
@@ -37,7 +37,7 @@ from novelwriter.tools.projwizard import (
|
||||
|
||||
@pytest.mark.gui
|
||||
@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.
|
||||
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
|
||||
assert nwGUI.openProject(nwMinimal)
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI, "closeProject", lambda *a: False)
|
||||
assert nwGUI.newProject() is False
|
||||
@@ -61,7 +61,7 @@ def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal):
|
||||
assert nwGUI.newProject() is False
|
||||
|
||||
# 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
|
||||
|
||||
# Test the Wizard Launching
|
||||
|
||||
+14
-4
@@ -167,9 +167,19 @@ def buildTestProject(theObject, projPath):
|
||||
|
||||
theProject.clearProject()
|
||||
theProject.setProjectPath(projPath, newProject=True)
|
||||
theProject.setProjectName("New Project")
|
||||
theProject.setBookTitle("New Novel")
|
||||
theProject.setBookAuthors("Jane Doe")
|
||||
|
||||
theProject.data.itemStatus.write(None, "New", (100, 100, 100))
|
||||
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
|
||||
# single chapter folder with a single file.
|
||||
@@ -195,7 +205,7 @@ def buildTestProject(theObject, projPath):
|
||||
aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene"))
|
||||
theProject.index.reIndexHandle(xHandle[8])
|
||||
|
||||
theProject.projOpened = time.time()
|
||||
theProject._projOpened = time.time()
|
||||
theProject.setProjectChanged(True)
|
||||
theProject.saveProject(autoSave=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user