Add a storage class (#1225)
This commit is contained in:
+4
-4
@@ -3707,8 +3707,8 @@ helpful feedback and issue reports for the new features added in this, and previ
|
||||
|
||||
**User Interface**
|
||||
|
||||
* Added a preferences dialog for the program settings. No longer necessary to edit the config file.
|
||||
PR #30.
|
||||
* Added a preferences dialog for the program settings. It is no longer necessary to edit the config
|
||||
file. PR #30.
|
||||
* The document viewer remembers scroll bar position when pressing `Ctrl+R` on a document already
|
||||
being viewed. PR #28.
|
||||
* Removed version number from windows title. PR #28.
|
||||
@@ -3749,8 +3749,8 @@ helpful feedback and issue reports for the new features added in this, and previ
|
||||
**Status Bar**
|
||||
|
||||
* Redesign of the status bar adding project and session stats as well as a session timer. PR #21.
|
||||
* Project word count is written to the project file, which is needed for the session word count. PR
|
||||
#21.
|
||||
* Project word count is written to the project file, which is needed for the session word count.
|
||||
PR #21.
|
||||
* Closing a project now clears the status bar. PR #21.
|
||||
|
||||
**Editor**
|
||||
|
||||
+19
-2
@@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
@@ -87,9 +88,10 @@ def checkBool(value, default):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
elif isinstance(value, str):
|
||||
if value == "True":
|
||||
check = value.lower()
|
||||
if check in ("true", "yes", "on"):
|
||||
return True
|
||||
elif value == "False":
|
||||
elif check in ("false", "no", "off"):
|
||||
return False
|
||||
else:
|
||||
return default
|
||||
@@ -113,6 +115,15 @@ def checkHandle(value, default, allowNone=False):
|
||||
return default
|
||||
|
||||
|
||||
def checkUuid(value, default):
|
||||
"""Try to process a value as an uuid, or return a default.
|
||||
"""
|
||||
try:
|
||||
return str(uuid.UUID(value))
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Validator Functions
|
||||
# =============================================================================================== #
|
||||
@@ -249,6 +260,12 @@ def simplified(string):
|
||||
return " ".join(str(string).strip().split())
|
||||
|
||||
|
||||
def yesNo(value):
|
||||
"""Convert a boolean evaluated variable to a yes or no.
|
||||
"""
|
||||
return "yes" if value else "no"
|
||||
|
||||
|
||||
def splitVersionNumber(value):
|
||||
"""Split a version string on the form aa.bb.cc into major, minor
|
||||
and patch, and computes an integer value aabbcc.
|
||||
|
||||
@@ -19,8 +19,7 @@ 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 novelwriter.core.doctools import DocMerger, DocSplitter
|
||||
from novelwriter.core.document import NWDoc
|
||||
from novelwriter.core.coretools import DocMerger, DocSplitter, ProjectBuilder
|
||||
from novelwriter.core.index import countWords
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.spellcheck import NWSpellEnchant
|
||||
@@ -31,8 +30,8 @@ from novelwriter.core.tomd import ToMarkdown
|
||||
__all__ = [
|
||||
"DocMerger",
|
||||
"DocSplitter",
|
||||
"ProjectBuilder",
|
||||
"countWords",
|
||||
"NWDoc",
|
||||
"NWProject",
|
||||
"NWSpellEnchant",
|
||||
"ToHtml",
|
||||
|
||||
@@ -0,0 +1,454 @@
|
||||
"""
|
||||
novelWriter – Project Document Tools
|
||||
====================================
|
||||
A collection of tools to create and manipulate documents
|
||||
|
||||
File History:
|
||||
Created: 2022-10-02 [2.0b1] DocMerger
|
||||
Created: 2022-10-11 [2.0b1] DocSplitter
|
||||
|
||||
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 shutil
|
||||
import logging
|
||||
import novelwriter
|
||||
|
||||
from time import time
|
||||
from functools import partial
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
from novelwriter.enum import nwAlert
|
||||
from novelwriter.common import minmax, simplified
|
||||
from novelwriter.constants import nwItemClass
|
||||
from novelwriter.core.project import NWProject
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DocMerger:
|
||||
"""Document tool for merging a set of documents into a single new
|
||||
document. The parameters are defined by the user using the
|
||||
GuiDocMerge dialog.
|
||||
"""
|
||||
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.theProject = theProject
|
||||
|
||||
self._error = ""
|
||||
self._targetDoc = None
|
||||
self._targetText = []
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def getError(self):
|
||||
"""Return any collected errors.
|
||||
"""
|
||||
return self._error
|
||||
|
||||
def setTargetDoc(self, tHandle):
|
||||
"""Set the target document for the merging. Calling this
|
||||
function resets the class.
|
||||
"""
|
||||
self._targetDoc = tHandle
|
||||
self._targetText = []
|
||||
return
|
||||
|
||||
def newTargetDoc(self, srcHandle, docLabel):
|
||||
"""Create a barnd new target document based on a source handle
|
||||
and a new doc label. Calling this function resets the class.
|
||||
"""
|
||||
srcItem = self.theProject.tree[srcHandle]
|
||||
if srcItem is None:
|
||||
return None
|
||||
|
||||
newHandle = self.theProject.newFile(docLabel, srcItem.itemParent)
|
||||
newItem = self.theProject.tree[newHandle]
|
||||
newItem.setLayout(srcItem.itemLayout)
|
||||
newItem.setStatus(srcItem.itemStatus)
|
||||
newItem.setImport(srcItem.itemImport)
|
||||
|
||||
self._targetDoc = newHandle
|
||||
self._targetText = []
|
||||
|
||||
return newHandle
|
||||
|
||||
def appendText(self, srcHandle, addComment, cmtPrefix):
|
||||
"""Append text from an existing document to the text buffer.
|
||||
"""
|
||||
srcItem = self.theProject.tree[srcHandle]
|
||||
if srcItem is None:
|
||||
return False
|
||||
|
||||
inDoc = self.theProject.storage.getDocument(srcHandle)
|
||||
docText = (inDoc.readDocument() or "").rstrip("\n")
|
||||
|
||||
if addComment:
|
||||
docInfo = srcItem.describeMe()
|
||||
docSt, _ = srcItem.getImportStatus(incIcon=False)
|
||||
cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n"
|
||||
docText = cmtLine + docText
|
||||
|
||||
self._targetText.append(docText)
|
||||
|
||||
return True
|
||||
|
||||
def writeTargetDoc(self):
|
||||
"""Write the accumulated text into the designated target
|
||||
document, appending any existing text.
|
||||
"""
|
||||
if self._targetDoc is None:
|
||||
return False
|
||||
|
||||
outDoc = self.theProject.storage.getDocument(self._targetDoc)
|
||||
docText = (outDoc.readDocument() or "").rstrip("\n")
|
||||
if docText:
|
||||
self._targetText.insert(0, docText)
|
||||
|
||||
status = outDoc.writeDocument("\n\n".join(self._targetText) + "\n\n")
|
||||
if not status:
|
||||
self._error = outDoc.getError()
|
||||
|
||||
return status
|
||||
|
||||
# END Class DocMerger
|
||||
|
||||
|
||||
class DocSplitter:
|
||||
"""Document tool for splitting a document into a set of new
|
||||
documents. The parameters are defined by the user using the
|
||||
GuiDocSplit dialog.
|
||||
"""
|
||||
|
||||
def __init__(self, theProject, sHandle):
|
||||
|
||||
self.theProject = theProject
|
||||
|
||||
self._error = ""
|
||||
self._parHandle = None
|
||||
self._srcHandle = None
|
||||
self._srcItem = None
|
||||
|
||||
self._inFolder = False
|
||||
self._rawData = []
|
||||
|
||||
srcItem = self.theProject.tree[sHandle]
|
||||
if srcItem is not None and srcItem.isFileType():
|
||||
self._srcHandle = sHandle
|
||||
self._srcItem = srcItem
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def getError(self):
|
||||
"""Return any collected errors.
|
||||
"""
|
||||
return self._error
|
||||
|
||||
def setParentItem(self, pHandle):
|
||||
"""Set the item that will be the top level parent item for the
|
||||
new documents.
|
||||
"""
|
||||
self._parHandle = pHandle
|
||||
self._inFolder = False
|
||||
return
|
||||
|
||||
def newParentFolder(self, pHandle, folderLabel):
|
||||
"""Create a new folder that will be the top level parent item
|
||||
for the new documents.
|
||||
"""
|
||||
if self._srcItem is None:
|
||||
return None
|
||||
|
||||
newHandle = self.theProject.newFolder(folderLabel, pHandle)
|
||||
newItem = self.theProject.tree[newHandle]
|
||||
newItem.setStatus(self._srcItem.itemStatus)
|
||||
newItem.setImport(self._srcItem.itemImport)
|
||||
|
||||
self._parHandle = newHandle
|
||||
self._inFolder = True
|
||||
|
||||
return newHandle
|
||||
|
||||
def splitDocument(self, splitData, splitText):
|
||||
"""Loop through the split data record and perform the split job.
|
||||
"""
|
||||
self._rawData = []
|
||||
buffer = splitText.copy()
|
||||
for lineNo, hLevel, hLabel in reversed(splitData):
|
||||
chunk = buffer[lineNo:]
|
||||
buffer = buffer[:lineNo]
|
||||
self._rawData.insert(0, (chunk, hLevel, hLabel))
|
||||
|
||||
return True
|
||||
|
||||
def writeDocuments(self, docHierarchy):
|
||||
"""An iterator that will write each document in the buffer, and
|
||||
return its new handle, parent handle, and sibling handle.
|
||||
"""
|
||||
if self._srcHandle is None or self._srcItem is None:
|
||||
return
|
||||
|
||||
pHandle = self._parHandle
|
||||
nHandle = self._parHandle if self._inFolder else self._srcHandle
|
||||
hHandle = [self._parHandle, None, None, None, None]
|
||||
|
||||
pLevel = 0
|
||||
for docText, hLevel, docLabel in self._rawData:
|
||||
|
||||
hLevel = minmax(hLevel, 1, 4)
|
||||
if pLevel == 0:
|
||||
pLevel = hLevel
|
||||
|
||||
if docHierarchy:
|
||||
if hLevel == 1:
|
||||
pHandle = self._parHandle
|
||||
elif hLevel == 2:
|
||||
pHandle = hHandle[1] or hHandle[0]
|
||||
elif hLevel == 3:
|
||||
pHandle = hHandle[2] or hHandle[1] or hHandle[0]
|
||||
elif hLevel == 4:
|
||||
pHandle = hHandle[3] or hHandle[2] or hHandle[1] or hHandle[0]
|
||||
|
||||
if hLevel < pLevel:
|
||||
nHandle = hHandle[hLevel] or hHandle[0]
|
||||
elif hLevel > pLevel:
|
||||
nHandle = pHandle
|
||||
|
||||
dHandle = self.theProject.newFile(docLabel, pHandle)
|
||||
hHandle[hLevel] = dHandle
|
||||
|
||||
newItem = self.theProject.tree[dHandle]
|
||||
newItem.setStatus(self._srcItem.itemStatus)
|
||||
newItem.setImport(self._srcItem.itemImport)
|
||||
|
||||
outDoc = self.theProject.storage.getDocument(dHandle)
|
||||
status = outDoc.writeDocument("\n".join(docText))
|
||||
if not status:
|
||||
self._error = outDoc.getError()
|
||||
|
||||
yield status, dHandle, nHandle
|
||||
|
||||
hHandle[hLevel] = dHandle
|
||||
nHandle = dHandle
|
||||
pLevel = hLevel
|
||||
|
||||
return
|
||||
|
||||
# END Class DocSplitter
|
||||
|
||||
|
||||
class ProjectBuilder:
|
||||
"""A class to build a new project from a set of user-defined
|
||||
parameter provided by the New Projecty Wizard.
|
||||
"""
|
||||
|
||||
def __init__(self, mainGui):
|
||||
|
||||
self.mainGui = mainGui
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
|
||||
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def buildProject(self, data):
|
||||
"""Build a project from a data dictionary of specifications
|
||||
provided by the wizard.
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
logger.error("Invalid call to newProject function")
|
||||
return False
|
||||
|
||||
popMinimal = data.get("popMinimal", True)
|
||||
popCustom = data.get("popCustom", False)
|
||||
popSample = data.get("popSample", False)
|
||||
|
||||
# Check if we're extracting the sample project. This is handled
|
||||
# differently as it isn't actually a new project, so we forward
|
||||
# this to another function and return here.
|
||||
if popSample:
|
||||
return self._extractSampleProject(data)
|
||||
|
||||
projPath = data.get("projPath", None)
|
||||
if projPath is None:
|
||||
logger.error("No project path set for the new project")
|
||||
return False
|
||||
|
||||
project = NWProject(self.mainGui)
|
||||
if not project.storage.openProjectInPlace(projPath, newProject=True):
|
||||
return False
|
||||
|
||||
lblNewProject = self.tr("New Project")
|
||||
lblNewChapter = self.tr("New Chapter")
|
||||
lblNewScene = self.tr("New Scene")
|
||||
lblTitlePage = self.tr("Title Page")
|
||||
lblByAuthors = self.tr("By")
|
||||
|
||||
# Settings
|
||||
projName = data.get("projName", lblNewProject)
|
||||
projTitle = data.get("projTitle", lblNewProject)
|
||||
projAuthors = data.get("projAuthors", "")
|
||||
|
||||
project.data.setUuid(None)
|
||||
project.data.setName(projName)
|
||||
project.data.setTitle(projTitle)
|
||||
project.data.setAuthors(projAuthors)
|
||||
project.setDefaultStatusImport()
|
||||
project._projOpened = int(time())
|
||||
|
||||
# Add Root Folders
|
||||
hNovelRoot = project.newRoot(nwItemClass.NOVEL)
|
||||
hTitlePage = project.newFile(lblTitlePage, hNovelRoot)
|
||||
novelTitle = project.data.title if project.data.title else project.data.name
|
||||
|
||||
titlePage = f"#! {novelTitle}\n\n"
|
||||
if project.data.authors:
|
||||
titlePage += f">> {lblByAuthors} {project.getFormattedAuthors()} <<\n\n"
|
||||
|
||||
aDoc = project.storage.getDocument(hTitlePage)
|
||||
aDoc.writeDocument(titlePage)
|
||||
|
||||
if popMinimal:
|
||||
# Creating a minimal project with a few root folders and a
|
||||
# single chapter with a single scene.
|
||||
hChapter = project.newFile(lblNewChapter, hNovelRoot)
|
||||
aDoc = project.storage.getDocument(hChapter)
|
||||
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
|
||||
|
||||
hScene = project.newFile(lblNewScene, hChapter)
|
||||
aDoc = project.storage.getDocument(hScene)
|
||||
aDoc.writeDocument(f"### {lblNewScene}\n\n")
|
||||
|
||||
project.newRoot(nwItemClass.PLOT)
|
||||
project.newRoot(nwItemClass.CHARACTER)
|
||||
project.newRoot(nwItemClass.WORLD)
|
||||
project.newRoot(nwItemClass.ARCHIVE)
|
||||
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
|
||||
elif popCustom:
|
||||
# Create a project structure based on selected root folders
|
||||
# and a number of chapters and scenes selected in the
|
||||
# wizard's custom page.
|
||||
|
||||
# Create chapters and scenes
|
||||
numChapters = data.get("numChapters", 0)
|
||||
numScenes = data.get("numScenes", 0)
|
||||
|
||||
chSynop = self.tr("Summary of the chapter.")
|
||||
scSynop = self.tr("Summary of the scene.")
|
||||
|
||||
# Create chapters
|
||||
if numChapters > 0:
|
||||
for ch in range(numChapters):
|
||||
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
|
||||
cHandle = project.newFile(chTitle, hNovelRoot)
|
||||
aDoc = project.storage.getDocument(cHandle)
|
||||
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
|
||||
|
||||
# Create chapter scenes
|
||||
if numScenes > 0:
|
||||
for sc in range(numScenes):
|
||||
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
||||
sHandle = project.newFile(scTitle, cHandle)
|
||||
aDoc = project.storage.getDocument(sHandle)
|
||||
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
||||
|
||||
# Create scenes (no chapters)
|
||||
elif numScenes > 0:
|
||||
for sc in range(numScenes):
|
||||
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
|
||||
sHandle = project.newFile(scTitle, hNovelRoot)
|
||||
aDoc = project.storage.getDocument(sHandle)
|
||||
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
||||
|
||||
# Create notes folders
|
||||
noteTitles = {
|
||||
nwItemClass.PLOT: self.tr("Main Plot"),
|
||||
nwItemClass.CHARACTER: self.tr("Protagonist"),
|
||||
nwItemClass.WORLD: self.tr("Main Location"),
|
||||
}
|
||||
|
||||
addNotes = data.get("addNotes", False)
|
||||
for newRoot in data.get("addRoots", []):
|
||||
if newRoot in nwItemClass:
|
||||
rHandle = project.newRoot(newRoot)
|
||||
if addNotes:
|
||||
aHandle = project.newFile(noteTitles[newRoot], rHandle)
|
||||
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
|
||||
aDoc = project.storage.getDocument(aHandle)
|
||||
aDoc.writeDocument(f"# {noteTitles[newRoot]}\n\n@tag: {ntTag}\n\n")
|
||||
|
||||
# Also add the archive and trash folders
|
||||
project.newRoot(nwItemClass.ARCHIVE)
|
||||
project.trashFolder()
|
||||
|
||||
project.saveProject()
|
||||
project.closeProject()
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _extractSampleProject(self, data):
|
||||
"""Make a copy of the sample project by extracting the
|
||||
sample.zip file to the new path.
|
||||
"""
|
||||
projPath = data.get("projPath", None)
|
||||
if projPath is None:
|
||||
logger.error("No project path set for the example project")
|
||||
return False
|
||||
|
||||
pkgSample = os.path.join(self.mainConf.assetPath, "sample.zip")
|
||||
if os.path.isfile(pkgSample):
|
||||
try:
|
||||
shutil.unpack_archive(pkgSample, projPath)
|
||||
except Exception as exc:
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to create a new example project."
|
||||
), nwAlert.ERROR, exception=exc)
|
||||
return False
|
||||
|
||||
else:
|
||||
self.mainGui.makeAlert(self.tr(
|
||||
"Failed to create a new example project. "
|
||||
"Could not find the necessary files. "
|
||||
"They seem to be missing from this installation."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# END Class ProjectBuilder
|
||||
@@ -1,244 +0,0 @@
|
||||
"""
|
||||
novelWriter – Project Document Tools
|
||||
====================================
|
||||
A collection of tools to create and manipulate documents
|
||||
|
||||
File History:
|
||||
Created: 2022-10-02 [2.0b1] DocMerger
|
||||
Created: 2022-10-11 [2.0b1] DocSplitter
|
||||
|
||||
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 logging
|
||||
|
||||
from novelwriter.common import minmax
|
||||
from novelwriter.core.document import NWDoc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DocMerger:
|
||||
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.theProject = theProject
|
||||
|
||||
self._error = ""
|
||||
self._targetDoc = None
|
||||
self._targetText = []
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def getError(self):
|
||||
"""Return any collected errors.
|
||||
"""
|
||||
return self._error
|
||||
|
||||
def setTargetDoc(self, tHandle):
|
||||
"""Set the target document for the merging. Calling this
|
||||
function resets the class.
|
||||
"""
|
||||
self._targetDoc = tHandle
|
||||
self._targetText = []
|
||||
return
|
||||
|
||||
def newTargetDoc(self, srcHandle, docLabel):
|
||||
"""Create a barnd new target document based on a source handle
|
||||
and a new doc label. Calling this function resets the class.
|
||||
"""
|
||||
srcItem = self.theProject.tree[srcHandle]
|
||||
if srcItem is None:
|
||||
return None
|
||||
|
||||
newHandle = self.theProject.newFile(docLabel, srcItem.itemParent)
|
||||
newItem = self.theProject.tree[newHandle]
|
||||
newItem.setLayout(srcItem.itemLayout)
|
||||
newItem.setStatus(srcItem.itemStatus)
|
||||
newItem.setImport(srcItem.itemImport)
|
||||
|
||||
self._targetDoc = newHandle
|
||||
self._targetText = []
|
||||
|
||||
return newHandle
|
||||
|
||||
def appendText(self, srcHandle, addComment, cmtPrefix):
|
||||
"""Append text from an existing document to the text buffer.
|
||||
"""
|
||||
srcItem = self.theProject.tree[srcHandle]
|
||||
if srcItem is None:
|
||||
return False
|
||||
|
||||
inDoc = NWDoc(self.theProject, srcHandle)
|
||||
docText = (inDoc.readDocument() or "").rstrip("\n")
|
||||
|
||||
if addComment:
|
||||
docInfo = srcItem.describeMe()
|
||||
docSt, _ = srcItem.getImportStatus(incIcon=False)
|
||||
cmtLine = f"% {cmtPrefix} {docInfo}: {srcItem.itemName} [{docSt}]\n\n"
|
||||
docText = cmtLine + docText
|
||||
|
||||
self._targetText.append(docText)
|
||||
|
||||
return True
|
||||
|
||||
def writeTargetDoc(self):
|
||||
"""Write the accumulated text into the designated target
|
||||
document, appending any existing text.
|
||||
"""
|
||||
if self._targetDoc is None:
|
||||
return False
|
||||
|
||||
outDoc = NWDoc(self.theProject, self._targetDoc)
|
||||
docText = (outDoc.readDocument() or "").rstrip("\n")
|
||||
if docText:
|
||||
self._targetText.insert(0, docText)
|
||||
|
||||
status = outDoc.writeDocument("\n\n".join(self._targetText) + "\n\n")
|
||||
if not status:
|
||||
self._error = outDoc.getError()
|
||||
|
||||
return status
|
||||
|
||||
# END Class DocMerger
|
||||
|
||||
|
||||
class DocSplitter:
|
||||
|
||||
def __init__(self, theProject, sHandle):
|
||||
|
||||
self.theProject = theProject
|
||||
|
||||
self._error = ""
|
||||
self._parHandle = None
|
||||
self._srcHandle = None
|
||||
self._srcItem = None
|
||||
|
||||
self._inFolder = False
|
||||
self._rawData = []
|
||||
|
||||
srcItem = self.theProject.tree[sHandle]
|
||||
if srcItem is not None and srcItem.isFileType():
|
||||
self._srcHandle = sHandle
|
||||
self._srcItem = srcItem
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def getError(self):
|
||||
"""Return any collected errors.
|
||||
"""
|
||||
return self._error
|
||||
|
||||
def setParentItem(self, pHandle):
|
||||
"""Set the item that will be the top level parent item for the
|
||||
new documents.
|
||||
"""
|
||||
self._parHandle = pHandle
|
||||
self._inFolder = False
|
||||
return
|
||||
|
||||
def newParentFolder(self, pHandle, folderLabel):
|
||||
"""Create a new folder that will be the top level parent item
|
||||
for the new documents.
|
||||
"""
|
||||
if self._srcItem is None:
|
||||
return None
|
||||
|
||||
newHandle = self.theProject.newFolder(folderLabel, pHandle)
|
||||
newItem = self.theProject.tree[newHandle]
|
||||
newItem.setStatus(self._srcItem.itemStatus)
|
||||
newItem.setImport(self._srcItem.itemImport)
|
||||
|
||||
self._parHandle = newHandle
|
||||
self._inFolder = True
|
||||
|
||||
return newHandle
|
||||
|
||||
def splitDocument(self, splitData, splitText):
|
||||
"""Loop through the split data record and perform the split job.
|
||||
"""
|
||||
self._rawData = []
|
||||
buffer = splitText.copy()
|
||||
for lineNo, hLevel, hLabel in reversed(splitData):
|
||||
chunk = buffer[lineNo:]
|
||||
buffer = buffer[:lineNo]
|
||||
self._rawData.insert(0, (chunk, hLevel, hLabel))
|
||||
|
||||
return True
|
||||
|
||||
def writeDocuments(self, docHierarchy):
|
||||
"""An iterator that will write each document in the buffer, and
|
||||
return its new handle, parent handle, and sibling handle.
|
||||
"""
|
||||
if self._srcHandle is None or self._srcItem is None:
|
||||
return
|
||||
|
||||
pHandle = self._parHandle
|
||||
nHandle = self._parHandle if self._inFolder else self._srcHandle
|
||||
hHandle = [self._parHandle, None, None, None, None]
|
||||
|
||||
pLevel = 0
|
||||
for docText, hLevel, docLabel in self._rawData:
|
||||
|
||||
hLevel = minmax(hLevel, 1, 4)
|
||||
if pLevel == 0:
|
||||
pLevel = hLevel
|
||||
|
||||
if docHierarchy:
|
||||
if hLevel == 1:
|
||||
pHandle = self._parHandle
|
||||
elif hLevel == 2:
|
||||
pHandle = hHandle[1] or hHandle[0]
|
||||
elif hLevel == 3:
|
||||
pHandle = hHandle[2] or hHandle[1] or hHandle[0]
|
||||
elif hLevel == 4:
|
||||
pHandle = hHandle[3] or hHandle[2] or hHandle[1] or hHandle[0]
|
||||
|
||||
if hLevel < pLevel:
|
||||
nHandle = hHandle[hLevel] or hHandle[0]
|
||||
elif hLevel > pLevel:
|
||||
nHandle = pHandle
|
||||
|
||||
dHandle = self.theProject.newFile(docLabel, pHandle)
|
||||
hHandle[hLevel] = dHandle
|
||||
|
||||
newItem = self.theProject.tree[dHandle]
|
||||
newItem.setStatus(self._srcItem.itemStatus)
|
||||
newItem.setImport(self._srcItem.itemImport)
|
||||
|
||||
outDoc = NWDoc(self.theProject, dHandle)
|
||||
status = outDoc.writeDocument("\n".join(docText))
|
||||
if not status:
|
||||
self._error = outDoc.getError()
|
||||
|
||||
yield status, dHandle, nHandle
|
||||
|
||||
hHandle[hLevel] = dHandle
|
||||
nHandle = dHandle
|
||||
pLevel = hLevel
|
||||
|
||||
return
|
||||
|
||||
# END Class DocSplitter
|
||||
@@ -23,9 +23,10 @@ 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
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.enum import nwItemLayout, nwItemClass
|
||||
from novelwriter.error import formatException
|
||||
from novelwriter.common import isHandle, sha256sum
|
||||
@@ -33,7 +34,7 @@ from novelwriter.common import isHandle, sha256sum
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWDoc:
|
||||
class NWDocument:
|
||||
|
||||
def __init__(self, theProject, theHandle):
|
||||
|
||||
@@ -57,7 +58,7 @@ class NWDoc:
|
||||
return
|
||||
|
||||
def __repr__(self):
|
||||
return f"<NWDoc handle={self._docHandle}>"
|
||||
return f"<NWDocument handle={self._docHandle}>"
|
||||
|
||||
def __bool__(self):
|
||||
return self._docHandle is not None and bool(self._theItem)
|
||||
@@ -73,7 +74,7 @@ class NWDoc:
|
||||
empty string. If something went wrong, return None.
|
||||
"""
|
||||
self._docError = ""
|
||||
if self._docHandle is None:
|
||||
if not isinstance(self._docHandle, str):
|
||||
logger.error("No document handle set")
|
||||
return None
|
||||
|
||||
@@ -81,17 +82,22 @@ class NWDoc:
|
||||
logger.error("Unknown novelWriter document")
|
||||
return None
|
||||
|
||||
contentPath = self.theProject.storage.contentPath
|
||||
if not isinstance(contentPath, Path):
|
||||
logger.error("No content path set")
|
||||
return None
|
||||
|
||||
docFile = self._docHandle+".nwd"
|
||||
logger.debug("Opening document: %s", docFile)
|
||||
|
||||
docPath = os.path.join(self.theProject.projContent, docFile)
|
||||
docPath = contentPath / docFile
|
||||
self._fileLoc = docPath
|
||||
|
||||
theText = ""
|
||||
self._docMeta = {}
|
||||
self._prevHash = None
|
||||
|
||||
if os.path.isfile(docPath):
|
||||
if docPath.exists():
|
||||
self._prevHash = sha256sum(docPath)
|
||||
try:
|
||||
with open(docPath, mode="r", encoding="utf-8") as inFile:
|
||||
@@ -125,17 +131,20 @@ class NWDoc:
|
||||
if not.
|
||||
"""
|
||||
self._docError = ""
|
||||
if self._docHandle is None:
|
||||
if not isinstance(self._docHandle, str):
|
||||
logger.error("No document handle set")
|
||||
return False
|
||||
|
||||
self.theProject.ensureFolderStructure()
|
||||
contentPath = self.theProject.storage.contentPath
|
||||
if not isinstance(contentPath, Path):
|
||||
logger.error("No content path set")
|
||||
return False
|
||||
|
||||
docFile = self._docHandle+".nwd"
|
||||
logger.debug("Saving document: %s", docFile)
|
||||
|
||||
docPath = os.path.join(self.theProject.projContent, docFile)
|
||||
docTemp = os.path.join(self.theProject.projContent, docFile+"~")
|
||||
docPath = contentPath / docFile
|
||||
docTemp = docPath.with_suffix(".tmp")
|
||||
|
||||
if self._prevHash is not None and not forceWrite:
|
||||
self._currHash = sha256sum(docPath)
|
||||
@@ -164,7 +173,7 @@ class NWDoc:
|
||||
# If we're here, the file was successfully saved, so we can
|
||||
# replace the temp file with the actual file
|
||||
try:
|
||||
os.replace(docTemp, docPath)
|
||||
docTemp.replace(docPath)
|
||||
except OSError as exc:
|
||||
self._docError = formatException(exc)
|
||||
return False
|
||||
@@ -179,23 +188,28 @@ class NWDoc:
|
||||
from the project data folder.
|
||||
"""
|
||||
self._docError = ""
|
||||
if self._docHandle is None:
|
||||
if not isinstance(self._docHandle, str):
|
||||
logger.error("No document handle set")
|
||||
return False
|
||||
|
||||
chkList = [
|
||||
os.path.join(self.theProject.projContent, f"{self._docHandle}.nwd"),
|
||||
os.path.join(self.theProject.projContent, f"{self._docHandle}.nwd~"),
|
||||
]
|
||||
contentPath = self.theProject.storage.contentPath
|
||||
if not isinstance(contentPath, Path):
|
||||
logger.error("No content path set")
|
||||
return False
|
||||
|
||||
for chkFile in chkList:
|
||||
if os.path.isfile(chkFile):
|
||||
try:
|
||||
os.unlink(chkFile)
|
||||
logger.debug("Deleted: %s", chkFile)
|
||||
except Exception as exc:
|
||||
self._docError = formatException(exc)
|
||||
return False
|
||||
docPath = contentPath / f"{self._docHandle}.nwd"
|
||||
docTemp = docPath.with_suffix(".tmp")
|
||||
|
||||
try:
|
||||
# ToDo: When Python 3.7 is dropped, these can be changed to
|
||||
# path.unlink(missing_ok=True)
|
||||
if docPath.exists():
|
||||
docPath.unlink()
|
||||
if docTemp.exists():
|
||||
docTemp.unlink()
|
||||
except Exception as exc:
|
||||
self._docError = formatException(exc)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -206,7 +220,7 @@ class NWDoc:
|
||||
def getFileLocation(self):
|
||||
"""Return the file location of the current document.
|
||||
"""
|
||||
return self._fileLoc
|
||||
return str(self._fileLoc)
|
||||
|
||||
def getCurrentItem(self):
|
||||
"""Return a pointer to the currently open NWItem.
|
||||
@@ -263,4 +277,4 @@ class NWDoc:
|
||||
|
||||
return
|
||||
|
||||
# END Class NWDoc
|
||||
# END Class NWDocument
|
||||
|
||||
@@ -26,16 +26,15 @@ 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 json
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.enum import nwItemType, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.constants import nwFiles, nwKeyWords, nwUnicode, nwHeaders
|
||||
from novelwriter.core.document import NWDoc
|
||||
from novelwriter.common import (
|
||||
checkInt, isHandle, isItemClass, isTitleTag, jsonEncode
|
||||
)
|
||||
@@ -118,7 +117,7 @@ class NWIndex:
|
||||
return False
|
||||
|
||||
logger.debug("Re-indexing item '%s'", tHandle)
|
||||
theDoc = NWDoc(self.theProject, tHandle)
|
||||
theDoc = self.theProject.storage.getDocument(tHandle)
|
||||
self.scanText(tHandle, theDoc.readDocument() or "")
|
||||
|
||||
return True
|
||||
@@ -141,12 +140,15 @@ class NWIndex:
|
||||
def loadIndex(self):
|
||||
"""Load index from last session from the project meta folder.
|
||||
"""
|
||||
indexFile = self.theProject.storage.getMetaFile(nwFiles.INDEX_FILE)
|
||||
if not isinstance(indexFile, Path):
|
||||
return False
|
||||
|
||||
theData = {}
|
||||
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||
tStart = time()
|
||||
|
||||
self._indexBroken = False
|
||||
if os.path.isfile(indexFile):
|
||||
if indexFile.exists():
|
||||
logger.debug("Loading index file")
|
||||
try:
|
||||
with open(indexFile, mode="r", encoding="utf-8") as inFile:
|
||||
@@ -184,8 +186,11 @@ class NWIndex:
|
||||
"""Save the current index as a json file in the project meta
|
||||
data folder.
|
||||
"""
|
||||
indexFile = self.theProject.storage.getMetaFile(nwFiles.INDEX_FILE)
|
||||
if not isinstance(indexFile, Path):
|
||||
return False
|
||||
|
||||
logger.debug("Saving index file")
|
||||
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||
tStart = time()
|
||||
|
||||
try:
|
||||
@@ -815,8 +820,6 @@ class ItemIndex:
|
||||
elif tItem.itemRoot == rootHandle:
|
||||
for sTitle in self._items[tHandle].headings():
|
||||
yield tHandle, sTitle, self._items[tHandle][sTitle]
|
||||
else:
|
||||
continue
|
||||
|
||||
return
|
||||
|
||||
|
||||
+26
-21
@@ -27,7 +27,7 @@ import logging
|
||||
|
||||
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
|
||||
from novelwriter.common import (
|
||||
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified
|
||||
checkInt, isHandle, isItemClass, isItemLayout, isItemType, simplified, yesNo
|
||||
)
|
||||
from novelwriter.constants import nwHeaders, nwLabels, trConst
|
||||
|
||||
@@ -162,7 +162,7 @@ class NWItem:
|
||||
item["order"] = str(self._order)
|
||||
item["type"] = str(self._type.name)
|
||||
item["class"] = str(self._class.name)
|
||||
meta["expanded"] = str(self._expanded)
|
||||
meta["expanded"] = yesNo(self._expanded)
|
||||
name["status"] = str(self._status)
|
||||
name["import"] = str(self._import)
|
||||
|
||||
@@ -173,7 +173,7 @@ class NWItem:
|
||||
meta["wordCount"] = str(self._wordCount)
|
||||
meta["paraCount"] = str(self._paraCount)
|
||||
meta["cursorPos"] = str(self._cursorPos)
|
||||
name["active"] = str(self._active)
|
||||
name["active"] = yesNo(self._active)
|
||||
|
||||
data = {
|
||||
"name": str(self._name),
|
||||
@@ -187,29 +187,34 @@ class NWItem:
|
||||
def unpack(self, data):
|
||||
"""Set the values from a data dictionary.
|
||||
"""
|
||||
if "handle" in data:
|
||||
self.setHandle(data["handle"])
|
||||
item = data.get("itemAttr", {})
|
||||
meta = data.get("metaAttr", {})
|
||||
name = data.get("nameAttr", {})
|
||||
|
||||
if "handle" in item:
|
||||
self.setHandle(item["handle"])
|
||||
else:
|
||||
logger.error("Item does not have a handle")
|
||||
return False
|
||||
|
||||
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))
|
||||
self.setName(data.get("name", ""))
|
||||
self.setParent(item.get("parent", None))
|
||||
self.setRoot(item.get("root", None))
|
||||
self.setOrder(item.get("order", 0))
|
||||
self.setType(item.get("type", nwItemType.NO_TYPE))
|
||||
self.setClass(item.get("class", nwItemClass.NO_CLASS))
|
||||
self.setExpanded(meta.get("expanded", False))
|
||||
self.setStatus(name.get("status", None))
|
||||
self.setImport(name.get("import", None))
|
||||
|
||||
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))
|
||||
if self._type == nwItemType.FILE:
|
||||
self.setLayout(item.get("layout", nwItemLayout.NO_LAYOUT))
|
||||
self.setMainHeading(meta.get("heading", "H0"))
|
||||
self.setCharCount(meta.get("charCount", 0))
|
||||
self.setWordCount(meta.get("wordCount", 0))
|
||||
self.setParaCount(meta.get("paraCount", 0))
|
||||
self.setCursorPos(meta.get("cursorPos", 0))
|
||||
self.setActive(name.get("active", True))
|
||||
|
||||
# Make some checks to ensure consistency
|
||||
if self._type == nwItemType.ROOT:
|
||||
|
||||
@@ -24,11 +24,11 @@ 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 json
|
||||
import logging
|
||||
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import checkBool, checkFloat, checkInt, checkString
|
||||
@@ -77,13 +77,12 @@ class OptionState:
|
||||
def loadSettings(self):
|
||||
"""Load the options dictionary from the project settings file.
|
||||
"""
|
||||
if self.theProject.projMeta is None:
|
||||
stateFile = self.theProject.storage.getMetaFile(nwFiles.OPTS_FILE)
|
||||
if not isinstance(stateFile, Path):
|
||||
return False
|
||||
|
||||
stateFile = os.path.join(self.theProject.projMeta, nwFiles.OPTS_FILE)
|
||||
theState = {}
|
||||
|
||||
if os.path.isfile(stateFile):
|
||||
if stateFile.exists():
|
||||
logger.debug("Loading GUI options file")
|
||||
try:
|
||||
with open(stateFile, mode="r", encoding="utf-8") as inFile:
|
||||
@@ -106,12 +105,11 @@ class OptionState:
|
||||
def saveSettings(self):
|
||||
"""Save the options dictionary to the project settings file.
|
||||
"""
|
||||
if self.theProject.projMeta is None:
|
||||
stateFile = self.theProject.storage.getMetaFile(nwFiles.OPTS_FILE)
|
||||
if not isinstance(stateFile, Path):
|
||||
return False
|
||||
|
||||
stateFile = os.path.join(self.theProject.projMeta, nwFiles.OPTS_FILE)
|
||||
logger.debug("Saving GUI options file")
|
||||
|
||||
try:
|
||||
with open(stateFile, mode="w+", encoding="utf-8") as outFile:
|
||||
json.dump(self._theState, outFile, indent=2)
|
||||
|
||||
+154
-907
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,354 @@
|
||||
"""
|
||||
novelWriter – Project Data Class
|
||||
================================
|
||||
Data class for novelWriter projects
|
||||
|
||||
File History:
|
||||
Created: 2022-10-30 [2.0rc1]
|
||||
|
||||
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/>.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
import logging
|
||||
|
||||
from novelwriter.common import (
|
||||
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
|
||||
)
|
||||
from novelwriter.core.status import NWStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWProjectData:
|
||||
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.theProject = theProject
|
||||
|
||||
# Project Meta
|
||||
self._uuid = ""
|
||||
self._name = ""
|
||||
self._title = ""
|
||||
self._authors = []
|
||||
self._saveCount = 0
|
||||
self._autoCount = 0
|
||||
self._editTime = 0
|
||||
|
||||
# Project Settings
|
||||
self._doBackup = True
|
||||
self._language = None
|
||||
self._spellCheck = False
|
||||
self._spellLang = None
|
||||
|
||||
# Project Dictionaries
|
||||
self._initCounts = [0, 0]
|
||||
self._currCounts = [0, 0]
|
||||
self._lastHandle: dict[str, str | None] = {
|
||||
"editor": None,
|
||||
"viewer": None,
|
||||
"novelTree": None,
|
||||
"outline": None,
|
||||
}
|
||||
self._autoReplace: dict[str, str] = {}
|
||||
self._titleFormat: dict[str, str] = {
|
||||
"title": "%title%",
|
||||
"chapter": "%title%",
|
||||
"unnumbered": "%title%",
|
||||
"scene": "* * *",
|
||||
"section": "",
|
||||
}
|
||||
|
||||
self._status = NWStatus(NWStatus.STATUS)
|
||||
self._import = NWStatus(NWStatus.IMPORT)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def uuid(self):
|
||||
return self._uuid
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self._title
|
||||
|
||||
@property
|
||||
def authors(self):
|
||||
return self._authors
|
||||
|
||||
@property
|
||||
def saveCount(self):
|
||||
return self._saveCount
|
||||
|
||||
@property
|
||||
def autoCount(self):
|
||||
return self._autoCount
|
||||
|
||||
@property
|
||||
def editTime(self):
|
||||
return self._editTime
|
||||
|
||||
@property
|
||||
def doBackup(self):
|
||||
return self._doBackup
|
||||
|
||||
@property
|
||||
def language(self):
|
||||
return self._language
|
||||
|
||||
@property
|
||||
def spellCheck(self):
|
||||
return self._spellCheck
|
||||
|
||||
@property
|
||||
def spellLang(self):
|
||||
return self._spellLang
|
||||
|
||||
@property
|
||||
def initCounts(self):
|
||||
return tuple(self._initCounts)
|
||||
|
||||
@property
|
||||
def currCounts(self):
|
||||
return tuple(self._currCounts)
|
||||
|
||||
@property
|
||||
def lastHandle(self):
|
||||
return self._lastHandle
|
||||
|
||||
@property
|
||||
def autoReplace(self):
|
||||
return self._autoReplace
|
||||
|
||||
@property
|
||||
def titleFormat(self):
|
||||
return self._titleFormat
|
||||
|
||||
@property
|
||||
def itemStatus(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def itemImport(self):
|
||||
return self._import
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def addAuthor(self, value):
|
||||
"""Add an author to the authors list.
|
||||
"""
|
||||
self._authors.append(simplified(str(value)))
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def incSaveCount(self):
|
||||
"""Increment the save count by one.
|
||||
"""
|
||||
self._saveCount += 1
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def incAutoCount(self):
|
||||
"""Increment the auto save count by one.
|
||||
"""
|
||||
self._autoCount += 1
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
##
|
||||
# Getters
|
||||
##
|
||||
|
||||
def getLastHandle(self, component):
|
||||
"""Retrieve the last used handle for a given component.
|
||||
"""
|
||||
return self._lastHandle.get(component, None)
|
||||
|
||||
def getTitleFormat(self, kind):
|
||||
"""Retrieve the title format string for a given kind of header.
|
||||
"""
|
||||
return self._titleFormat.get(kind, "%title%")
|
||||
|
||||
##
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setUuid(self, value):
|
||||
"""Set the project id.
|
||||
"""
|
||||
value = checkUuid(value, "")
|
||||
if not value:
|
||||
self._uuid = str(uuid.uuid4())
|
||||
elif value != self._uuid:
|
||||
self._uuid = value
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setName(self, value):
|
||||
"""Set a new project name.
|
||||
"""
|
||||
if value != self._name:
|
||||
self._name = simplified(str(value))
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setTitle(self, value):
|
||||
"""Set a new novel title.
|
||||
"""
|
||||
if value != self._title:
|
||||
self._title = simplified(str(value))
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setAuthors(self, value):
|
||||
"""Set the list of authors from either a string with one author
|
||||
per line, or a list of authors.
|
||||
"""
|
||||
self._authors = []
|
||||
self.theProject.setProjectChanged(True)
|
||||
if isinstance(value, str):
|
||||
for author in value.splitlines():
|
||||
author = simplified(author)
|
||||
if author:
|
||||
self._authors.append(author)
|
||||
self.theProject.setProjectChanged(True)
|
||||
elif isinstance(value, list):
|
||||
self._authors = value
|
||||
return
|
||||
|
||||
def setSaveCount(self, value):
|
||||
"""Set the save count from last session.
|
||||
"""
|
||||
self._saveCount = checkInt(value, 0)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setAutoCount(self, value):
|
||||
"""Set the auto save count from last session.
|
||||
"""
|
||||
self._autoCount = checkInt(value, 0)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setEditTime(self, value):
|
||||
"""Set tyje edit time from last session.
|
||||
"""
|
||||
self._editTime = checkInt(value, 0)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setDoBackup(self, value):
|
||||
"""Set the do write backup flag.
|
||||
"""
|
||||
if value != self._doBackup:
|
||||
self._doBackup = checkBool(value, False)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setLanguage(self, value):
|
||||
"""Set the project language.
|
||||
"""
|
||||
if value != self._language:
|
||||
self._language = checkStringNone(value, None)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setSpellCheck(self, value):
|
||||
"""Set the spell check flag.
|
||||
"""
|
||||
if value != self._spellCheck:
|
||||
self._spellCheck = checkBool(value, False)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setSpellLang(self, value):
|
||||
"""Set the spell check language.
|
||||
"""
|
||||
if value != self._spellLang:
|
||||
self._spellLang = checkStringNone(value, None)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setLastHandle(self, value, component=None):
|
||||
"""Set a last used handle into the handle registry. If component
|
||||
is None, the value is assumed to be the whole dictionary of
|
||||
values.
|
||||
"""
|
||||
if isinstance(component, str):
|
||||
self._lastHandle[component] = checkStringNone(value, None)
|
||||
self.theProject.setProjectChanged(True)
|
||||
elif isinstance(value, dict):
|
||||
for key, entry in value.items():
|
||||
if key in self._lastHandle:
|
||||
self._lastHandle[key] = str(entry) if isHandle(entry) else None
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setInitCounts(self, novel=None, notes=None):
|
||||
"""Set the worc count totals for novel and note files.
|
||||
"""
|
||||
if novel is not None:
|
||||
self._initCounts[0] = checkInt(novel, 0)
|
||||
self._currCounts[0] = checkInt(novel, 0)
|
||||
if notes is not None:
|
||||
self._initCounts[1] = checkInt(notes, 0)
|
||||
self._currCounts[1] = checkInt(notes, 0)
|
||||
return
|
||||
|
||||
def setCurrCounts(self, novel=None, notes=None):
|
||||
"""Set the worc count totals for novel and note files.
|
||||
"""
|
||||
if novel is not None:
|
||||
self._currCounts[0] = checkInt(novel, 0)
|
||||
if notes is not None:
|
||||
self._currCounts[1] = checkInt(notes, 0)
|
||||
return
|
||||
|
||||
def setAutoReplace(self, value):
|
||||
"""Set the auto-replace dictionary.
|
||||
"""
|
||||
if isinstance(value, dict):
|
||||
self._autoReplace = {}
|
||||
for key, entry in value.items():
|
||||
if isinstance(entry, str):
|
||||
self._autoReplace[key] = simplified(entry)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
def setTitleFormat(self, value):
|
||||
"""Set the title formats.
|
||||
"""
|
||||
if isinstance(value, dict):
|
||||
for key, entry in value.items():
|
||||
if key in self._titleFormat and isinstance(entry, str):
|
||||
self._titleFormat[key] = simplified(entry)
|
||||
self.theProject.setProjectChanged(True)
|
||||
return
|
||||
|
||||
# END Class NWProjectData
|
||||
+214
-160
@@ -24,29 +24,32 @@ 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 pathlib import Path
|
||||
|
||||
from novelwriter.common import (
|
||||
checkBool, checkInt, checkStringNone, formatTimeStamp, simplified, checkString
|
||||
checkBool, checkInt, checkString, checkStringNone, formatTimeStamp,
|
||||
hexToInt, simplified, yesNo
|
||||
)
|
||||
from novelwriter.constants import nwFiles
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
FILE_VERSION = "1.4" # The current project file format version
|
||||
FILE_VERSION = "1.5" # The current project file format version
|
||||
HEX_VERSION = 0x0105
|
||||
|
||||
NUM_VERSION = {
|
||||
"1.0": 0x0100,
|
||||
"1.1": 0x0101,
|
||||
"1.2": 0x0102,
|
||||
"1.3": 0x0103,
|
||||
"1.4": 0x0104,
|
||||
"1.0": 0x0100, # Up to 0.7
|
||||
"1.1": 0x0101, # Up to 0.10
|
||||
"1.2": 0x0102, # Up to 1.5
|
||||
"1.3": 0x0103, # Up to 2.0 Beta 1
|
||||
"1.4": 0x0104, # Up to 2.0 RC 2
|
||||
"1.5": 0x0105, # Current
|
||||
}
|
||||
|
||||
|
||||
@@ -68,36 +71,42 @@ class ProjectXMLReader:
|
||||
"""The main project XML file reader class. All data is read into a
|
||||
NWProjectData instance, which must be provided.
|
||||
|
||||
Version Change History
|
||||
======================
|
||||
File Format 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.2 Changes the way autoReplace entries are stored. 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.
|
||||
way satus and importance labels are stored. This format was only
|
||||
a part of version 2.0 RC 1
|
||||
|
||||
1.5 The actual format released for 2.0. It moves last used handles
|
||||
and title formats into a key/value format similar to auto-
|
||||
replace, status and imporetance. It adds the heading value to
|
||||
the content item meta entry. It also moves meta data related to
|
||||
the project or the content into their respective section nodes
|
||||
as attributes. The id attribute was also added to the project.
|
||||
"""
|
||||
|
||||
def __init__(self, path):
|
||||
|
||||
self._path = path
|
||||
self._path = Path(path)
|
||||
self._state = XMLReadState.NO_ACTION
|
||||
|
||||
self._root = ""
|
||||
self._version = 0x0000
|
||||
self._version = 0x0
|
||||
self._appVersion = ""
|
||||
self._hexVersion = ""
|
||||
self._hexVersion = 0x0
|
||||
self._timeStamp = ""
|
||||
|
||||
return
|
||||
@@ -153,22 +162,22 @@ class ProjectXMLReader:
|
||||
logger.debug("Reading project XML")
|
||||
|
||||
try:
|
||||
xml = etree.parse(self._path)
|
||||
xml = etree.parse(str(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)
|
||||
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):
|
||||
backFile = self._path.with_suffix(".bak")
|
||||
if backFile.is_file():
|
||||
try:
|
||||
xml = etree.parse(backFile)
|
||||
xml = etree.parse(str(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)
|
||||
logger.error("Failed to parse backup project XML", exc_info=exc)
|
||||
self._state = XMLReadState.CANNOT_PARSE
|
||||
return False
|
||||
else:
|
||||
@@ -191,7 +200,7 @@ class ProjectXMLReader:
|
||||
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._hexVersion = hexToInt(xRoot.attrib.get("hexVersion", ""))
|
||||
self._timeStamp = str(xRoot.attrib.get("timeStamp", ""))
|
||||
|
||||
for xSection in xRoot:
|
||||
@@ -201,13 +210,13 @@ class ProjectXMLReader:
|
||||
self._parseProjectSettings(xSection, projData)
|
||||
elif xSection.tag == "content":
|
||||
if self._version >= 0x0104:
|
||||
self._parseProjectContent(xSection, projContent)
|
||||
self._parseProjectContent(xSection, projData, projContent)
|
||||
else:
|
||||
self._parseProjectContentLegacy(xSection, projContent, projData)
|
||||
self._parseProjectContentLegacy(xSection, projData, projContent)
|
||||
else:
|
||||
logger.warning("Ignored <root/%s> in xml", xSection.tag)
|
||||
logger.warning("Ignored <root/%s> in XML", xSection.tag)
|
||||
|
||||
if self._version == 0x0104:
|
||||
if self._version == HEX_VERSION:
|
||||
self._state = XMLReadState.PARSED_OK
|
||||
else:
|
||||
self._state = XMLReadState.WAS_LEGACY
|
||||
@@ -224,6 +233,12 @@ class ProjectXMLReader:
|
||||
"""Parse the project section of the XML file.
|
||||
"""
|
||||
logger.debug("Parsing <project> section")
|
||||
|
||||
projData.setUuid(xSection.attrib.get("id", None)) # Added in 1.5
|
||||
projData.setSaveCount(xSection.attrib.get("saveCount", 0)) # Moved in 1.5
|
||||
projData.setAutoCount(xSection.attrib.get("autoCount", 0)) # Moved in 1.5
|
||||
projData.setEditTime(xSection.attrib.get("editTime", 0)) # Moved in 1.5
|
||||
|
||||
for xItem in xSection:
|
||||
if xItem.tag == "name":
|
||||
projData.setName(xItem.text)
|
||||
@@ -231,14 +246,18 @@ class ProjectXMLReader:
|
||||
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)
|
||||
logger.warning("Ignored <root/project/%s> in XML", xItem.tag)
|
||||
|
||||
# Deprecated Nodes
|
||||
if self._version < HEX_VERSION:
|
||||
for xItem in xSection:
|
||||
if xItem.tag == "saveCount": # Moved to attribute in 1.5
|
||||
projData.setSaveCount(xItem.text)
|
||||
elif xItem.tag == "autoCount": # Moved to attribute in 1.5
|
||||
projData.setAutoCount(xItem.text)
|
||||
elif xItem.tag == "editTime": # Moved to attribute in 1.5
|
||||
projData.setEditTime(xItem.text)
|
||||
|
||||
return
|
||||
|
||||
@@ -252,17 +271,12 @@ class ProjectXMLReader:
|
||||
projData.setDoBackup(xItem.text)
|
||||
elif xItem.tag == "language":
|
||||
projData.setLanguage(xItem.text)
|
||||
elif xItem.tag == "spellCheck":
|
||||
projData.setSpellCheck(xItem.text)
|
||||
elif xItem.tag == "spellLang":
|
||||
elif xItem.tag == "spellChecking":
|
||||
projData.setSpellLang(xItem.text)
|
||||
elif xItem.tag == "novelWordCount":
|
||||
projData.setInitCounts(novel=xItem.text)
|
||||
elif xItem.tag == "notesWordCount":
|
||||
projData.setInitCounts(notes=xItem.text)
|
||||
projData.setSpellCheck(xItem.attrib.get("auto", False))
|
||||
elif xItem.tag == "status":
|
||||
self._parseStatusImport(xItem, projData.itemStatus)
|
||||
elif xItem.tag in ("import", "importance"):
|
||||
elif xItem.tag == "importance":
|
||||
self._parseStatusImport(xItem, projData.itemImport)
|
||||
elif xItem.tag == "lastHandle":
|
||||
projData.setLastHandle(self._parseDictKeyText(xItem))
|
||||
@@ -272,122 +286,157 @@ class ProjectXMLReader:
|
||||
else: # Pre 1.2 format
|
||||
projData.setAutoReplace(self._parseDictTagText(xItem))
|
||||
elif xItem.tag == "titleFormat":
|
||||
if self._version >= 0x0104:
|
||||
if self._version >= 0x0105:
|
||||
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)
|
||||
logger.warning("Ignored <root/settings/%s> in XML", xItem.tag)
|
||||
|
||||
# Deprecated Nodes
|
||||
if self._version < HEX_VERSION:
|
||||
for xItem in xSection:
|
||||
if xItem.tag == "spellCheck": # Changed to spellChecking in 1.5
|
||||
projData.setSpellCheck(xItem.text)
|
||||
elif xItem.tag == "spellLang": # Changed to spellChecking in 1.5
|
||||
projData.setSpellLang(xItem.text)
|
||||
elif xItem.tag == "novelWordCount": # Moved to content attribute in 1.5
|
||||
projData.setInitCounts(novel=xItem.text)
|
||||
elif xItem.tag == "notesWordCount": # Moved to content attribute in 1.5
|
||||
projData.setInitCounts(notes=xItem.text)
|
||||
|
||||
return
|
||||
|
||||
def _parseProjectContent(self, xSection, projContent):
|
||||
def _parseProjectContent(self, xSection, projData, projContent):
|
||||
"""Parse the content section of the XML file.
|
||||
"""
|
||||
logger.debug("Parsing <content> section")
|
||||
|
||||
projData.setInitCounts(novel=xSection.attrib.get("novelWords", None)) # Moved in 1.5
|
||||
projData.setInitCounts(notes=xSection.attrib.get("notesWords", None)) # Moved in 1.5
|
||||
|
||||
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")
|
||||
if xItem.tag != "item":
|
||||
logger.warning("Ignored item <root/content/%s> in XML", xItem.tag)
|
||||
continue
|
||||
|
||||
item = {}
|
||||
meta = {}
|
||||
name = {}
|
||||
itemName = ""
|
||||
|
||||
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":
|
||||
meta["expanded"] = checkBool(xVal.attrib.get("expanded"), False)
|
||||
meta["heading"] = checkString(xVal.attrib.get("heading"), "H0")
|
||||
meta["charCount"] = checkInt(xVal.attrib.get("charCount"), 0)
|
||||
meta["wordCount"] = checkInt(xVal.attrib.get("wordCount"), 0)
|
||||
meta["paraCount"] = checkInt(xVal.attrib.get("paraCount"), 0)
|
||||
meta["cursorPos"] = checkInt(xVal.attrib.get("cursorPos"), 0)
|
||||
elif xVal.tag == "name":
|
||||
itemName = simplified(checkString(xVal.text, ""))
|
||||
name["status"] = checkStringNone(xVal.attrib.get("status"), None)
|
||||
name["import"] = checkStringNone(xVal.attrib.get("import"), None)
|
||||
name["active"] = checkBool(xVal.attrib.get("active"), False)
|
||||
else:
|
||||
logger.warning("Ignored <root/content/item/%s> in XML", xVal.tag)
|
||||
|
||||
# Deprecated Nodes
|
||||
if self._version < HEX_VERSION:
|
||||
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)
|
||||
if xVal.tag == "name" and "exported" in xVal.attrib:
|
||||
name["active"] = checkBool(xVal.attrib.get("exported"), 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)
|
||||
projContent.append({
|
||||
"name": itemName,
|
||||
"itemAttr": item,
|
||||
"metaAttr": meta,
|
||||
"nameAttr": name,
|
||||
})
|
||||
|
||||
return
|
||||
|
||||
def _parseProjectContentLegacy(self, xSection, projContent, projData):
|
||||
def _parseProjectContentLegacy(self, xSection, projData, projContent):
|
||||
"""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()}
|
||||
statusMap = {entry.get("name"): key for key, entry in projData.itemStatus.items()}
|
||||
importMap = {entry.get("name"): key for key, entry in projData.itemImport.items()}
|
||||
|
||||
for xItem in xSection:
|
||||
if xItem.tag != "item":
|
||||
logger.warning("Ignored item <root/content/%s> in XML", xItem.tag)
|
||||
continue
|
||||
|
||||
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
|
||||
meta = {}
|
||||
name = {}
|
||||
itemName = ""
|
||||
|
||||
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)
|
||||
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)
|
||||
meta["heading"] = "H0" # Value was added in 1.4
|
||||
|
||||
# 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)
|
||||
tmpStatus = ""
|
||||
for xVal in xItem:
|
||||
if xVal.tag == "name":
|
||||
itemName = 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":
|
||||
meta["expanded"] = checkBool(xVal.text, False)
|
||||
elif xVal.tag == "exported": # Renamed to active in 1.5
|
||||
name["active"] = checkBool(xVal.text, False)
|
||||
elif xVal.tag == "charCount":
|
||||
meta["charCount"] = checkInt(xVal.text, 0)
|
||||
elif xVal.tag == "wordCount":
|
||||
meta["wordCount"] = checkInt(xVal.text, 0)
|
||||
elif xVal.tag == "paraCount":
|
||||
meta["paraCount"] = checkInt(xVal.text, 0)
|
||||
elif xVal.tag == "cursorPos":
|
||||
meta["cursorPos"] = checkInt(xVal.text, 0)
|
||||
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)
|
||||
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"):
|
||||
name["status"] = statusMap.get(tmpStatus, None)
|
||||
else:
|
||||
logger.warning("Ignored <root/content/%s> in xml", xItem.tag)
|
||||
name["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 trash type was removed in 1.4
|
||||
if item.get("type", "") == "TRASH":
|
||||
item["type"] = "ROOT"
|
||||
|
||||
projContent.append({
|
||||
"name": itemName,
|
||||
"itemAttr": item,
|
||||
"metaAttr": meta,
|
||||
"nameAttr": name,
|
||||
})
|
||||
|
||||
return
|
||||
|
||||
@@ -418,7 +467,7 @@ class ProjectXMLReader:
|
||||
"""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}
|
||||
return {xNode.tag: checkString(xNode.text, "") for xNode in xItem}
|
||||
|
||||
# END Class ProjectXMLReader
|
||||
|
||||
@@ -427,7 +476,7 @@ class ProjectXMLWriter:
|
||||
|
||||
def __init__(self, path):
|
||||
|
||||
self._path = path
|
||||
self._path = Path(path)
|
||||
self._error = None
|
||||
|
||||
return
|
||||
@@ -458,22 +507,25 @@ class ProjectXMLWriter:
|
||||
})
|
||||
|
||||
# Save Project Meta
|
||||
xProject = etree.SubElement(xRoot, "project")
|
||||
projAttr = {
|
||||
"id": projData.uuid,
|
||||
"saveCount": str(projData.saveCount),
|
||||
"autoCount": str(projData.autoCount),
|
||||
"editTime": str(editTime),
|
||||
}
|
||||
|
||||
xProject = etree.SubElement(xRoot, "project", attrib=projAttr)
|
||||
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, "doBackup", yesNo(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._packSingleValue(xSettings, "spellChecking", projData.spellLang, attrib={
|
||||
"auto": yesNo(projData.spellCheck)
|
||||
})
|
||||
self._packDictKeyValue(xSettings, "lastHandle", projData.lastHandle)
|
||||
self._packDictKeyValue(xSettings, "autoReplace", projData.autoReplace)
|
||||
self._packDictKeyValue(xSettings, "titleFormat", projData.titleFormat)
|
||||
@@ -488,25 +540,27 @@ class ProjectXMLWriter:
|
||||
self._packSingleValue(xImport, "entry", label, attrib=attrib)
|
||||
|
||||
# Save Tree Content
|
||||
xContent = etree.SubElement(xRoot, "content", attrib={"count": str(len(projContent))})
|
||||
contAttr = {
|
||||
"items": str(len(projContent)),
|
||||
"novelWords": str(projData.currCounts[0]),
|
||||
"notesWords": str(projData.currCounts[1]),
|
||||
}
|
||||
|
||||
xContent = etree.SubElement(xRoot, "content", attrib=contAttr)
|
||||
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")
|
||||
# Write the XML tree to file
|
||||
saveFile = self._path / nwFiles.PROJ_FILE
|
||||
tempFile = saveFile.with_suffix(".tmp")
|
||||
backFile = saveFile.with_suffix(".bak")
|
||||
try:
|
||||
with open(tempFile, mode="wb") as outFile:
|
||||
outFile.write(etree.tostring(
|
||||
xRoot,
|
||||
pretty_print=True,
|
||||
encoding="utf-8",
|
||||
xml_declaration=True
|
||||
))
|
||||
tempFile.write_bytes(etree.tostring(
|
||||
xRoot, pretty_print=True, encoding="utf-8", xml_declaration=True
|
||||
))
|
||||
except Exception as exc:
|
||||
self._error = exc
|
||||
return False
|
||||
@@ -514,10 +568,10 @@ class ProjectXMLWriter:
|
||||
# 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:
|
||||
if saveFile.exists():
|
||||
saveFile.replace(backFile)
|
||||
tempFile.replace(saveFile)
|
||||
except Exception as exc:
|
||||
self._error = exc
|
||||
return False
|
||||
|
||||
@@ -530,14 +584,14 @@ class ProjectXMLWriter:
|
||||
##
|
||||
|
||||
def _packSingleValue(self, xParent, name, value, attrib=None):
|
||||
"""Pack a single value into an xml element.
|
||||
"""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.
|
||||
"""Pack a list of values into an XML element.
|
||||
"""
|
||||
for value in data:
|
||||
xItem = etree.SubElement(xParent, name)
|
||||
@@ -545,7 +599,7 @@ class ProjectXMLWriter:
|
||||
return
|
||||
|
||||
def _packDictKeyValue(self, xParent, name, data):
|
||||
"""Pack the entries of a dictionary into an xml element.
|
||||
"""Pack the entries of a dictionary into an XML element.
|
||||
"""
|
||||
xItem = etree.SubElement(xParent, name)
|
||||
for key, value in data.items():
|
||||
|
||||
@@ -23,10 +23,10 @@ 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
|
||||
|
||||
from collections import namedtuple
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.error import logException
|
||||
|
||||
@@ -173,10 +173,10 @@ class NWSpellEnchant:
|
||||
self._projDict = set()
|
||||
self._projectDict = projectDict
|
||||
|
||||
if projectDict is None:
|
||||
if not isinstance(projectDict, Path):
|
||||
return False
|
||||
|
||||
if not os.path.isfile(projectDict):
|
||||
if not projectDict.exists():
|
||||
return False
|
||||
|
||||
try:
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
"""
|
||||
novelWriter – Project Storage Class
|
||||
===================================
|
||||
The main class handling the project storage
|
||||
|
||||
File History:
|
||||
Created: 2022-11-01 [2.0rc1] NWStorage
|
||||
|
||||
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 logging
|
||||
import novelwriter
|
||||
|
||||
from time import time
|
||||
from pathlib import Path
|
||||
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
|
||||
|
||||
from novelwriter.common import minmax
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.core.document import NWDocument
|
||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
|
||||
from novelwriter.error import logException
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWStorage:
|
||||
|
||||
MODE_INACTIVE = 0
|
||||
MODE_INPLACE = 1
|
||||
MODE_ARCHIVE = 2
|
||||
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theProject = theProject
|
||||
|
||||
self._storagePath = None
|
||||
self._runtimePath = None
|
||||
self._lockFilePath = None
|
||||
self._openMode = self.MODE_INACTIVE
|
||||
|
||||
return
|
||||
|
||||
def clear(self):
|
||||
"""Reset internal variables.
|
||||
"""
|
||||
self._storagePath = None
|
||||
self._runtimePath = None
|
||||
self._openMode = self.MODE_INACTIVE
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def storagePath(self):
|
||||
return self._storagePath
|
||||
|
||||
@property
|
||||
def runtimePath(self):
|
||||
return self._runtimePath
|
||||
|
||||
@property
|
||||
def contentPath(self):
|
||||
if self._runtimePath is not None:
|
||||
return self._runtimePath / "content"
|
||||
return None
|
||||
|
||||
##
|
||||
# Core Methods
|
||||
##
|
||||
|
||||
def isOpen(self):
|
||||
"""Check if the storage location is open.
|
||||
"""
|
||||
return self._runtimePath is not None
|
||||
|
||||
def openProjectInPlace(self, path, newProject=False):
|
||||
"""Open a novelWriter project in-place. That is, it is opened
|
||||
directly from a project folder.
|
||||
"""
|
||||
inPath = Path(path).resolve()
|
||||
if inPath.is_file():
|
||||
# The path should not point to an exisitng file,
|
||||
# but it can point to a folder containing files
|
||||
inPath = inPath.parent
|
||||
|
||||
self._storagePath = inPath
|
||||
self._runtimePath = inPath
|
||||
self._lockFilePath = inPath / nwFiles.PROJ_LOCK
|
||||
self._openMode = self.MODE_INPLACE
|
||||
|
||||
if not self._prepareStorage(checkLegacy=True, newProject=newProject):
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def openProjectArchive(self, path): # pragma: no cover
|
||||
pass
|
||||
|
||||
def runPostSaveTasks(self, autoSave=False): # pragma: no cover
|
||||
"""Run tasks after the project has been saved.
|
||||
"""
|
||||
if self._openMode == self.MODE_INPLACE:
|
||||
# Nothing to do, so we just return
|
||||
return True
|
||||
|
||||
return True
|
||||
|
||||
def closeSession(self):
|
||||
"""Run tasks related to closing the session.
|
||||
"""
|
||||
# Clear lockfile
|
||||
self.clear()
|
||||
return
|
||||
|
||||
##
|
||||
# Content Access Methods
|
||||
##
|
||||
|
||||
def getXmlReader(self):
|
||||
"""Return a properly configured ProjectXMLReader instance.
|
||||
"""
|
||||
if self._runtimePath is None:
|
||||
return None
|
||||
|
||||
projFile = self._runtimePath / nwFiles.PROJ_FILE
|
||||
xmlReader = ProjectXMLReader(projFile)
|
||||
|
||||
return xmlReader
|
||||
|
||||
def getXmlWriter(self):
|
||||
"""Return a properly configured ProjectXMLWriter instance.
|
||||
"""
|
||||
if self._runtimePath is None:
|
||||
return None
|
||||
|
||||
xmlWriter = ProjectXMLWriter(self._runtimePath)
|
||||
|
||||
return xmlWriter
|
||||
|
||||
def getDocument(self, tHandle):
|
||||
"""Return a document wrapper object.
|
||||
"""
|
||||
if self._runtimePath is not None:
|
||||
return NWDocument(self.theProject, tHandle)
|
||||
return NWDocument(self.theProject, None)
|
||||
|
||||
def getMetaFile(self, fileName):
|
||||
"""Return the path to a file in the project meta folder.
|
||||
"""
|
||||
if self._runtimePath is not None:
|
||||
return self._runtimePath / "meta" / fileName
|
||||
return None
|
||||
|
||||
def getCacheFile(self, fileName):
|
||||
"""Return the path to a file in the project cache folder.
|
||||
"""
|
||||
if self._runtimePath is not None:
|
||||
return self._runtimePath / "cache" / fileName
|
||||
return None
|
||||
|
||||
def readLockFile(self):
|
||||
"""Read the project lock file.
|
||||
"""
|
||||
if self._lockFilePath is None:
|
||||
return ["ERROR"]
|
||||
|
||||
if not self._lockFilePath.exists():
|
||||
return []
|
||||
|
||||
try:
|
||||
lines = self._lockFilePath.read_text(encoding="utf-8").split(";")
|
||||
except Exception:
|
||||
logger.error("Failed to read project lockfile")
|
||||
logException()
|
||||
return ["ERROR"]
|
||||
|
||||
if len(lines) != 4:
|
||||
return ["ERROR"]
|
||||
|
||||
return lines
|
||||
|
||||
def writeLockFile(self):
|
||||
"""Write the project lock file.
|
||||
"""
|
||||
if self._lockFilePath is None:
|
||||
return False
|
||||
|
||||
data = [
|
||||
self.mainConf.hostName, self.mainConf.osType,
|
||||
self.mainConf.kernelVer, str(int(time()))
|
||||
]
|
||||
try:
|
||||
self._lockFilePath.write_text(";".join(data), encoding="utf-8")
|
||||
except Exception:
|
||||
logger.error("Failed to write project lockfile")
|
||||
logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def clearLockFile(self):
|
||||
"""Remove the lock file, if it exists.
|
||||
"""
|
||||
if self._lockFilePath is None:
|
||||
return False
|
||||
|
||||
if self._lockFilePath.exists():
|
||||
try:
|
||||
self._lockFilePath.unlink()
|
||||
except Exception:
|
||||
logger.error("Failed to remove project lockfile")
|
||||
logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def zipIt(self, target, compression=None):
|
||||
"""Zip the content of the project at its runtime location into a
|
||||
zip file. This process will only grab files that are supposed to
|
||||
be in the project. All non-project files will be left out.
|
||||
"""
|
||||
basePath = self._runtimePath
|
||||
if not isinstance(basePath, Path):
|
||||
logger.error("No path set")
|
||||
return False
|
||||
|
||||
baseMeta = basePath / "meta"
|
||||
baseCont = basePath / "content"
|
||||
files = [
|
||||
(basePath / nwFiles.PROJ_FILE, nwFiles.PROJ_FILE),
|
||||
(baseMeta / nwFiles.OPTS_FILE, f"meta/{nwFiles.OPTS_FILE}"),
|
||||
(baseMeta / nwFiles.SESS_STATS, f"meta/{nwFiles.SESS_STATS}"),
|
||||
(baseMeta / nwFiles.INDEX_FILE, f"meta/{nwFiles.INDEX_FILE}"),
|
||||
(baseMeta / nwFiles.PROJ_DICT, f"meta/{nwFiles.PROJ_DICT}"),
|
||||
]
|
||||
for contItem in baseCont.iterdir():
|
||||
name = contItem.name
|
||||
if contItem.is_file() and len(name) == 17 and name.endswith(".nwd"):
|
||||
files.append((contItem, f"content/{name}"))
|
||||
|
||||
comp = ZIP_STORED if compression is None else ZIP_DEFLATED
|
||||
level = minmax(compression, 0, 9) if isinstance(compression, int) else None
|
||||
try:
|
||||
with ZipFile(target, mode="w", compression=comp, compresslevel=level) as zipObj:
|
||||
logger.info("Creating archive: %s", target)
|
||||
for srcPath, zipPath in files:
|
||||
if srcPath.is_file():
|
||||
zipObj.write(srcPath, zipPath)
|
||||
logger.debug("Added: %s", zipPath)
|
||||
except Exception:
|
||||
logger.error("Failed to create acrhive")
|
||||
logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _prepareStorage(self, checkLegacy=True, newProject=False):
|
||||
"""Prepare the storage area for the project.
|
||||
"""
|
||||
path = self._runtimePath
|
||||
if not isinstance(path, Path):
|
||||
logger.error("No path set")
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
if path == Path.home().absolute():
|
||||
logger.error("Cannot use the user's home path as the root of a project")
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
if newProject:
|
||||
# If it's a new project, we check that there is no existing
|
||||
# project in the selected path.
|
||||
if path.exists() and len(list(path.iterdir())) > 0:
|
||||
logger.error("The new project folder is not empty")
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
# The folder is not required to exist, as it could be a new
|
||||
# project, so we make sure it does. Then we add subfolders.
|
||||
try:
|
||||
path.mkdir(exist_ok=True)
|
||||
(path / "content").mkdir(exist_ok=True)
|
||||
(path / "cache").mkdir(exist_ok=True)
|
||||
(path / "meta").mkdir(exist_ok=True)
|
||||
except Exception as exc:
|
||||
logger.error("Failed to create required project folders", exc_info=exc)
|
||||
self.clear()
|
||||
return False
|
||||
|
||||
if not checkLegacy:
|
||||
# The legacy content check is only needed for project folder
|
||||
# storage, so if it is not expected to be that, there's no
|
||||
# need for the remaning checks.
|
||||
return True
|
||||
|
||||
# Check for legacy data folders
|
||||
for child in path.iterdir():
|
||||
if child.is_dir() and child.name.startswith("data_"):
|
||||
self._legacyDataFolder(path, child)
|
||||
|
||||
# Check for no longer used files, and delete them
|
||||
self._deleteDeprecatedFiles(path)
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Legacy Project Data Handlers
|
||||
##
|
||||
|
||||
def _legacyDataFolder(self, path: Path, child: Path):
|
||||
"""Handle the content of a legacy data folder from a version 1.0
|
||||
project.
|
||||
"""
|
||||
logger.info("Processing legacy data folder: %s", path)
|
||||
|
||||
# Move Documents to Content
|
||||
first = child.name[-1]
|
||||
if first not in "0123456789abcdef":
|
||||
return
|
||||
|
||||
for item in child.iterdir():
|
||||
if not item.is_file():
|
||||
continue
|
||||
|
||||
name = item.name
|
||||
if len(name) == 21 and name.endswith("_main.nwd"):
|
||||
newPath = path / "content" / f"{first}{name[:12]}.nwd"
|
||||
try:
|
||||
item.rename(newPath)
|
||||
logger.info("Moved file: %s", newPath)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to move: %s", item, exc_info=exc)
|
||||
elif len(name) == 21 and name.endswith("_main.bak"):
|
||||
try:
|
||||
item.unlink()
|
||||
logger.info("Deleted file: %s", item)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to delete: %s", item, exc_info=exc)
|
||||
|
||||
# Remove Data Folder
|
||||
try:
|
||||
child.rmdir()
|
||||
logger.info("Deleted folder: %s", child)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to delete: %s", child, exc_info=exc)
|
||||
|
||||
return
|
||||
|
||||
def _deleteDeprecatedFiles(self, path: Path):
|
||||
"""Delete files that are no longer used by novelWriter.
|
||||
"""
|
||||
remove = [
|
||||
path / "meta" / "mainOptions.json", # Replaced in 0.5
|
||||
path / "meta" / "exportOptions.json", # Replaced in 0.5
|
||||
path / "meta" / "outlineOptions.json", # Replaced in 0.5
|
||||
path / "meta" / "timelineOptions.json", # Replaced in 0.5
|
||||
path / "meta" / "docMergeOptions.json", # Replaced in 0.5
|
||||
path / "meta" / "sessionLogOptions.json", # Replaced in 0.5
|
||||
path / "ToC.json", # Dropped in 1.0 RC 1
|
||||
]
|
||||
for item in remove:
|
||||
if item.is_file():
|
||||
try:
|
||||
item.unlink()
|
||||
logger.info("Deleted: %s", item)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to delete: %s", item, exc_info=exc)
|
||||
|
||||
return
|
||||
|
||||
# END Class NWStorage
|
||||
@@ -36,7 +36,6 @@ from PyQt5.QtCore import QCoreApplication, QRegularExpression
|
||||
from novelwriter.enum import nwItemLayout, nwItemType
|
||||
from novelwriter.common import numberToRoman, checkInt
|
||||
from novelwriter.constants import nwConst, nwRegEx, nwUnicode
|
||||
from novelwriter.core.document import NWDoc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -305,7 +304,7 @@ class Tokenizer(ABC):
|
||||
return False
|
||||
|
||||
if theText is None:
|
||||
theText = NWDoc(self.theProject, theHandle).readDocument() or ""
|
||||
theText = self.theProject.storage.getDocument(theHandle).readDocument() or ""
|
||||
|
||||
self._theText = theText
|
||||
|
||||
|
||||
@@ -23,10 +23,11 @@ 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 random
|
||||
import logging
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import checkHandle
|
||||
@@ -140,16 +141,22 @@ class NWTree:
|
||||
"""Write the convenience table of contents file in the root of
|
||||
the project directory.
|
||||
"""
|
||||
runtimePath = self.theProject.storage.runtimePath
|
||||
contentPath = self.theProject.storage.contentPath
|
||||
if not (isinstance(contentPath, Path) and isinstance(runtimePath, Path)):
|
||||
return False
|
||||
|
||||
tocList = []
|
||||
tocLen = 0
|
||||
for tHandle in self._treeOrder:
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if tItem is None:
|
||||
continue
|
||||
|
||||
tFile = tHandle+".nwd"
|
||||
if os.path.isfile(os.path.join(self.theProject.projContent, tFile)):
|
||||
if (contentPath / tFile).is_file():
|
||||
tocLine = "{0:<25s} {1:<9s} {2:<8s} {3:s}".format(
|
||||
os.path.join("content", tFile),
|
||||
str(Path("content") / tFile),
|
||||
tItem.itemClass.name,
|
||||
tItem.itemLayout.name,
|
||||
tItem.itemName,
|
||||
@@ -159,7 +166,7 @@ class NWTree:
|
||||
|
||||
try:
|
||||
# Dump the text
|
||||
tocText = os.path.join(self.theProject.projPath, nwFiles.TOC_TXT)
|
||||
tocText = runtimePath / nwFiles.TOC_TXT
|
||||
with open(tocText, mode="w", encoding="utf-8") as outFile:
|
||||
outFile.write("\n")
|
||||
outFile.write("Table of Contents\n")
|
||||
|
||||
@@ -33,7 +33,6 @@ from PyQt5.QtWidgets import (
|
||||
QListWidgetItem, QDialogButtonBox, QLabel, QGridLayout
|
||||
)
|
||||
|
||||
from novelwriter.core import NWDoc
|
||||
from novelwriter.custom import QHelpLabel, QSwitch
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -204,7 +203,7 @@ class GuiDocSplit(QDialog):
|
||||
|
||||
spLevel = self.splitLevel.currentData()
|
||||
if not self._text:
|
||||
inDoc = NWDoc(self.theProject, sHandle)
|
||||
inDoc = self.theProject.storage.getDocument(sHandle)
|
||||
self._text = (inDoc.readDocument() or "").splitlines()
|
||||
|
||||
for lineNo, aLine in enumerate(self._text):
|
||||
|
||||
@@ -260,7 +260,7 @@ class GuiProjectDetailsMain(QWidget):
|
||||
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)
|
||||
self.projPathVal.setText(str(self.theProject.storage.storagePath))
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -23,10 +23,11 @@ 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 pathlib import Path
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QListWidget,
|
||||
@@ -150,9 +151,11 @@ class GuiWordList(QDialog):
|
||||
"""
|
||||
self._saveGuiSettings()
|
||||
|
||||
dctFile = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
|
||||
tmpFile = dctFile + "~"
|
||||
dctFile = self.theProject.storage.getMetaFile(nwFiles.PROJ_DICT)
|
||||
if not isinstance(dctFile, Path):
|
||||
return False
|
||||
|
||||
tmpFile = dctFile.with_suffix(".tmp")
|
||||
try:
|
||||
with open(tmpFile, mode="w", encoding="utf-8") as outFile:
|
||||
for i in range(self.listBox.count()):
|
||||
@@ -160,15 +163,14 @@ class GuiWordList(QDialog):
|
||||
if item is not None:
|
||||
outFile.write(item.text() + "\n")
|
||||
|
||||
tmpFile.replace(dctFile)
|
||||
|
||||
except Exception:
|
||||
logger.error("Could not save new word list")
|
||||
logException()
|
||||
self.reject()
|
||||
return False
|
||||
|
||||
if os.path.isfile(dctFile):
|
||||
os.unlink(dctFile)
|
||||
os.rename(tmpFile, dctFile)
|
||||
self.accept()
|
||||
|
||||
return True
|
||||
@@ -187,10 +189,12 @@ class GuiWordList(QDialog):
|
||||
def _loadWordList(self):
|
||||
"""Load the project's word list, if it exists.
|
||||
"""
|
||||
self.listBox.clear()
|
||||
wordList = self.theProject.storage.getMetaFile(nwFiles.PROJ_DICT)
|
||||
if not isinstance(wordList, Path):
|
||||
return False
|
||||
|
||||
wordList = os.path.join(self.theProject.projMeta, nwFiles.PROJ_DICT)
|
||||
if not os.path.isfile(wordList):
|
||||
self.listBox.clear()
|
||||
if not wordList.exists():
|
||||
logger.debug("No project dictionary file found")
|
||||
return False
|
||||
|
||||
|
||||
@@ -50,10 +50,10 @@ from PyQt5.QtWidgets import (
|
||||
QFrame
|
||||
)
|
||||
|
||||
from novelwriter.core import NWDoc, NWSpellEnchant, countWords
|
||||
from novelwriter.core import NWSpellEnchant, countWords
|
||||
from novelwriter.enum import nwAlert, nwDocAction, nwDocInsert, nwDocMode
|
||||
from novelwriter.common import transferCase
|
||||
from novelwriter.constants import nwConst, nwKeyWords, nwUnicode
|
||||
from novelwriter.constants import nwConst, nwFiles, nwKeyWords, nwUnicode
|
||||
from novelwriter.gui.dochighlight import GuiDocHighlighter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -339,7 +339,7 @@ class GuiDocEditor(QTextEdit):
|
||||
document is new (empty string), we set up the editor for editing
|
||||
the file.
|
||||
"""
|
||||
self._nwDocument = NWDoc(self.theProject, tHandle)
|
||||
self._nwDocument = self.theProject.storage.getDocument(tHandle)
|
||||
self._nwItem = self._nwDocument.getCurrentItem()
|
||||
|
||||
theDoc = self._nwDocument.readDocument()
|
||||
@@ -470,8 +470,8 @@ class GuiDocEditor(QTextEdit):
|
||||
return True
|
||||
|
||||
def saveText(self):
|
||||
"""Save the text currently in the editor to the NWDoc object,
|
||||
and update the NWItem meta data.
|
||||
"""Save the text currently in the editor to the NWDocument
|
||||
object, and update the NWItem meta data.
|
||||
"""
|
||||
if self._nwItem is None or self._nwDocument is None:
|
||||
logger.error("Cannot save text as no document is open")
|
||||
@@ -689,7 +689,8 @@ class GuiDocEditor(QTextEdit):
|
||||
else:
|
||||
theLang = self.theProject.data.spellLang
|
||||
|
||||
self.spEnchant.setLanguage(theLang, self.theProject.projDict)
|
||||
projDict = self.theProject.storage.getMetaFile(nwFiles.PROJ_DICT)
|
||||
self.spEnchant.setLanguage(theLang, projDict)
|
||||
_, theProvider = self.spEnchant.describeDict()
|
||||
|
||||
self.spellDictionaryChanged.emit(str(theLang), str(theProvider))
|
||||
|
||||
@@ -824,7 +824,7 @@ class GuiMainMenu(QMenuBar):
|
||||
|
||||
# Tools > Backup
|
||||
self.aBackupProject = QAction(self.tr("Backup Project"), self)
|
||||
self.aBackupProject.triggered.connect(lambda: self.theProject.zipIt(True))
|
||||
self.aBackupProject.triggered.connect(lambda: self.theProject.backupProject(doNoify=True))
|
||||
self.toolsMenu.addAction(self.aBackupProject)
|
||||
|
||||
# Tools > Export Project
|
||||
|
||||
+8
-33
@@ -50,9 +50,9 @@ from novelwriter.dialogs import (
|
||||
from novelwriter.tools import (
|
||||
GuiBuildNovel, GuiLipsum, GuiProjectWizard, GuiWritingStats
|
||||
)
|
||||
from novelwriter.core import NWProject
|
||||
from novelwriter.core import NWProject, ProjectBuilder
|
||||
from novelwriter.enum import (
|
||||
nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwState, nwView
|
||||
nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwView
|
||||
)
|
||||
from novelwriter.common import getGuiItem, hexToInt
|
||||
from novelwriter.constants import nwFiles
|
||||
@@ -365,30 +365,10 @@ class GuiMain(QMainWindow):
|
||||
return False
|
||||
|
||||
logger.info("Creating new project")
|
||||
if self.theProject.newProject(projData):
|
||||
|
||||
self.hasProject = True
|
||||
self.idleRefTime = time()
|
||||
self.idleTime = 0.0
|
||||
|
||||
self.rebuildTrees()
|
||||
self.saveProject()
|
||||
|
||||
self.docEditor.setDictionaries()
|
||||
self.projView.openProjectTasks()
|
||||
self.novelView.openProjectTasks()
|
||||
self.outlineView.openProjectTasks()
|
||||
self.rebuildIndex(beQuiet=True)
|
||||
|
||||
self.mainStatus.setRefTime(self.theProject.projOpened)
|
||||
self.mainStatus.setProjectStatus(nwState.GOOD)
|
||||
self.mainStatus.setDocumentStatus(nwState.NONE)
|
||||
self.mainStatus.setStatus(self.tr("New project created ..."))
|
||||
|
||||
self._updateWindowTitle(self.theProject.data.name)
|
||||
|
||||
nwProject = ProjectBuilder(self)
|
||||
if nwProject.buildProject(projData):
|
||||
self.openProject(projPath)
|
||||
else:
|
||||
self.theProject.clearProject()
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -429,7 +409,7 @@ class GuiMain(QMainWindow):
|
||||
if not msgYes:
|
||||
doBackup = False
|
||||
if doBackup:
|
||||
self.theProject.zipIt(False)
|
||||
self.theProject.backupProject(doNotify=False)
|
||||
else:
|
||||
saveOK = True
|
||||
|
||||
@@ -443,7 +423,6 @@ class GuiMain(QMainWindow):
|
||||
self.idleRefTime = time()
|
||||
self.idleTime = 0.0
|
||||
|
||||
self.theProject.index.clearIndex()
|
||||
self.clearGUI()
|
||||
self.hasProject = False
|
||||
self._changeView(nwView.PROJECT)
|
||||
@@ -519,9 +498,6 @@ class GuiMain(QMainWindow):
|
||||
self.idleRefTime = time()
|
||||
self.idleTime = 0.0
|
||||
|
||||
# Load the tag index
|
||||
self.theProject.index.loadIndex()
|
||||
|
||||
# Update GUI
|
||||
self._updateWindowTitle(self.theProject.data.name)
|
||||
self.rebuildTrees()
|
||||
@@ -573,8 +549,7 @@ class GuiMain(QMainWindow):
|
||||
return False
|
||||
|
||||
self.projView.saveProjectTasks()
|
||||
if self.theProject.saveProject(autoSave=autoSave):
|
||||
self.theProject.index.saveIndex()
|
||||
self.theProject.saveProject(autoSave=autoSave)
|
||||
|
||||
return True
|
||||
|
||||
@@ -1407,7 +1382,7 @@ class GuiMain(QMainWindow):
|
||||
"""
|
||||
doSave = self.hasProject
|
||||
doSave &= self.theProject.projChanged
|
||||
doSave &= self.theProject.projPath is not None
|
||||
doSave &= self.theProject.storage.isOpen()
|
||||
|
||||
if doSave:
|
||||
logger.debug("Autosaving project")
|
||||
|
||||
@@ -29,6 +29,7 @@ import logging
|
||||
import novelwriter
|
||||
|
||||
from time import time
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
@@ -1088,9 +1089,12 @@ class GuiBuildNovel(QDialog):
|
||||
def _loadCache(self):
|
||||
"""Save the current data to cache.
|
||||
"""
|
||||
buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
|
||||
buildCache = self.theProject.storage.getCacheFile(nwFiles.BUILD_CACHE)
|
||||
if not isinstance(buildCache, Path):
|
||||
return False
|
||||
|
||||
dataCount = 0
|
||||
if os.path.isfile(buildCache):
|
||||
if buildCache.exists():
|
||||
logger.debug("Loading build cache")
|
||||
try:
|
||||
with open(buildCache, mode="r", encoding="utf-8") as inFile:
|
||||
@@ -1115,7 +1119,10 @@ class GuiBuildNovel(QDialog):
|
||||
def _saveCache(self):
|
||||
"""Save the current data to cache.
|
||||
"""
|
||||
buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
|
||||
buildCache = self.theProject.storage.getCacheFile(nwFiles.BUILD_CACHE)
|
||||
if not isinstance(buildCache, Path):
|
||||
return False
|
||||
|
||||
logger.debug("Saving build cache")
|
||||
try:
|
||||
with open(buildCache, mode="w+", encoding="utf-8") as outFile:
|
||||
|
||||
@@ -28,6 +28,7 @@ import json
|
||||
import logging
|
||||
import novelwriter
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtGui import QPixmap, QCursor
|
||||
@@ -439,8 +440,8 @@ class GuiWritingStats(QDialog):
|
||||
ttTime = 0
|
||||
ttIdle = 0
|
||||
|
||||
logFile = os.path.join(self.theProject.projMeta, nwFiles.SESS_STATS)
|
||||
if not os.path.isfile(logFile):
|
||||
logFile = self.theProject.storage.getMetaFile(nwFiles.SESS_STATS)
|
||||
if not isinstance(logFile, Path) or not logFile.exists():
|
||||
logger.info("This project has no writing stats logfile")
|
||||
return False
|
||||
|
||||
|
||||
+51
-57
@@ -1,21 +1,15 @@
|
||||
<?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>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:00:48">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1448" autoCount="237" editTime="69737">
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
<saveCount>1409</saveCount>
|
||||
<autoCount>236</autoCount>
|
||||
<editTime>69427</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
<doBackup>no</doBackup>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>954</novelWordCount>
|
||||
<notesWordCount>409</notesWordCount>
|
||||
<spellChecking auto="yes">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">636b6aa9b697b</entry>
|
||||
<entry key="viewer">636b6aa9b697b</entry>
|
||||
@@ -50,114 +44,114 @@
|
||||
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="27">
|
||||
<content items="27" novelWords="954" notesWords="409">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H1" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
||||
<name status="sf12341" import="ia857f0" active="yes">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>
|
||||
<meta expanded="no" heading="H1" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">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>
|
||||
<meta expanded="yes" heading="H2" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
||||
<name status="sf24ce6" import="ia857f0" active="yes">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>
|
||||
<meta expanded="no" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">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>
|
||||
<meta expanded="no" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">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>
|
||||
<meta expanded="no" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
||||
<name status="s78ea90" import="ia857f0" active="yes">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>
|
||||
<meta expanded="no" heading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
||||
<name status="sf24ce6" import="ia857f0" active="no">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>
|
||||
<meta expanded="yes" heading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">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>
|
||||
<meta expanded="no" heading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">We Found John!</name>
|
||||
</item>
|
||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H1" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">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>
|
||||
<meta expanded="no" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="sf12341" import="ia857f0">Characters</name>
|
||||
</item>
|
||||
<item handle="f7e2d9f330615" parent="f6622b4617424" root="f6622b4617424" order="0" type="FOLDER" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||
<name status="sf12341" import="icfb3a5" active="yes">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>
|
||||
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||
<name status="sf12341" import="i56be10" active="yes">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>
|
||||
<meta expanded="no" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||
<name status="sf12341" import="icfb3a5" active="yes">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>
|
||||
<meta expanded="no" heading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Mars</name>
|
||||
</item>
|
||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="sf12341" import="ia857f0">Archive</name>
|
||||
</item>
|
||||
<item handle="ae9bf3c3ea159" parent="6827118336ac1" root="6827118336ac1" order="0" type="FOLDER" class="ARCHIVE">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H3" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Old File</name>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H3" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="sf12341" import="ia857f0" active="yes">Delete Me!</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
+39
-23
@@ -24,10 +24,12 @@ import sys
|
||||
import pytest
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from mock import MockGuiMain
|
||||
from tools import cleanProject
|
||||
|
||||
sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
|
||||
sys.path.insert(1, str(Path(__file__).parent.parent.absolute()))
|
||||
|
||||
import novelwriter # noqa: E402
|
||||
|
||||
@@ -62,6 +64,41 @@ def tmpDir():
|
||||
return theDir
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def tmpPath(tmpDir):
|
||||
"""A temporary folder for the test session. Path version.
|
||||
"""
|
||||
return Path(tmpDir)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def tstPaths(tmpPath):
|
||||
"""Returns an object that can provide the various paths needed for
|
||||
running tests.
|
||||
"""
|
||||
class _Store:
|
||||
testDir = Path(__file__).parent
|
||||
filesDir = testDir / "files"
|
||||
refDir = testDir / "reference"
|
||||
outDir = tmpPath / "results"
|
||||
|
||||
store = _Store()
|
||||
store.outDir.mkdir(exist_ok=True)
|
||||
|
||||
return store
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def fncPath(tmpPath):
|
||||
"""A temporary folder for a single test function. Path version.
|
||||
"""
|
||||
fncPath = tmpPath / "function"
|
||||
if fncPath.is_dir():
|
||||
shutil.rmtree(fncPath)
|
||||
fncPath.mkdir(exist_ok=True)
|
||||
return fncPath
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def refDir():
|
||||
"""The folder where all the reference files are stored for verifying
|
||||
@@ -95,7 +132,7 @@ def outDir(tmpDir):
|
||||
def fncDir(tmpDir):
|
||||
"""A temporary folder for a single test function.
|
||||
"""
|
||||
fncDir = os.path.join(tmpDir, "f_temp")
|
||||
fncDir = os.path.join(tmpDir, "function")
|
||||
if os.path.isdir(fncDir):
|
||||
shutil.rmtree(fncDir)
|
||||
if not os.path.isdir(fncDir):
|
||||
@@ -236,27 +273,6 @@ def nwLipsum(tmpDir):
|
||||
return
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def nwOldProj(tmpDir):
|
||||
"""A minimal movelWriter project using the old folder structure used
|
||||
for storage versions < 1.2.
|
||||
"""
|
||||
tstDir = os.path.dirname(__file__)
|
||||
srcDir = os.path.join(tstDir, "oldproj")
|
||||
dstDir = os.path.join(tmpDir, "oldproj")
|
||||
if os.path.isdir(dstDir):
|
||||
shutil.rmtree(dstDir)
|
||||
|
||||
shutil.copytree(srcDir, dstDir)
|
||||
|
||||
yield dstDir
|
||||
|
||||
if os.path.isdir(dstDir):
|
||||
shutil.rmtree(dstDir)
|
||||
|
||||
return
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def ipsumText():
|
||||
"""Return five paragraphs of Lorem Ipsum text.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 12:20:53">
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-15 12:12:59">
|
||||
<project>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
@@ -14,25 +14,24 @@
|
||||
<language>en_GB</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>en_GB</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>
|
||||
<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>
|
||||
<title>%title%</title>
|
||||
<chapter>Chapter %chw%: %title%</chapter>
|
||||
<unnumbered>%title%</unnumbered>
|
||||
<scene>Scene %ch%.%sc%: %title%</scene>
|
||||
<section></section>
|
||||
</titleFormat>
|
||||
<status>
|
||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||
@@ -56,56 +55,56 @@
|
||||
<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>
|
||||
<meta expanded="False" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
||||
<name status="sc24b8f" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
||||
<name status="sf12341" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="s90e6c9" import="ia857f0" exported="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>
|
||||
<meta expanded="True" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
||||
<name status="sf24ce6" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
||||
<name status="s90e6c9" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
||||
<name status="s90e6c9" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
||||
<name status="s78ea90" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
||||
<name status="sf24ce6" import="ia857f0" exported="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>
|
||||
<meta expanded="True" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
||||
<name status="s90e6c9" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
||||
<name status="s90e6c9" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
||||
<name status="sc24b8f" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
||||
<name status="s90e6c9" import="ia857f0" exported="True">Chapter One</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
@@ -116,28 +115,28 @@
|
||||
<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>
|
||||
<meta expanded="False" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||
<name status="sf12341" import="icfb3a5" exported="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>
|
||||
<meta expanded="False" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||
<name status="sf12341" import="i2d7a54" exported="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>
|
||||
<meta expanded="False" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||
<name status="sf12341" import="i56be10" exported="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>
|
||||
<meta expanded="False" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||
<name status="sf12341" import="icfb3a5" exported="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>
|
||||
<meta expanded="False" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||
<name status="sf12341" import="i2d7a54" exported="True">Mars</name>
|
||||
</item>
|
||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="True"/>
|
||||
@@ -148,16 +147,16 @@
|
||||
<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>
|
||||
<meta expanded="False" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
||||
<name status="s90e6c9" import="ia857f0" exported="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>
|
||||
<meta expanded="False" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="sf12341" import="ia857f0" exported="True">Delete Me!</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:00:48">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="5" autoCount="10" editTime="1000">
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>en_GB</language>
|
||||
<spellChecking auto="yes">en_GB</spellChecking>
|
||||
<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 items="27" novelWords="954" notesWords="409">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H1" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
||||
<name status="sf12341" import="ia857f0" active="yes">Page</name>
|
||||
</item>
|
||||
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H1" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Part One</name>
|
||||
</item>
|
||||
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="yes" heading="H2" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Another Scene</name>
|
||||
</item>
|
||||
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
||||
<name status="s78ea90" import="ia857f0" active="yes">Interlude</name>
|
||||
</item>
|
||||
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
||||
<name status="sf24ce6" import="ia857f0" active="no">A Note on Structure</name>
|
||||
</item>
|
||||
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="yes" heading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Chapter Two</name>
|
||||
</item>
|
||||
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">We Found John!</name>
|
||||
</item>
|
||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H1" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="yes"/>
|
||||
<name status="sf12341" import="ia857f0">Characters</name>
|
||||
</item>
|
||||
<item handle="f7e2d9f330615" parent="f6622b4617424" root="f6622b4617424" order="0" type="FOLDER" class="CHARACTER">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||
<name status="sf12341" import="icfb3a5" active="yes">John Smith</name>
|
||||
</item>
|
||||
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||
<name status="sf12341" import="i56be10" active="yes">Earth</name>
|
||||
</item>
|
||||
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||
<name status="sf12341" import="icfb3a5" active="yes">Space</name>
|
||||
</item>
|
||||
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="no" heading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Mars</name>
|
||||
</item>
|
||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="yes"/>
|
||||
<name status="sf12341" import="ia857f0">Archive</name>
|
||||
</item>
|
||||
<item handle="ae9bf3c3ea159" parent="6827118336ac1" root="6827118336ac1" order="0" type="FOLDER" class="ARCHIVE">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H3" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Old File</name>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H3" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="sf12341" import="ia857f0" active="yes">Delete Me!</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
+41
-48
@@ -1,21 +1,14 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-31 22:17:38">
|
||||
<project>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:00:43">
|
||||
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="38" autoCount="24" editTime="1900">
|
||||
<name>Lorem Ipsum</name>
|
||||
<title>Lorem Ipsum</title>
|
||||
<author>lipsum.com</author>
|
||||
<saveCount>32</saveCount>
|
||||
<autoCount>24</autoCount>
|
||||
<editTime>1889</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
<doBackup>no</doBackup>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<totalWordCount>3847</totalWordCount>
|
||||
<novelWordCount>3109</novelWordCount>
|
||||
<notesWordCount>738</notesWordCount>
|
||||
<spellChecking auto="no">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">7a992350f3eb6</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -46,90 +39,90 @@
|
||||
<entry key="id6b1d0" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="21">
|
||||
<content items="21" novelWords="3109" notesWords="738">
|
||||
<item handle="b3643d0f92e32" parent="None" root="b3643d0f92e32" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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" heading="H1" charCount="230" wordCount="40" paraCount="3" cursorPos="148"/>
|
||||
<name status="sedd043" import="i613591" active="True">Lorem Ipsum</name>
|
||||
<meta expanded="no" heading="H1" charCount="230" wordCount="40" paraCount="3" cursorPos="148"/>
|
||||
<name status="sedd043" import="i613591" active="yes">Lorem Ipsum</name>
|
||||
</item>
|
||||
<item handle="8c58a65414c23" parent="b3643d0f92e32" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H0" charCount="1058" wordCount="176" paraCount="2" cursorPos="43"/>
|
||||
<name status="sedd043" import="i613591" active="True">Front Matter</name>
|
||||
<meta expanded="no" heading="H0" charCount="1058" wordCount="176" paraCount="2" cursorPos="43"/>
|
||||
<name status="sedd043" import="i613591" active="yes">Front Matter</name>
|
||||
</item>
|
||||
<item handle="88d59a277361b" parent="b3643d0f92e32" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H2" charCount="584" wordCount="92" paraCount="1" cursorPos="4"/>
|
||||
<name status="s92a87b" import="i613591" active="True">Prologue</name>
|
||||
<meta expanded="no" heading="H2" charCount="584" wordCount="92" paraCount="1" cursorPos="4"/>
|
||||
<name status="s92a87b" import="i613591" active="yes">Prologue</name>
|
||||
</item>
|
||||
<item handle="db7e733775d4d" parent="b3643d0f92e32" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H1" charCount="35" wordCount="6" paraCount="1" cursorPos="42"/>
|
||||
<name status="sbaa94f" import="i613591" active="True">Act One</name>
|
||||
<meta expanded="no" heading="H1" charCount="35" wordCount="6" paraCount="1" cursorPos="42"/>
|
||||
<name status="sbaa94f" import="i613591" active="yes">Act One</name>
|
||||
</item>
|
||||
<item handle="45e6b01ca35c1" parent="b3643d0f92e32" root="b3643d0f92e32" order="4" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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" heading="H2" charCount="419" wordCount="67" paraCount="1" cursorPos="56"/>
|
||||
<name status="s92a87b" import="i613591" active="True">Chapter One</name>
|
||||
<meta expanded="no" heading="H2" charCount="419" wordCount="67" paraCount="1" cursorPos="56"/>
|
||||
<name status="s92a87b" import="i613591" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="88243afbe5ed8" parent="45e6b01ca35c1" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H3" charCount="2758" wordCount="404" paraCount="4" cursorPos="1528"/>
|
||||
<name status="sedd043" import="i613591" active="True">Scene One</name>
|
||||
<meta expanded="no" heading="H3" charCount="2758" wordCount="404" paraCount="4" cursorPos="1528"/>
|
||||
<name status="sedd043" import="i613591" active="yes">Scene One</name>
|
||||
</item>
|
||||
<item handle="f96ec11c6a3da" parent="45e6b01ca35c1" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H3" charCount="4043" wordCount="600" paraCount="6" cursorPos="2335"/>
|
||||
<name status="sedd043" import="i613591" active="True">Scene Two</name>
|
||||
<meta expanded="no" heading="H3" charCount="4043" wordCount="600" paraCount="6" cursorPos="2335"/>
|
||||
<name status="sedd043" import="i613591" active="yes">Scene Two</name>
|
||||
</item>
|
||||
<item handle="846352075de7d" parent="b3643d0f92e32" root="b3643d0f92e32" order="5" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H2" charCount="631" wordCount="109" paraCount="3" cursorPos="376"/>
|
||||
<name status="sbaa94f" import="i613591" active="False">Interlude</name>
|
||||
<meta expanded="no" heading="H2" charCount="631" wordCount="109" paraCount="3" cursorPos="376"/>
|
||||
<name status="sbaa94f" import="i613591" active="no">Interlude</name>
|
||||
</item>
|
||||
<item handle="6bd935d2490cd" parent="b3643d0f92e32" root="b3643d0f92e32" order="6" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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" heading="H2" charCount="477" wordCount="70" paraCount="1" cursorPos="56"/>
|
||||
<name status="s92a87b" import="i613591" active="True">Chapter Two</name>
|
||||
<meta expanded="no" heading="H2" charCount="477" wordCount="70" paraCount="1" cursorPos="56"/>
|
||||
<name status="s92a87b" import="i613591" active="yes">Chapter Two</name>
|
||||
</item>
|
||||
<item handle="eb103bc70c90c" parent="6bd935d2490cd" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H3" charCount="3006" wordCount="439" paraCount="4" cursorPos="57"/>
|
||||
<name status="sedd043" import="i613591" active="True">Scene Three</name>
|
||||
<meta expanded="no" heading="H3" charCount="3006" wordCount="439" paraCount="4" cursorPos="57"/>
|
||||
<name status="sedd043" import="i613591" active="yes">Scene Three</name>
|
||||
</item>
|
||||
<item handle="f8c0562e50f1b" parent="6bd935d2490cd" root="b3643d0f92e32" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H3" charCount="3839" wordCount="563" paraCount="6" cursorPos="56"/>
|
||||
<name status="sedd043" import="i613591" active="True">Scene Four</name>
|
||||
<meta expanded="no" heading="H3" charCount="3839" wordCount="563" paraCount="6" cursorPos="56"/>
|
||||
<name status="sedd043" import="i613591" active="yes">Scene Four</name>
|
||||
</item>
|
||||
<item handle="47666c91c7ccf" parent="6bd935d2490cd" root="b3643d0f92e32" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H3" charCount="3644" wordCount="543" paraCount="5" cursorPos="351"/>
|
||||
<name status="sedd043" import="i613591" active="True">Scene Five</name>
|
||||
<meta expanded="no" heading="H3" charCount="3644" wordCount="543" paraCount="5" cursorPos="351"/>
|
||||
<name status="sedd043" import="i613591" active="yes">Scene Five</name>
|
||||
</item>
|
||||
<item handle="67a8707f2f249" parent="None" root="67a8707f2f249" order="1" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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" heading="H1" charCount="1864" wordCount="284" paraCount="3" cursorPos="1883"/>
|
||||
<name status="sbaa94f" import="i613591" active="True">Mr. Nobody</name>
|
||||
<meta expanded="no" heading="H1" charCount="1864" wordCount="284" paraCount="3" cursorPos="1883"/>
|
||||
<name status="sbaa94f" import="i613591" active="yes">Mr. Nobody</name>
|
||||
</item>
|
||||
<item handle="6c6afb1247750" parent="None" root="6c6afb1247750" order="2" type="ROOT" class="PLOT">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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" heading="H1" charCount="1369" wordCount="195" paraCount="2" cursorPos="1387"/>
|
||||
<name status="sbaa94f" import="i613591" active="True">Main</name>
|
||||
<meta expanded="no" heading="H1" charCount="1369" wordCount="195" paraCount="2" cursorPos="1387"/>
|
||||
<name status="sbaa94f" import="i613591" active="yes">Main</name>
|
||||
</item>
|
||||
<item handle="60bdf227455cc" parent="None" root="60bdf227455cc" order="3" type="ROOT" class="WORLD">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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" heading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="1792"/>
|
||||
<name status="sbaa94f" import="i613591" active="True">Ancient Europe</name>
|
||||
<meta expanded="no" heading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="1792"/>
|
||||
<name status="sbaa94f" import="i613591" active="yes">Ancient Europe</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
### Scene Four
|
||||
|
||||
Scene Four
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Antagonist
|
||||
|
||||
Antagonist
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
### Scene Two
|
||||
|
||||
Scene Two
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Protagonist
|
||||
|
||||
Protagonist
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
### Scene Three
|
||||
|
||||
Scene Three
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
### Scene Five
|
||||
|
||||
Scene Five
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
### Scene One
|
||||
|
||||
Scene One
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Start: 2020-09-26 16:13:00 End: 2020-09-26 16:15:54 Words: 24
|
||||
Start: 2020-09-26 16:16:28 End: 2020-09-26 16:16:40 Words: -1
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"tagIndex": {},
|
||||
"refIndex": {
|
||||
"f528d831f5b24": [],
|
||||
"88124a4292d8b": [],
|
||||
"91239bf2f8b69": [],
|
||||
"19752e7f9d8af": [],
|
||||
"a764d5acf5a21": [],
|
||||
"9058ae29f0dfd": [],
|
||||
"7ff63b8afc4cd": []
|
||||
},
|
||||
"novelIndex": {
|
||||
"f528d831f5b24": [
|
||||
[
|
||||
1,
|
||||
3,
|
||||
"Scene One",
|
||||
"SCENE"
|
||||
]
|
||||
],
|
||||
"88124a4292d8b": [
|
||||
[
|
||||
1,
|
||||
3,
|
||||
"Scene Two",
|
||||
"SCENE"
|
||||
]
|
||||
],
|
||||
"91239bf2f8b69": [
|
||||
[
|
||||
1,
|
||||
3,
|
||||
"Scene Three",
|
||||
"SCENE"
|
||||
]
|
||||
],
|
||||
"19752e7f9d8af": [
|
||||
[
|
||||
1,
|
||||
3,
|
||||
"Scene Four",
|
||||
"SCENE"
|
||||
]
|
||||
],
|
||||
"a764d5acf5a21": [
|
||||
[
|
||||
1,
|
||||
3,
|
||||
"Scene Five",
|
||||
"SCENE"
|
||||
]
|
||||
]
|
||||
},
|
||||
"noteIndex": {
|
||||
"9058ae29f0dfd": [
|
||||
[
|
||||
1,
|
||||
1,
|
||||
"Protagonist",
|
||||
"NOTE"
|
||||
]
|
||||
],
|
||||
"7ff63b8afc4cd": [
|
||||
[
|
||||
1,
|
||||
1,
|
||||
"Antagonist",
|
||||
"NOTE"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="0.4.5" fileVersion="1.0" timeStamp="2020-09-26 16:16:39">
|
||||
<project>
|
||||
<name></name>
|
||||
<title></title>
|
||||
<backup>True</backup>
|
||||
</project>
|
||||
<settings>
|
||||
<spellCheck>False</spellCheck>
|
||||
<lastEdited>a764d5acf5a21</lastEdited>
|
||||
<lastViewed>None</lastViewed>
|
||||
<lastWordCount>23</lastWordCount>
|
||||
<autoReplace/>
|
||||
<status>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Note</entry>
|
||||
<entry blue="0" green="150" red="200">Draft</entry>
|
||||
<entry blue="0" green="200" red="50">Finished</entry>
|
||||
</status>
|
||||
<importance>
|
||||
<entry blue="100" green="100" red="100">New</entry>
|
||||
<entry blue="0" green="50" red="200">Minor</entry>
|
||||
<entry blue="0" green="150" red="200">Major</entry>
|
||||
<entry blue="0" green="200" red="50">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="12">
|
||||
<item handle="095c337ad50c8" order="0" parent="None">
|
||||
<name>Novel</name>
|
||||
<type>ROOT</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="04d47c4b31af7" order="0" parent="095c337ad50c8">
|
||||
<name>Chapter One</name>
|
||||
<type>FOLDER</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="f528d831f5b24" order="0" parent="04d47c4b31af7">
|
||||
<name>Scene One</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>18</charCount>
|
||||
<wordCount>4</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>3</cursorPos>
|
||||
</item>
|
||||
<item handle="88124a4292d8b" order="1" parent="04d47c4b31af7">
|
||||
<name>Scene Two</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>18</charCount>
|
||||
<wordCount>4</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>2</cursorPos>
|
||||
</item>
|
||||
<item handle="91239bf2f8b69" order="2" parent="04d47c4b31af7">
|
||||
<name>Scene Three</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>22</charCount>
|
||||
<wordCount>4</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>2</cursorPos>
|
||||
</item>
|
||||
<item handle="19752e7f9d8af" order="3" parent="04d47c4b31af7">
|
||||
<name>Scene Four</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>20</charCount>
|
||||
<wordCount>4</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>2</cursorPos>
|
||||
</item>
|
||||
<item handle="a764d5acf5a21" order="4" parent="04d47c4b31af7">
|
||||
<name>Scene Five</name>
|
||||
<type>FILE</type>
|
||||
<class>NOVEL</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>SCENE</layout>
|
||||
<charCount>20</charCount>
|
||||
<wordCount>4</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>2</cursorPos>
|
||||
</item>
|
||||
<item handle="a11282c943444" order="1" parent="None">
|
||||
<name>Characters</name>
|
||||
<type>ROOT</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>New</status>
|
||||
<expanded>True</expanded>
|
||||
</item>
|
||||
<item handle="9058ae29f0dfd" order="0" parent="a11282c943444">
|
||||
<name>Protagonist</name>
|
||||
<type>FILE</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>NOTE</layout>
|
||||
<charCount>11</charCount>
|
||||
<wordCount>1</wordCount>
|
||||
<paraCount>0</paraCount>
|
||||
<cursorPos>28</cursorPos>
|
||||
</item>
|
||||
<item handle="7ff63b8afc4cd" order="1" parent="a11282c943444">
|
||||
<name>Antagonist</name>
|
||||
<type>FILE</type>
|
||||
<class>CHARACTER</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
<layout>NOTE</layout>
|
||||
<charCount>13</charCount>
|
||||
<wordCount>2</wordCount>
|
||||
<paraCount>1</paraCount>
|
||||
<cursorPos>26</cursorPos>
|
||||
</item>
|
||||
<item handle="dabb9d158b2b2" order="2" parent="None">
|
||||
<name>Plot</name>
|
||||
<type>ROOT</type>
|
||||
<class>PLOT</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
<item handle="4a817a3a84b42" order="3" parent="None">
|
||||
<name>World</name>
|
||||
<type>ROOT</type>
|
||||
<class>WORLD</class>
|
||||
<status>New</status>
|
||||
<expanded>False</expanded>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
@@ -1,20 +1,14 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||
<project>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:05:22">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
<author>Jane Doe</author>
|
||||
<saveCount>2</saveCount>
|
||||
<autoCount>1</autoCount>
|
||||
<editTime>0</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>10</novelWordCount>
|
||||
<notesWordCount>3</notesWordCount>
|
||||
<spellChecking auto="no">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -42,50 +36,50 @@
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="11">
|
||||
<content items="11" novelWords="10" notesWords="3">
|
||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<item handle="0000000000009" parent="None" root="0000000000009" order="0" type="ROOT" class="PLOT">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<item handle="000000000000a" parent="None" root="000000000000a" order="0" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<item handle="000000000000b" parent="None" root="000000000000b" order="0" type="ROOT" class="WORLD">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<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" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||
<meta expanded="no" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<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" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||
<meta expanded="no" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
||||
</item>
|
||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||
<meta expanded="no" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
||||
</item>
|
||||
<item handle="0000000000010" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Stuff</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H2" charCount="5" wordCount="1" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Hello</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H1" charCount="11" wordCount="3" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Jane</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||
<project>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:05:22">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
<author>Jane Doe</author>
|
||||
<saveCount>2</saveCount>
|
||||
<autoCount>1</autoCount>
|
||||
<editTime>0</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>9</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
<spellChecking auto="no">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -42,69 +36,69 @@
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="16">
|
||||
<content items="16" novelWords="9" notesWords="0">
|
||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<item handle="0000000000009" parent="None" root="0000000000009" order="0" type="ROOT" class="PLOT">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<item handle="000000000000a" parent="None" root="000000000000a" order="0" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<item handle="000000000000b" parent="None" root="000000000000b" order="0" type="ROOT" class="WORLD">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<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" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||
<meta expanded="no" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="0" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<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" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||
<meta expanded="no" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
||||
</item>
|
||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||
<meta expanded="no" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
||||
</item>
|
||||
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Locations</name>
|
||||
</item>
|
||||
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="TIMELINE">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Timeline</name>
|
||||
</item>
|
||||
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="OBJECT">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Objects</name>
|
||||
</item>
|
||||
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="CUSTOM">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Custom</name>
|
||||
</item>
|
||||
<item handle="0000000000017" parent="None" root="0000000000017" order="0" type="ROOT" class="CUSTOM">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Custom</name>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
+43
-49
@@ -1,21 +1,15 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||
<project>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:05:22">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||
<name>Test Custom</name>
|
||||
<title>Test Novel</title>
|
||||
<author>Jane Doe</author>
|
||||
<author>John Doh</author>
|
||||
<saveCount>1</saveCount>
|
||||
<autoCount>1</autoCount>
|
||||
<editTime>0</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
<spellChecking auto="no">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -43,93 +37,93 @@
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="22">
|
||||
<content items="22" novelWords="0" notesWords="0">
|
||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Title Page</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Chapter 1</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 1.1</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 1.2</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 1.3</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Chapter 2</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 2.1</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 2.2</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 2.3</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Chapter 3</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 3.1</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 3.2</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 3.3</name>
|
||||
</item>
|
||||
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="PLOT">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Main Plot</name>
|
||||
</item>
|
||||
<item handle="0000000000018" parent="None" root="0000000000018" order="0" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Protagonist</name>
|
||||
</item>
|
||||
<item handle="000000000001a" parent="None" root="000000000001a" order="0" type="ROOT" class="WORLD">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Locations</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Main Location</name>
|
||||
</item>
|
||||
<item handle="000000000001c" parent="None" root="000000000001c" order="0" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Archive</name>
|
||||
</item>
|
||||
<item handle="000000000001d" parent="None" root="000000000001d" order="0" type="ROOT" class="TRASH">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Trash</name>
|
||||
</item>
|
||||
</content>
|
||||
+31
-37
@@ -1,21 +1,15 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||
<project>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:05:22">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||
<name>Test Custom</name>
|
||||
<title>Test Novel</title>
|
||||
<author>Jane Doe</author>
|
||||
<author>John Doh</author>
|
||||
<saveCount>1</saveCount>
|
||||
<autoCount>1</autoCount>
|
||||
<editTime>0</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
<spellChecking auto="no">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -43,69 +37,69 @@
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="16">
|
||||
<content items="16" novelWords="0" notesWords="0">
|
||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Title Page</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 1</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 2</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 3</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 4</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 5</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Scene 6</name>
|
||||
</item>
|
||||
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="PLOT">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Main Plot</name>
|
||||
</item>
|
||||
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Protagonist</name>
|
||||
</item>
|
||||
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="WORLD">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Locations</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Main Location</name>
|
||||
</item>
|
||||
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Archive</name>
|
||||
</item>
|
||||
<item handle="0000000000017" parent="None" root="0000000000017" order="0" type="ROOT" class="TRASH">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Trash</name>
|
||||
</item>
|
||||
</content>
|
||||
+17
-23
@@ -1,19 +1,13 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
||||
<project>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:05:22">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||
<name>New Project</name>
|
||||
<title>None</title>
|
||||
<saveCount>2</saveCount>
|
||||
<autoCount>1</autoCount>
|
||||
<editTime>0</editTime>
|
||||
<title>New Project</title>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
<spellChecking auto="no">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -41,37 +35,37 @@
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="8">
|
||||
<content items="8" novelWords="0" notesWords="0">
|
||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Novel</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Title Page</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
||||
</item>
|
||||
<item handle="000000000000c" parent="None" root="000000000000c" order="0" type="ROOT" class="PLOT">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<item handle="000000000000d" parent="None" root="000000000000d" order="0" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<item handle="000000000000e" parent="None" root="000000000000e" order="0" type="ROOT" class="WORLD">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Locations</name>
|
||||
</item>
|
||||
<item handle="000000000000f" parent="None" root="000000000000f" order="0" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Archive</name>
|
||||
</item>
|
||||
</content>
|
||||
@@ -1,20 +1,14 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 12:01:37">
|
||||
<project>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 12:58:26">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="4" autoCount="2" editTime="3">
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
<author>Jane Doe</author>
|
||||
<saveCount>4</saveCount>
|
||||
<autoCount>2</autoCount>
|
||||
<editTime>3</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>136</novelWordCount>
|
||||
<notesWordCount>27</notesWordCount>
|
||||
<spellChecking auto="yes">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">000000000000f</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -42,53 +36,53 @@
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="12">
|
||||
<content items="12" novelWords="136" notesWords="27">
|
||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||
<meta expanded="no" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||
<meta expanded="no" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
||||
</item>
|
||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H1" charCount="781" wordCount="129" paraCount="14" cursorPos="1010"/>
|
||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||
<meta expanded="no" heading="H1" charCount="781" wordCount="129" paraCount="14" cursorPos="1010"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
||||
</item>
|
||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H1" charCount="48" wordCount="10" paraCount="1" cursorPos="69"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Note</name>
|
||||
</item>
|
||||
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H1" charCount="34" wordCount="8" paraCount="1" cursorPos="51"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Note</name>
|
||||
</item>
|
||||
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000004">World</name>
|
||||
</item>
|
||||
<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>
|
||||
<meta expanded="no" heading="H1" charCount="51" wordCount="9" paraCount="1" cursorPos="68"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Note</name>
|
||||
</item>
|
||||
<item handle="0000000000014" parent="None" root="0000000000014" order="4" type="ROOT" class="TRASH">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Trash</name>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:30">
|
||||
<project>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 12:57:21">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="2" autoCount="1" editTime="0">
|
||||
<name>New Project</name>
|
||||
<title>New Novel</title>
|
||||
<author>Jane Doe</author>
|
||||
<saveCount>2</saveCount>
|
||||
<autoCount>1</autoCount>
|
||||
<editTime>0</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>9</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
<spellChecking auto="no">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -42,37 +36,37 @@
|
||||
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="8">
|
||||
<content items="8" novelWords="9" notesWords="0">
|
||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<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" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">Title Page</name>
|
||||
<meta expanded="no" heading="H1" charCount="20" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<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" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Chapter</name>
|
||||
<meta expanded="no" heading="H2" charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
||||
</item>
|
||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="False" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="True">New Scene</name>
|
||||
<meta expanded="no" heading="H3" charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
|
||||
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
||||
</item>
|
||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Plot</name>
|
||||
</item>
|
||||
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">Characters</name>
|
||||
</item>
|
||||
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
|
||||
<meta expanded="False"/>
|
||||
<meta expanded="no"/>
|
||||
<name status="s000000" import="i000004">World</name>
|
||||
</item>
|
||||
</content>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,362 +1,494 @@
|
||||
[
|
||||
{
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Novel",
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL",
|
||||
"expanded": true,
|
||||
"status": "s000002"
|
||||
"name": "Novel",
|
||||
"itemAttr": {
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Title Page",
|
||||
"itemAttr": {
|
||||
"handle": "53b69b83cdafc",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 72,
|
||||
"wordCount": 15,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 78
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Page",
|
||||
"itemAttr": {
|
||||
"handle": "974e400180a99",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 208,
|
||||
"wordCount": 40,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 213
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Part One",
|
||||
"itemAttr": {
|
||||
"handle": "edca4be2fcaf8",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 23,
|
||||
"wordCount": 5,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "e7ded148d6e4a",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"heading": "H0",
|
||||
"label": "A Folder",
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL",
|
||||
"expanded": true,
|
||||
"status": "s000003"
|
||||
"name": "A Folder",
|
||||
"itemAttr": {
|
||||
"handle": "e7ded148d6e4a",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Chapter One",
|
||||
"itemAttr": {
|
||||
"handle": "6a2d6d5f4f401",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 12,
|
||||
"wordCount": 3,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 215
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Making a Scene",
|
||||
"itemAttr": {
|
||||
"handle": "636b6aa9b697b",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 1199,
|
||||
"wordCount": 216,
|
||||
"paraCount": 7,
|
||||
"cursorPos": 527
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Another Scene",
|
||||
"itemAttr": {
|
||||
"handle": "bc0cbd2a407f3",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 476,
|
||||
"wordCount": 93,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 551
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Interlude",
|
||||
"itemAttr": {
|
||||
"handle": "ba8a28a246524",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 633,
|
||||
"wordCount": 101,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 1238
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "A Note on Structure",
|
||||
"itemAttr": {
|
||||
"handle": "96b68994dfa3d",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 4,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 1692,
|
||||
"wordCount": 313,
|
||||
"paraCount": 6,
|
||||
"cursorPos": 1721
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": false,
|
||||
"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"
|
||||
"name": "Chapter Two",
|
||||
"itemAttr": {
|
||||
"handle": "88706ddc78b1b",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 5,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 139,
|
||||
"wordCount": 28,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 343
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "We Found John!",
|
||||
"itemAttr": {
|
||||
"handle": "ae7339df26ded",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 6,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 189,
|
||||
"wordCount": 37,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 224
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000003"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"heading": "H0",
|
||||
"label": "Characters",
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"import": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Main Characters",
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Main Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "John Smith",
|
||||
"itemAttr": {
|
||||
"handle": "14298de4d9524",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 49,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 24
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Jane Smith",
|
||||
"itemAttr": {
|
||||
"handle": "bb2c23b3c42cc",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 55,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 25
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"import": "i000009"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"heading": "H0",
|
||||
"label": "Locations",
|
||||
"type": "ROOT",
|
||||
"class": "WORLD",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Locations",
|
||||
"itemAttr": {
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "ROOT",
|
||||
"class": "WORLD"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Earth",
|
||||
"itemAttr": {
|
||||
"handle": "b3e74dbc1f584",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 76,
|
||||
"wordCount": 15,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 20
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Space",
|
||||
"itemAttr": {
|
||||
"handle": "f1471bef9f2ae",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 115,
|
||||
"wordCount": 24,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 133
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Mars",
|
||||
"itemAttr": {
|
||||
"handle": "5eaea4e8cdee8",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 28,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 45
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"import": "i000009"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"heading": "H0",
|
||||
"label": "Trash",
|
||||
"type": "ROOT",
|
||||
"class": "TRASH",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Trash",
|
||||
"itemAttr": {
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "ROOT",
|
||||
"class": "TRASH"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Delete Me!",
|
||||
"itemAttr": {
|
||||
"handle": "b8136a5a774a0",
|
||||
"parent": "98acd8c76c93a",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": false,
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 36
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
<?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>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2020-05-28 09:59:15">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="0" autoCount="0" editTime="1000">
|
||||
<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>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
<spellChecking auto="yes">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -50,94 +44,94 @@
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="22">
|
||||
<content items="22" novelWords="0" notesWords="0">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="72" wordCount="15" paraCount="2" cursorPos="78"/>
|
||||
<name status="s000002" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="208" wordCount="40" paraCount="2" cursorPos="213"/>
|
||||
<name status="s000000" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="23" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000007" active="yes">Part One</name>
|
||||
</item>
|
||||
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="None" order="3" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="12" wordCount="3" paraCount="0" cursorPos="215"/>
|
||||
<name status="s000001" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="1199" wordCount="216" paraCount="7" cursorPos="527"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="476" wordCount="93" paraCount="3" cursorPos="551"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="633" wordCount="101" paraCount="3" cursorPos="1238"/>
|
||||
<name status="s000006" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="1692" wordCount="313" paraCount="6" cursorPos="1721"/>
|
||||
<name status="s000004" import="i000007" active="no">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>
|
||||
<meta expanded="no" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
|
||||
<name status="s000003" import="i000007" active="yes">We Found John!</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000007">Characters</name>
|
||||
</item>
|
||||
<item handle="f7e2d9f330615" parent="f6622b4617424" root="None" order="0" type="FOLDER" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||
<name status="s000000" import="i000008" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||
<name status="s000000" import="i000009" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||
<name status="s000000" import="i00000a" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||
<name status="s000000" import="i000008" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||
<name status="s000000" import="i000009" active="yes">Mars</name>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="3" type="ROOT" class="TRASH">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="0" wordCount="0" paraCount="0" cursorPos="36"/>
|
||||
<name status="s000000" import="i000007" active="yes">Delete Me!</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -1,346 +1,478 @@
|
||||
[
|
||||
{
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Novel",
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL",
|
||||
"expanded": true,
|
||||
"status": "s000002"
|
||||
"name": "Novel",
|
||||
"itemAttr": {
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Title Page",
|
||||
"itemAttr": {
|
||||
"handle": "53b69b83cdafc",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 72,
|
||||
"wordCount": 15,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 78
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Page",
|
||||
"itemAttr": {
|
||||
"handle": "974e400180a99",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 210,
|
||||
"wordCount": 40,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 213
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Part One",
|
||||
"itemAttr": {
|
||||
"handle": "edca4be2fcaf8",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 23,
|
||||
"wordCount": 5,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "e7ded148d6e4a",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"heading": "H0",
|
||||
"label": "A Folder",
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL",
|
||||
"expanded": true,
|
||||
"status": "s000003"
|
||||
"name": "A Folder",
|
||||
"itemAttr": {
|
||||
"handle": "e7ded148d6e4a",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Chapter One",
|
||||
"itemAttr": {
|
||||
"handle": "6a2d6d5f4f401",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 12,
|
||||
"wordCount": 3,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 215
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Making a Scene",
|
||||
"itemAttr": {
|
||||
"handle": "636b6aa9b697b",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 1483,
|
||||
"wordCount": 263,
|
||||
"paraCount": 8,
|
||||
"cursorPos": 1086
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Another Scene",
|
||||
"itemAttr": {
|
||||
"handle": "bc0cbd2a407f3",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 476,
|
||||
"wordCount": 93,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 428
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Interlude",
|
||||
"itemAttr": {
|
||||
"handle": "ba8a28a246524",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 633,
|
||||
"wordCount": 101,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 1238
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "A Note on Structure",
|
||||
"itemAttr": {
|
||||
"handle": "96b68994dfa3d",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 4,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 1692,
|
||||
"wordCount": 313,
|
||||
"paraCount": 6,
|
||||
"cursorPos": 1721
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": false,
|
||||
"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"
|
||||
"name": "Chapter Two",
|
||||
"itemAttr": {
|
||||
"handle": "88706ddc78b1b",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 5,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 139,
|
||||
"wordCount": 28,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 343
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "We Found John!",
|
||||
"itemAttr": {
|
||||
"handle": "ae7339df26ded",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 6,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 189,
|
||||
"wordCount": 37,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 224
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000003"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"heading": "H0",
|
||||
"label": "Characters",
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"import": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Main Characters",
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Main Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "John Smith",
|
||||
"itemAttr": {
|
||||
"handle": "14298de4d9524",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 49,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 24
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Jane Smith",
|
||||
"itemAttr": {
|
||||
"handle": "bb2c23b3c42cc",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 55,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 25
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"import": "i000009"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"heading": "H0",
|
||||
"label": "Locations",
|
||||
"type": "ROOT",
|
||||
"class": "WORLD",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Locations",
|
||||
"itemAttr": {
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "ROOT",
|
||||
"class": "WORLD"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Earth",
|
||||
"itemAttr": {
|
||||
"handle": "b3e74dbc1f584",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 76,
|
||||
"wordCount": 15,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 20
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Space",
|
||||
"itemAttr": {
|
||||
"handle": "f1471bef9f2ae",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 115,
|
||||
"wordCount": 24,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 133
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Mars",
|
||||
"itemAttr": {
|
||||
"handle": "5eaea4e8cdee8",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 28,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 45
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"import": "i000009"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"heading": "H0",
|
||||
"label": "Trash",
|
||||
"type": "ROOT",
|
||||
"class": "TRASH",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Trash",
|
||||
"itemAttr": {
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "ROOT",
|
||||
"class": "TRASH"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Delete Me!",
|
||||
"itemAttr": {
|
||||
"handle": "b8136a5a774a0",
|
||||
"parent": "98acd8c76c93a",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 30,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 36
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
<?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>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2020-06-26 21:20:24">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||
<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>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>None</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<novelWordCount>0</novelWordCount>
|
||||
<notesWordCount>0</notesWordCount>
|
||||
<spellChecking auto="yes">None</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -50,94 +44,94 @@
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="22">
|
||||
<content items="22" novelWords="0" notesWords="0">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="72" wordCount="15" paraCount="2" cursorPos="78"/>
|
||||
<name status="s000002" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="210" wordCount="40" paraCount="2" cursorPos="213"/>
|
||||
<name status="s000000" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="23" wordCount="5" paraCount="1" cursorPos="0"/>
|
||||
<name status="s000000" import="i000007" active="yes">Part One</name>
|
||||
</item>
|
||||
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="None" order="3" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="12" wordCount="3" paraCount="0" cursorPos="215"/>
|
||||
<name status="s000001" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="1483" wordCount="263" paraCount="8" cursorPos="1086"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="476" wordCount="93" paraCount="3" cursorPos="428"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="633" wordCount="101" paraCount="3" cursorPos="1238"/>
|
||||
<name status="s000006" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="1692" wordCount="313" paraCount="6" cursorPos="1721"/>
|
||||
<name status="s000004" import="i000007" active="no">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>
|
||||
<meta expanded="no" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
|
||||
<name status="s000003" import="i000007" active="yes">We Found John!</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000007">Characters</name>
|
||||
</item>
|
||||
<item handle="f7e2d9f330615" parent="f6622b4617424" root="None" order="0" type="FOLDER" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||
<name status="s000000" import="i000008" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||
<name status="s000000" import="i000009" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||
<name status="s000000" import="i00000a" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||
<name status="s000000" import="i000008" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||
<name status="s000000" import="i000009" active="yes">Mars</name>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="3" type="ROOT" class="TRASH">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="s000000" import="i000007" active="yes">Delete Me!</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -1,387 +1,537 @@
|
||||
[
|
||||
{
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Novel",
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL",
|
||||
"expanded": true,
|
||||
"status": "s000002"
|
||||
"name": "Novel",
|
||||
"itemAttr": {
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Title Page",
|
||||
"itemAttr": {
|
||||
"handle": "53b69b83cdafc",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 241,
|
||||
"wordCount": 42,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 252
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Page",
|
||||
"itemAttr": {
|
||||
"handle": "974e400180a99",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 125,
|
||||
"wordCount": 26,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 127
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Part One",
|
||||
"itemAttr": {
|
||||
"handle": "edca4be2fcaf8",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 26,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 30
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "e7ded148d6e4a",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"heading": "H0",
|
||||
"label": "A Folder",
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL",
|
||||
"expanded": true,
|
||||
"status": "s000003"
|
||||
"name": "A Folder",
|
||||
"itemAttr": {
|
||||
"handle": "e7ded148d6e4a",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Chapter One",
|
||||
"itemAttr": {
|
||||
"handle": "6a2d6d5f4f401",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 75,
|
||||
"wordCount": 14,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 279
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Making a Scene",
|
||||
"itemAttr": {
|
||||
"handle": "636b6aa9b697b",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 2429,
|
||||
"wordCount": 432,
|
||||
"paraCount": 14,
|
||||
"cursorPos": 61
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Another Scene",
|
||||
"itemAttr": {
|
||||
"handle": "bc0cbd2a407f3",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 476,
|
||||
"wordCount": 93,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 577
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Interlude",
|
||||
"itemAttr": {
|
||||
"handle": "ba8a28a246524",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 617,
|
||||
"wordCount": 101,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 1137
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "A Note on Structure",
|
||||
"itemAttr": {
|
||||
"handle": "96b68994dfa3d",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 4,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 1692,
|
||||
"wordCount": 313,
|
||||
"paraCount": 6,
|
||||
"cursorPos": 1110
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": false,
|
||||
"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"
|
||||
"name": "Chapter Two",
|
||||
"itemAttr": {
|
||||
"handle": "88706ddc78b1b",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 5,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 139,
|
||||
"wordCount": 28,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 343
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "We Found John!",
|
||||
"itemAttr": {
|
||||
"handle": "ae7339df26ded",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 6,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 189,
|
||||
"wordCount": 37,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 224
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000003"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"heading": "H0",
|
||||
"label": "Characters",
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"import": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Main Characters",
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Main Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "John Smith",
|
||||
"itemAttr": {
|
||||
"handle": "14298de4d9524",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 49,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 24
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Jane Smith",
|
||||
"itemAttr": {
|
||||
"handle": "bb2c23b3c42cc",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 55,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 25
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"import": "i000009"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"heading": "H0",
|
||||
"label": "Locations",
|
||||
"type": "ROOT",
|
||||
"class": "WORLD",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Locations",
|
||||
"itemAttr": {
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "ROOT",
|
||||
"class": "WORLD"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Earth",
|
||||
"itemAttr": {
|
||||
"handle": "b3e74dbc1f584",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 76,
|
||||
"wordCount": 15,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 20
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Space",
|
||||
"itemAttr": {
|
||||
"handle": "f1471bef9f2ae",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 115,
|
||||
"wordCount": 24,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 133
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Mars",
|
||||
"itemAttr": {
|
||||
"handle": "5eaea4e8cdee8",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 28,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 45
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"import": "i000009"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "6827118336ac1",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"heading": "H0",
|
||||
"label": "Outtakes",
|
||||
"type": "ROOT",
|
||||
"class": "ARCHIVE",
|
||||
"expanded": true,
|
||||
"status": null
|
||||
"name": "Outtakes",
|
||||
"itemAttr": {
|
||||
"handle": "6827118336ac1",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "ROOT",
|
||||
"class": "ARCHIVE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "ae9bf3c3ea159",
|
||||
"parent": "6827118336ac1",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Scenes",
|
||||
"type": "FOLDER",
|
||||
"class": "ARCHIVE",
|
||||
"expanded": true,
|
||||
"status": null
|
||||
"name": "Scenes",
|
||||
"itemAttr": {
|
||||
"handle": "ae9bf3c3ea159",
|
||||
"parent": "6827118336ac1",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FOLDER",
|
||||
"class": "ARCHIVE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Old File",
|
||||
"itemAttr": {
|
||||
"handle": "8a5deb88c0e97",
|
||||
"parent": "ae9bf3c3ea159",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 315,
|
||||
"wordCount": 55,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 322
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000003"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 4,
|
||||
"heading": "H0",
|
||||
"label": "Trash",
|
||||
"type": "ROOT",
|
||||
"class": "TRASH",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Trash",
|
||||
"itemAttr": {
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 4,
|
||||
"type": "ROOT",
|
||||
"class": "TRASH"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Delete Me!",
|
||||
"itemAttr": {
|
||||
"handle": "b8136a5a774a0",
|
||||
"parent": "98acd8c76c93a",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 30,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 36
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
<?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>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2021-08-30 23:33:44">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||
<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>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>en_GB</spellLang>
|
||||
<novelWordCount>840</novelWordCount>
|
||||
<notesWordCount>376</notesWordCount>
|
||||
<spellChecking auto="yes">en_GB</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -50,106 +44,106 @@
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="25">
|
||||
<content items="25" novelWords="840" notesWords="376">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="241" wordCount="42" paraCount="3" cursorPos="252"/>
|
||||
<name status="s000002" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="125" wordCount="26" paraCount="2" cursorPos="127"/>
|
||||
<name status="s000000" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="26" wordCount="6" paraCount="1" cursorPos="30"/>
|
||||
<name status="s000000" import="i000007" active="yes">Part One</name>
|
||||
</item>
|
||||
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="None" order="3" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="75" wordCount="14" paraCount="1" cursorPos="279"/>
|
||||
<name status="s000001" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="2429" wordCount="432" paraCount="14" cursorPos="61"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="476" wordCount="93" paraCount="3" cursorPos="577"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="617" wordCount="101" paraCount="3" cursorPos="1137"/>
|
||||
<name status="s000000" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="1692" wordCount="313" paraCount="6" cursorPos="1110"/>
|
||||
<name status="s000004" import="i000007" active="no">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>
|
||||
<meta expanded="no" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
|
||||
<name status="s000003" import="i000007" active="yes">We Found John!</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000007">Characters</name>
|
||||
</item>
|
||||
<item handle="f7e2d9f330615" parent="f6622b4617424" root="None" order="0" type="FOLDER" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||
<name status="s000000" import="i000008" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||
<name status="s000000" import="i000009" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||
<name status="s000000" import="i00000a" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||
<name status="s000000" import="i000008" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||
<name status="s000000" import="i000009" active="yes">Mars</name>
|
||||
</item>
|
||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="3" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000007">Outtakes</name>
|
||||
</item>
|
||||
<item handle="ae9bf3c3ea159" parent="6827118336ac1" root="None" order="0" type="FOLDER" class="ARCHIVE">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="315" wordCount="55" paraCount="1" cursorPos="322"/>
|
||||
<name status="s000003" import="i000007" active="yes">Old File</name>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="4" type="ROOT" class="TRASH">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="s000000" import="i000007" active="yes">Delete Me!</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -1,387 +1,537 @@
|
||||
[
|
||||
{
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Novel",
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL",
|
||||
"expanded": true,
|
||||
"status": "s000002"
|
||||
"name": "Novel",
|
||||
"itemAttr": {
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Title Page",
|
||||
"itemAttr": {
|
||||
"handle": "53b69b83cdafc",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 93,
|
||||
"wordCount": 19,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 2
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Page",
|
||||
"itemAttr": {
|
||||
"handle": "974e400180a99",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 186,
|
||||
"wordCount": 39,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 212
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Part One",
|
||||
"itemAttr": {
|
||||
"handle": "edca4be2fcaf8",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 26,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 33
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "e7ded148d6e4a",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"heading": "H0",
|
||||
"label": "A Folder",
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL",
|
||||
"expanded": true,
|
||||
"status": "s000003"
|
||||
"name": "A Folder",
|
||||
"itemAttr": {
|
||||
"handle": "e7ded148d6e4a",
|
||||
"parent": "7031beac91f75",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Chapter One",
|
||||
"itemAttr": {
|
||||
"handle": "6a2d6d5f4f401",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 75,
|
||||
"wordCount": 14,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 279
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Making a Scene",
|
||||
"itemAttr": {
|
||||
"handle": "636b6aa9b697b",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 2429,
|
||||
"wordCount": 432,
|
||||
"paraCount": 14,
|
||||
"cursorPos": 62
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Another Scene",
|
||||
"itemAttr": {
|
||||
"handle": "bc0cbd2a407f3",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 476,
|
||||
"wordCount": 93,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 577
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Interlude",
|
||||
"itemAttr": {
|
||||
"handle": "ba8a28a246524",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 617,
|
||||
"wordCount": 101,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 4
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "A Note on Structure",
|
||||
"itemAttr": {
|
||||
"handle": "96b68994dfa3d",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 4,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 1692,
|
||||
"wordCount": 313,
|
||||
"paraCount": 6,
|
||||
"cursorPos": 1110
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": false,
|
||||
"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"
|
||||
"name": "Chapter Two",
|
||||
"itemAttr": {
|
||||
"handle": "88706ddc78b1b",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 5,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 139,
|
||||
"wordCount": 28,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 343
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "We Found John!",
|
||||
"itemAttr": {
|
||||
"handle": "ae7339df26ded",
|
||||
"parent": "e7ded148d6e4a",
|
||||
"root": null,
|
||||
"order": 6,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 189,
|
||||
"wordCount": 37,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 224
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000003"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"heading": "H0",
|
||||
"label": "Characters",
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"import": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Main Characters",
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Main Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "John Smith",
|
||||
"itemAttr": {
|
||||
"handle": "14298de4d9524",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 49,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 24
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Jane Smith",
|
||||
"itemAttr": {
|
||||
"handle": "bb2c23b3c42cc",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 55,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 25
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"import": "i000009"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"heading": "H0",
|
||||
"label": "Locations",
|
||||
"type": "ROOT",
|
||||
"class": "WORLD",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Locations",
|
||||
"itemAttr": {
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "ROOT",
|
||||
"class": "WORLD"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Earth",
|
||||
"itemAttr": {
|
||||
"handle": "b3e74dbc1f584",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 76,
|
||||
"wordCount": 15,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 20
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Space",
|
||||
"itemAttr": {
|
||||
"handle": "f1471bef9f2ae",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 115,
|
||||
"wordCount": 24,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 133
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"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"
|
||||
"name": "Mars",
|
||||
"itemAttr": {
|
||||
"handle": "5eaea4e8cdee8",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": null,
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 28,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 45
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"import": "i000009"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "6827118336ac1",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"heading": "H0",
|
||||
"label": "Archive",
|
||||
"type": "ROOT",
|
||||
"class": "ARCHIVE",
|
||||
"expanded": true,
|
||||
"status": "s000000"
|
||||
"name": "Archive",
|
||||
"itemAttr": {
|
||||
"handle": "6827118336ac1",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 3,
|
||||
"type": "ROOT",
|
||||
"class": "ARCHIVE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "ae9bf3c3ea159",
|
||||
"parent": "6827118336ac1",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"heading": "H0",
|
||||
"label": "Scenes",
|
||||
"type": "FOLDER",
|
||||
"class": "ARCHIVE",
|
||||
"expanded": true,
|
||||
"status": "s000000"
|
||||
"name": "Scenes",
|
||||
"itemAttr": {
|
||||
"handle": "ae9bf3c3ea159",
|
||||
"parent": "6827118336ac1",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FOLDER",
|
||||
"class": "ARCHIVE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Old File",
|
||||
"itemAttr": {
|
||||
"handle": "8a5deb88c0e97",
|
||||
"parent": "ae9bf3c3ea159",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 314,
|
||||
"wordCount": 55,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 322
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000003"
|
||||
}
|
||||
},
|
||||
{
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 4,
|
||||
"heading": "H0",
|
||||
"label": "Trash",
|
||||
"type": "ROOT",
|
||||
"class": "TRASH",
|
||||
"expanded": true,
|
||||
"import": null
|
||||
"name": "Trash",
|
||||
"itemAttr": {
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": null,
|
||||
"order": 4,
|
||||
"type": "ROOT",
|
||||
"class": "TRASH"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"expanded": true
|
||||
},
|
||||
"nameAttr": {
|
||||
"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"
|
||||
"name": "Delete Me!",
|
||||
"itemAttr": {
|
||||
"handle": "b8136a5a774a0",
|
||||
"parent": "98acd8c76c93a",
|
||||
"root": null,
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"heading": "H0",
|
||||
"charCount": 30,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 36
|
||||
},
|
||||
"nameAttr": {
|
||||
"active": true,
|
||||
"status": "s000000"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
<?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>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-10-25 18:26:15">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||
<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>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>en_GB</spellLang>
|
||||
<novelWordCount>830</novelWordCount>
|
||||
<notesWordCount>376</notesWordCount>
|
||||
<spellChecking auto="yes">en_GB</spellChecking>
|
||||
<lastHandle>
|
||||
<entry key="editor">None</entry>
|
||||
<entry key="viewer">None</entry>
|
||||
@@ -50,106 +44,106 @@
|
||||
<entry key="i00000a" count="0" red="117" green="0" blue="175">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content count="25">
|
||||
<content items="25" novelWords="830" notesWords="376">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="93" wordCount="19" paraCount="2" cursorPos="2"/>
|
||||
<name status="s000002" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="186" wordCount="39" paraCount="2" cursorPos="212"/>
|
||||
<name status="s000000" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="26" wordCount="6" paraCount="1" cursorPos="33"/>
|
||||
<name status="s000000" import="i000007" active="yes">Part One</name>
|
||||
</item>
|
||||
<item handle="e7ded148d6e4a" parent="7031beac91f75" root="None" order="3" type="FOLDER" class="NOVEL">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="75" wordCount="14" paraCount="1" cursorPos="279"/>
|
||||
<name status="s000001" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="2429" wordCount="432" paraCount="14" cursorPos="62"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="476" wordCount="93" paraCount="3" cursorPos="577"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="617" wordCount="101" paraCount="3" cursorPos="4"/>
|
||||
<name status="s000000" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="1692" wordCount="313" paraCount="6" cursorPos="1110"/>
|
||||
<name status="s000004" import="i000007" active="no">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>
|
||||
<meta expanded="no" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="343"/>
|
||||
<name status="s000003" import="i000007" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
|
||||
<name status="s000003" import="i000007" active="yes">We Found John!</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000007">Characters</name>
|
||||
</item>
|
||||
<item handle="f7e2d9f330615" parent="f6622b4617424" root="None" order="0" type="FOLDER" class="CHARACTER">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||
<name status="s000000" import="i000008" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||
<name status="s000000" import="i000009" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||
<name status="s000000" import="i00000a" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||
<name status="s000000" import="i000008" active="yes">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>
|
||||
<meta expanded="no" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||
<name status="s000000" import="i000009" active="yes">Mars</name>
|
||||
</item>
|
||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="3" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<name status="s000000" import="i000007">Archive</name>
|
||||
</item>
|
||||
<item handle="ae9bf3c3ea159" parent="6827118336ac1" root="None" order="0" type="FOLDER" class="ARCHIVE">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="314" wordCount="55" paraCount="1" cursorPos="322"/>
|
||||
<name status="s000003" import="i000007" active="yes">Old File</name>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="4" type="ROOT" class="TRASH">
|
||||
<meta expanded="True"/>
|
||||
<meta expanded="yes"/>
|
||||
<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>
|
||||
<meta expanded="no" heading="H0" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="s000000" import="i000007" active="yes">Delete Me!</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
|
||||
@@ -0,0 +1,677 @@
|
||||
[
|
||||
{
|
||||
"name": "Novel",
|
||||
"itemAttr": {
|
||||
"handle": "7031beac91f75",
|
||||
"parent": null,
|
||||
"root": "7031beac91f75",
|
||||
"order": 0,
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL",
|
||||
"layout": "NO_LAYOUT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sc24b8f",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Title Page",
|
||||
"itemAttr": {
|
||||
"handle": "53b69b83cdafc",
|
||||
"parent": "7031beac91f75",
|
||||
"root": "7031beac91f75",
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 93,
|
||||
"wordCount": 19,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 119
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sc24b8f",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Page",
|
||||
"itemAttr": {
|
||||
"handle": "974e400180a99",
|
||||
"parent": "7031beac91f75",
|
||||
"root": "7031beac91f75",
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 251,
|
||||
"wordCount": 50,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 277
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Part One",
|
||||
"itemAttr": {
|
||||
"handle": "edca4be2fcaf8",
|
||||
"parent": "7031beac91f75",
|
||||
"root": "7031beac91f75",
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 26,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 36
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s90e6c9",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Chapter One",
|
||||
"itemAttr": {
|
||||
"handle": "6a2d6d5f4f401",
|
||||
"parent": "7031beac91f75",
|
||||
"root": "7031beac91f75",
|
||||
"order": 3,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 95,
|
||||
"wordCount": 18,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 291
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf24ce6",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Making a Scene",
|
||||
"itemAttr": {
|
||||
"handle": "636b6aa9b697b",
|
||||
"parent": "6a2d6d5f4f401",
|
||||
"root": "7031beac91f75",
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 2687,
|
||||
"wordCount": 479,
|
||||
"paraCount": 14,
|
||||
"cursorPos": 67
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s90e6c9",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Another Scene",
|
||||
"itemAttr": {
|
||||
"handle": "bc0cbd2a407f3",
|
||||
"parent": "6a2d6d5f4f401",
|
||||
"root": "7031beac91f75",
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 548,
|
||||
"wordCount": 108,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 465
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s90e6c9",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Interlude",
|
||||
"itemAttr": {
|
||||
"handle": "ba8a28a246524",
|
||||
"parent": "7031beac91f75",
|
||||
"root": "7031beac91f75",
|
||||
"order": 4,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 617,
|
||||
"wordCount": 101,
|
||||
"paraCount": 3,
|
||||
"cursorPos": 310
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s78ea90",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "A Note on Structure",
|
||||
"itemAttr": {
|
||||
"handle": "96b68994dfa3d",
|
||||
"parent": "7031beac91f75",
|
||||
"root": "7031beac91f75",
|
||||
"order": 5,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 1909,
|
||||
"wordCount": 346,
|
||||
"paraCount": 7,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf24ce6",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Chapter Two",
|
||||
"itemAttr": {
|
||||
"handle": "88706ddc78b1b",
|
||||
"parent": "7031beac91f75",
|
||||
"root": "7031beac91f75",
|
||||
"order": 6,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 139,
|
||||
"wordCount": 28,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 188
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s90e6c9",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "We Found John!",
|
||||
"itemAttr": {
|
||||
"handle": "ae7339df26ded",
|
||||
"parent": "88706ddc78b1b",
|
||||
"root": "7031beac91f75",
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 189,
|
||||
"wordCount": 37,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s90e6c9",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Sequel",
|
||||
"itemAttr": {
|
||||
"handle": "e5e47ebf63b1c",
|
||||
"parent": null,
|
||||
"root": "e5e47ebf63b1c",
|
||||
"order": 1,
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL",
|
||||
"layout": "NO_LAYOUT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Title Page",
|
||||
"itemAttr": {
|
||||
"handle": "bacb7059e3083",
|
||||
"parent": "e5e47ebf63b1c",
|
||||
"root": "e5e47ebf63b1c",
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 27,
|
||||
"wordCount": 5,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 100
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sc24b8f",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Chapter One",
|
||||
"itemAttr": {
|
||||
"handle": "a520879ca0b45",
|
||||
"parent": "e5e47ebf63b1c",
|
||||
"root": "e5e47ebf63b1c",
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 299,
|
||||
"wordCount": 55,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 104
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s90e6c9",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f6622b4617424",
|
||||
"parent": null,
|
||||
"root": "f6622b4617424",
|
||||
"order": 2,
|
||||
"type": "ROOT",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NO_LAYOUT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Main Characters",
|
||||
"itemAttr": {
|
||||
"handle": "f7e2d9f330615",
|
||||
"parent": "f6622b4617424",
|
||||
"root": "f6622b4617424",
|
||||
"order": 0,
|
||||
"type": "FOLDER",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NO_LAYOUT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "John Smith",
|
||||
"itemAttr": {
|
||||
"handle": "14298de4d9524",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": "f6622b4617424",
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 49,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 24
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "icfb3a5",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Jane Smith",
|
||||
"itemAttr": {
|
||||
"handle": "bb2c23b3c42cc",
|
||||
"parent": "f7e2d9f330615",
|
||||
"root": "f6622b4617424",
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "CHARACTER",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 55,
|
||||
"wordCount": 9,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 25
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "i2d7a54",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Locations",
|
||||
"itemAttr": {
|
||||
"handle": "15c4492bd5107",
|
||||
"parent": null,
|
||||
"root": "15c4492bd5107",
|
||||
"order": 3,
|
||||
"type": "ROOT",
|
||||
"class": "WORLD",
|
||||
"layout": "NO_LAYOUT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Earth",
|
||||
"itemAttr": {
|
||||
"handle": "b3e74dbc1f584",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": "15c4492bd5107",
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 76,
|
||||
"wordCount": 15,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 20
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "i56be10",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Space",
|
||||
"itemAttr": {
|
||||
"handle": "f1471bef9f2ae",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": "15c4492bd5107",
|
||||
"order": 1,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 115,
|
||||
"wordCount": 24,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 133
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "icfb3a5",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Mars",
|
||||
"itemAttr": {
|
||||
"handle": "5eaea4e8cdee8",
|
||||
"parent": "15c4492bd5107",
|
||||
"root": "15c4492bd5107",
|
||||
"order": 2,
|
||||
"type": "FILE",
|
||||
"class": "WORLD",
|
||||
"layout": "NOTE"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 28,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 45
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "i2d7a54",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Archive",
|
||||
"itemAttr": {
|
||||
"handle": "6827118336ac1",
|
||||
"parent": null,
|
||||
"root": "6827118336ac1",
|
||||
"order": 4,
|
||||
"type": "ROOT",
|
||||
"class": "ARCHIVE",
|
||||
"layout": "NO_LAYOUT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Scenes",
|
||||
"itemAttr": {
|
||||
"handle": "ae9bf3c3ea159",
|
||||
"parent": "6827118336ac1",
|
||||
"root": "6827118336ac1",
|
||||
"order": 0,
|
||||
"type": "FOLDER",
|
||||
"class": "ARCHIVE",
|
||||
"layout": "NO_LAYOUT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Old File",
|
||||
"itemAttr": {
|
||||
"handle": "8a5deb88c0e97",
|
||||
"parent": "ae9bf3c3ea159",
|
||||
"root": "6827118336ac1",
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "ARCHIVE",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 232,
|
||||
"wordCount": 42,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 239
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s90e6c9",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Trash",
|
||||
"itemAttr": {
|
||||
"handle": "98acd8c76c93a",
|
||||
"parent": null,
|
||||
"root": "98acd8c76c93a",
|
||||
"order": 5,
|
||||
"type": "ROOT",
|
||||
"class": "TRASH",
|
||||
"layout": "NO_LAYOUT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": true,
|
||||
"heading": "H0",
|
||||
"charCount": 0,
|
||||
"wordCount": 0,
|
||||
"paraCount": 0,
|
||||
"cursorPos": 0
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delete Me!",
|
||||
"itemAttr": {
|
||||
"handle": "b8136a5a774a0",
|
||||
"parent": "98acd8c76c93a",
|
||||
"root": "98acd8c76c93a",
|
||||
"order": 0,
|
||||
"type": "FILE",
|
||||
"class": "TRASH",
|
||||
"layout": "DOCUMENT"
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": false,
|
||||
"heading": "H0",
|
||||
"charCount": 30,
|
||||
"wordCount": 6,
|
||||
"paraCount": 1,
|
||||
"cursorPos": 36
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "sf12341",
|
||||
"import": "ia857f0",
|
||||
"active": true
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,157 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-10-15 12:12:59">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>yes</doBackup>
|
||||
<language>en_GB</language>
|
||||
<spellChecking auto="yes">en_GB</spellChecking>
|
||||
<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="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 items="27" novelWords="954" notesWords="409">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H0" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="277"/>
|
||||
<name status="sf12341" import="ia857f0" active="yes">Page</name>
|
||||
</item>
|
||||
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="26" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Part One</name>
|
||||
</item>
|
||||
<item handle="6a2d6d5f4f401" parent="7031beac91f75" root="7031beac91f75" order="3" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="yes" heading="H0" charCount="95" wordCount="18" paraCount="1" cursorPos="291"/>
|
||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="2687" wordCount="479" paraCount="14" cursorPos="67"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="548" wordCount="108" paraCount="3" cursorPos="465"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Another Scene</name>
|
||||
</item>
|
||||
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="617" wordCount="101" paraCount="3" cursorPos="310"/>
|
||||
<name status="s78ea90" import="ia857f0" active="yes">Interlude</name>
|
||||
</item>
|
||||
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
||||
<meta expanded="no" heading="H0" charCount="1909" wordCount="346" paraCount="7" cursorPos="0"/>
|
||||
<name status="sf24ce6" import="ia857f0" active="no">A Note on Structure</name>
|
||||
</item>
|
||||
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="yes" heading="H0" charCount="139" wordCount="28" paraCount="1" cursorPos="188"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Chapter Two</name>
|
||||
</item>
|
||||
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="189" wordCount="37" paraCount="1" cursorPos="0"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">We Found John!</name>
|
||||
</item>
|
||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H0" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||
<meta expanded="yes"/>
|
||||
<name status="sf12341" import="ia857f0">Characters</name>
|
||||
</item>
|
||||
<item handle="f7e2d9f330615" parent="f6622b4617424" root="f6622b4617424" order="0" type="FOLDER" class="CHARACTER">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H0" charCount="49" wordCount="9" paraCount="1" cursorPos="24"/>
|
||||
<name status="sf12341" import="icfb3a5" active="yes">John Smith</name>
|
||||
</item>
|
||||
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||
<meta expanded="no" heading="H0" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Jane Smith</name>
|
||||
</item>
|
||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H0" charCount="76" wordCount="15" paraCount="1" cursorPos="20"/>
|
||||
<name status="sf12341" import="i56be10" active="yes">Earth</name>
|
||||
</item>
|
||||
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="no" heading="H0" charCount="115" wordCount="24" paraCount="1" cursorPos="133"/>
|
||||
<name status="sf12341" import="icfb3a5" active="yes">Space</name>
|
||||
</item>
|
||||
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||
<meta expanded="no" heading="H0" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
|
||||
<name status="sf12341" import="i2d7a54" active="yes">Mars</name>
|
||||
</item>
|
||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||
<meta expanded="yes"/>
|
||||
<name status="sf12341" import="ia857f0">Archive</name>
|
||||
</item>
|
||||
<item handle="ae9bf3c3ea159" parent="6827118336ac1" root="6827118336ac1" order="0" type="FOLDER" class="ARCHIVE">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H0" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Old File</name>
|
||||
</item>
|
||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
||||
<meta expanded="yes"/>
|
||||
<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="no" heading="H0" charCount="30" wordCount="6" paraCount="1" cursorPos="36"/>
|
||||
<name status="sf12341" import="ia857f0" active="yes">Delete Me!</name>
|
||||
</item>
|
||||
</content>
|
||||
</novelWriterXML>
|
||||
@@ -30,11 +30,11 @@ from tools import writeFile
|
||||
from novelwriter.guimain import GuiMain
|
||||
from novelwriter.common import (
|
||||
checkStringNone, checkString, checkInt, checkFloat, checkBool, checkHandle,
|
||||
isHandle, isTitleTag, isItemClass, isItemType, isItemLayout, hexToInt,
|
||||
minmax, checkIntTuple, formatInt, formatTimeStamp, formatTime, simplified,
|
||||
splitVersionNumber, transferCase, fuzzyTime, numberToRoman, jsonEncode,
|
||||
readTextFile, makeFileNameSafe, ensureFolder, sha256sum, getGuiItem,
|
||||
NWConfigParser
|
||||
checkUuid, isHandle, isTitleTag, isItemClass, isItemType, isItemLayout,
|
||||
hexToInt, minmax, checkIntTuple, formatInt, formatTimeStamp, formatTime,
|
||||
simplified, yesNo, splitVersionNumber, transferCase, fuzzyTime,
|
||||
numberToRoman, jsonEncode, readTextFile, makeFileNameSafe, ensureFolder,
|
||||
sha256sum, getGuiItem, NWConfigParser
|
||||
)
|
||||
|
||||
|
||||
@@ -105,16 +105,41 @@ def testBaseCommon_CheckBool():
|
||||
bool, or integer 1 or 0, are returned as bool. Otherwise, the
|
||||
default is returned.
|
||||
"""
|
||||
# Bools
|
||||
assert checkBool(True, False) is True
|
||||
assert checkBool(False, True) is False
|
||||
|
||||
# Valid Strings
|
||||
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("true", False) is True
|
||||
assert checkBool("false", True) is False
|
||||
assert checkBool("Yes", False) is True
|
||||
assert checkBool("No", True) is False
|
||||
assert checkBool("yes", False) is True
|
||||
assert checkBool("no", True) is False
|
||||
assert checkBool("On", False) is True
|
||||
assert checkBool("Off", True) is False
|
||||
assert checkBool("on", False) is True
|
||||
assert checkBool("off", True) is False
|
||||
|
||||
# Invalid Strings
|
||||
assert checkBool("Foo", False) is False
|
||||
assert checkBool("Foo", True) is True
|
||||
assert checkBool("bar", False) is False
|
||||
assert checkBool("bar", True) is True
|
||||
|
||||
# Valid Integers
|
||||
assert checkBool(0, True) is False
|
||||
assert checkBool(1, False) is True
|
||||
|
||||
# Inalid Integers
|
||||
assert checkBool(2, True) is True
|
||||
assert checkBool(2, False) is False
|
||||
|
||||
# Other Types
|
||||
assert checkBool(None, True) is True
|
||||
assert checkBool(None, False) is False
|
||||
assert checkBool(0.0, True) is True
|
||||
assert checkBool(1.0, False) is False
|
||||
assert checkBool(2.0, True) is True
|
||||
@@ -137,6 +162,20 @@ def testBaseCommon_CheckHandle():
|
||||
# END Test testBaseCommon_CheckHandle
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_CheckUuid():
|
||||
"""Test the checkUuid function.
|
||||
"""
|
||||
testUuid = "e2be99af-f9bf-4403-857a-c3d1ac25abea"
|
||||
assert checkUuid("", None) is None
|
||||
assert checkUuid("e2be99af-f9bf-4403-857a-c3d1ac25abe", None) is None
|
||||
assert checkUuid("e2be99af-f9bf-qq03-857a-c3d1ac25abea", None) is None
|
||||
assert checkUuid("e2be99af-f9bf-4403-857a-c3d1ac25abeaa", None) is None
|
||||
assert checkUuid(testUuid, None) == testUuid
|
||||
|
||||
# END Test testBaseCommon_CheckUuid
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_IsHandle():
|
||||
"""Test the isHandle function.
|
||||
@@ -317,6 +356,34 @@ def testBaseCommon_Simplified():
|
||||
# END Test testBaseCommon_Simplified
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_YesNo():
|
||||
"""Test the yesNo function.
|
||||
"""
|
||||
# Bool
|
||||
assert yesNo(True) == "yes"
|
||||
assert yesNo(False) == "no"
|
||||
|
||||
# None
|
||||
assert yesNo(None) == "no"
|
||||
|
||||
# String
|
||||
assert yesNo("foo") == "yes"
|
||||
assert yesNo("") == "no"
|
||||
|
||||
# Integer
|
||||
assert yesNo(0) == "no"
|
||||
assert yesNo(1) == "yes"
|
||||
assert yesNo(2) == "yes"
|
||||
|
||||
# Float
|
||||
assert yesNo(0.0) == "no"
|
||||
assert yesNo(1.0) == "yes"
|
||||
assert yesNo(2.0) == "yes"
|
||||
|
||||
# END Test testBaseCommon_YesNo
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseCommon_SplitVersionNumber():
|
||||
"""Test the splitVersionNumber function.
|
||||
|
||||
@@ -20,20 +20,22 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import uuid
|
||||
import pytest
|
||||
|
||||
from shutil import copyfile
|
||||
from zipfile import ZipFile
|
||||
|
||||
from mock import causeOSError
|
||||
from tools import C, buildTestProject, cmpFiles
|
||||
from tools import C, buildTestProject, cmpFiles, XML_IGNORE
|
||||
|
||||
from novelwriter.constants import nwItemClass
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.doctools import DocMerger, DocSplitter
|
||||
from novelwriter.core.document import NWDoc
|
||||
from novelwriter.core.coretools import DocMerger, DocSplitter, ProjectBuilder
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocTools_DocMerger(monkeypatch, mockGUI, fncDir, outDir, refDir, mockRnd, ipsumText):
|
||||
def testCoreTools_DocMerger(monkeypatch, mockGUI, fncDir, outDir, refDir, mockRnd, ipsumText):
|
||||
"""Test the DocMerger utility.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
@@ -118,11 +120,11 @@ def testCoreDocTools_DocMerger(monkeypatch, mockGUI, fncDir, outDir, refDir, moc
|
||||
# Just for debugging
|
||||
docMerger.writeTargetDoc()
|
||||
|
||||
# END Test testCoreDocTools_DocMerger
|
||||
# END Test testCoreTools_DocMerger
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, mockRnd, ipsumText):
|
||||
def testCoreTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, mockRnd, ipsumText):
|
||||
"""Test the DocSplitter utility.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
@@ -161,7 +163,7 @@ def testCoreDocTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, m
|
||||
|
||||
docText = "\n\n".join(docData)
|
||||
docRaw = docText.splitlines()
|
||||
assert NWDoc(theProject, hSplitDoc).writeDocument(docText) is True
|
||||
assert theProject.storage.getDocument(hSplitDoc).writeDocument(docText) is True
|
||||
theProject.tree[hSplitDoc].setStatus(C.sFinished)
|
||||
theProject.tree[hSplitDoc].setImport(C.iMain)
|
||||
|
||||
@@ -258,4 +260,160 @@ def testCoreDocTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, m
|
||||
|
||||
theProject.saveProject()
|
||||
|
||||
# END Test testCoreDocTools_DocSplitter
|
||||
# END Test testCoreTools_DocSplitter
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTools_NewMinimal(monkeypatch, fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
"""Create a new project from a project wizard dictionary. With
|
||||
default setting, creating a Minimal project.
|
||||
"""
|
||||
monkeypatch.setattr("uuid.uuid4", lambda *a: uuid.UUID("d0f3fe10-c6e6-4310-8bfd-181eb4224eed"))
|
||||
|
||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreTools_NewMinimal_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreTools_NewMinimal_nwProject.nwx")
|
||||
|
||||
projBuild = ProjectBuilder(mockGUI)
|
||||
|
||||
# Setting no data should fail
|
||||
assert projBuild.buildProject({}) is False
|
||||
|
||||
# Wrong type should also fail
|
||||
assert projBuild.buildProject("stuff") is False
|
||||
|
||||
# Try again with a proper path
|
||||
assert projBuild.buildProject({"projPath": fncDir}) is True
|
||||
|
||||
# Creating the project once more should fail
|
||||
assert projBuild.buildProject({"projPath": fncDir}) is False
|
||||
|
||||
# Save and close
|
||||
copyfile(projFile, testFile)
|
||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||
|
||||
# END Test testCoreTools_NewMinimal
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTools_NewCustomA(monkeypatch, fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
"""Create a new project from a project wizard dictionary.
|
||||
Custom type with chapters and scenes.
|
||||
"""
|
||||
monkeypatch.setattr("uuid.uuid4", lambda *a: uuid.UUID("d0f3fe10-c6e6-4310-8bfd-181eb4224eed"))
|
||||
|
||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreTools_NewCustomA_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreTools_NewCustomA_nwProject.nwx")
|
||||
|
||||
projData = {
|
||||
"projName": "Test Custom",
|
||||
"projTitle": "Test Novel",
|
||||
"projAuthors": "Jane Doe\nJohn Doh\n",
|
||||
"projPath": fncDir,
|
||||
"popSample": False,
|
||||
"popMinimal": False,
|
||||
"popCustom": True,
|
||||
"addRoots": [
|
||||
nwItemClass.PLOT,
|
||||
nwItemClass.CHARACTER,
|
||||
nwItemClass.WORLD,
|
||||
],
|
||||
"addNotes": True,
|
||||
"numChapters": 3,
|
||||
"numScenes": 3,
|
||||
}
|
||||
|
||||
projBuild = ProjectBuilder(mockGUI)
|
||||
assert projBuild.buildProject(projData) is True
|
||||
|
||||
copyfile(projFile, testFile)
|
||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||
|
||||
# END Test testCoreTools_NewCustomA
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTools_NewCustomB(monkeypatch, fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||
"""Create a new project from a project wizard dictionary.
|
||||
Custom type without chapters, but with scenes.
|
||||
"""
|
||||
monkeypatch.setattr("uuid.uuid4", lambda *a: uuid.UUID("d0f3fe10-c6e6-4310-8bfd-181eb4224eed"))
|
||||
|
||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreTools_NewCustomB_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreTools_NewCustomB_nwProject.nwx")
|
||||
|
||||
projData = {
|
||||
"projName": "Test Custom",
|
||||
"projTitle": "Test Novel",
|
||||
"projAuthors": "Jane Doe\nJohn Doh\n",
|
||||
"projPath": fncDir,
|
||||
"popSample": False,
|
||||
"popMinimal": False,
|
||||
"popCustom": True,
|
||||
"addRoots": [
|
||||
nwItemClass.PLOT,
|
||||
nwItemClass.CHARACTER,
|
||||
nwItemClass.WORLD,
|
||||
],
|
||||
"addNotes": True,
|
||||
"numChapters": 0,
|
||||
"numScenes": 6,
|
||||
}
|
||||
|
||||
projBuild = ProjectBuilder(mockGUI)
|
||||
assert projBuild.buildProject(projData) is True
|
||||
|
||||
copyfile(projFile, testFile)
|
||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||
|
||||
# END Test testCoreTools_NewCustomB
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTools_NewSample(fncDir, tmpConf, mockGUI, tmpDir):
|
||||
"""Check that we can create a new project can be created from the
|
||||
provided sample project via a zip file.
|
||||
"""
|
||||
projData = {
|
||||
"projName": "Test Sample",
|
||||
"projTitle": "Test Novel",
|
||||
"projAuthors": "Jane Doe\nJohn Doh\n",
|
||||
"projPath": fncDir,
|
||||
"popSample": True,
|
||||
"popMinimal": False,
|
||||
"popCustom": False,
|
||||
}
|
||||
|
||||
projBuild = ProjectBuilder(mockGUI)
|
||||
|
||||
# No path set
|
||||
assert projBuild.buildProject({"popSample": True}) is False
|
||||
|
||||
# Force the lookup path for assets to our temp folder
|
||||
srcSample = os.path.abspath(os.path.join(tmpConf.appRoot, "sample"))
|
||||
dstSample = os.path.join(tmpDir, "sample.zip")
|
||||
tmpConf.assetPath = tmpDir
|
||||
|
||||
# Cannot extract when the zip does not exist
|
||||
assert projBuild.buildProject(projData) is False
|
||||
|
||||
# Create and open a defective zip file
|
||||
with open(dstSample, mode="w+") as outFile:
|
||||
outFile.write("foo")
|
||||
|
||||
assert projBuild.buildProject(projData) is False
|
||||
os.unlink(dstSample)
|
||||
|
||||
# Create a real zip file, and unpack it
|
||||
with ZipFile(dstSample, "w") as zipObj:
|
||||
zipObj.write(os.path.join(srcSample, "nwProject.nwx"), "nwProject.nwx")
|
||||
for docFile in os.listdir(os.path.join(srcSample, "content")):
|
||||
srcDoc = os.path.join(srcSample, "content", docFile)
|
||||
zipObj.write(srcDoc, "content/"+docFile)
|
||||
|
||||
assert projBuild.buildProject(projData) is True
|
||||
os.unlink(dstSample)
|
||||
|
||||
# END Test testCoreTools_NewSample
|
||||
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
novelWriter – NWDoc Class Tester
|
||||
================================
|
||||
novelWriter – NWDocument Class Tester
|
||||
=====================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2022, Veronica Berglyd Olsen
|
||||
@@ -19,7 +19,6 @@ 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 pytest
|
||||
|
||||
from mock import causeOSError
|
||||
@@ -27,62 +26,74 @@ from tools import C, buildTestProject, readFile, writeFile
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.document import NWDoc
|
||||
from novelwriter.core.document import NWDocument
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
"""Test loading and saving a document with the NWDoc class.
|
||||
def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncPath, mockRnd):
|
||||
"""Test loading and saving a document with the NWDocument class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Read Document
|
||||
# =============
|
||||
|
||||
# Not a valid handle
|
||||
theDoc = NWDoc(theProject, "stuff")
|
||||
theDoc = NWDocument(theProject, "stuff")
|
||||
assert bool(theDoc) is False
|
||||
assert theDoc.readDocument() is None
|
||||
|
||||
# Non-existent handle
|
||||
theDoc = NWDoc(theProject, C.hInvalid)
|
||||
theDoc = NWDocument(theProject, C.hInvalid)
|
||||
assert theDoc.readDocument() is None
|
||||
assert theDoc._currHash is None
|
||||
|
||||
# No content path
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.contentPath", property(lambda *a: None))
|
||||
theDoc = NWDocument(theProject, C.hSceneDoc)
|
||||
assert theDoc.readDocument() is None
|
||||
|
||||
# Cause open() to fail while loading
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeOSError)
|
||||
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||
theDoc = NWDocument(theProject, C.hSceneDoc)
|
||||
assert theDoc.readDocument() is None
|
||||
assert theDoc.getError() == "OSError: Mock OSError"
|
||||
|
||||
# Load the text
|
||||
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||
theDoc = NWDocument(theProject, C.hSceneDoc)
|
||||
assert theDoc.readDocument() == "### New Scene\n\n"
|
||||
|
||||
# Try to open a new (non-existent) file
|
||||
xHandle = theProject.newFile("New File", C.hNovelRoot)
|
||||
theDoc = NWDoc(theProject, xHandle)
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert bool(theDoc) is True
|
||||
assert repr(theDoc) == f"<NWDoc handle={xHandle}>"
|
||||
assert repr(theDoc) == f"<NWDocument handle={xHandle}>"
|
||||
assert theDoc.readDocument() == ""
|
||||
|
||||
# Write Document
|
||||
# ==============
|
||||
|
||||
# Set handle and save again
|
||||
# No content path
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.contentPath", property(lambda *a: None))
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.writeDocument("") is False
|
||||
|
||||
# Set handle and save
|
||||
theText = "### Test File\n\nText ...\n\n"
|
||||
theDoc = NWDoc(theProject, xHandle)
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.readDocument(xHandle) == ""
|
||||
assert theDoc.writeDocument(theText) is True
|
||||
|
||||
# Save again to ensure temp file and previous file is handled
|
||||
assert theDoc.writeDocument(theText)
|
||||
assert theDoc.writeDocument(theText) is True
|
||||
|
||||
# Check file content
|
||||
docPath = os.path.join(fncDir, "content", xHandle+".nwd")
|
||||
docPath = fncPath / "content" / f"{xHandle}.nwd"
|
||||
assert readFile(docPath) == (
|
||||
"%%~name: New File\n"
|
||||
f"%%~path: {C.hNovelRoot}/{xHandle}\n"
|
||||
@@ -114,7 +125,7 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
|
||||
# Cause os.replace() to fail while saving
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("os.replace", causeOSError)
|
||||
mp.setattr("pathlib.Path.replace", causeOSError)
|
||||
assert theDoc.writeDocument(theText) is False
|
||||
assert theDoc.getError() == "OSError: Mock OSError"
|
||||
|
||||
@@ -128,41 +139,47 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, fncDir, mockRnd):
|
||||
# Delete Document
|
||||
# ===============
|
||||
|
||||
# Delete the last document
|
||||
theDoc = NWDoc(theProject, "stuff")
|
||||
# Delete a non-existing document
|
||||
theDoc = NWDocument(theProject, "stuff")
|
||||
assert theDoc.deleteDocument() is False
|
||||
assert os.path.isfile(docPath)
|
||||
assert docPath.exists()
|
||||
|
||||
# No content path
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.contentPath", property(lambda *a: None))
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.deleteDocument() is False
|
||||
|
||||
# Cause the delete to fail
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("os.unlink", causeOSError)
|
||||
theDoc = NWDoc(theProject, xHandle)
|
||||
mp.setattr("pathlib.Path.unlink", causeOSError)
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.deleteDocument() is False
|
||||
assert theDoc.getError() == "OSError: Mock OSError"
|
||||
|
||||
# Make the delete pass
|
||||
theDoc = NWDoc(theProject, xHandle)
|
||||
theDoc = NWDocument(theProject, xHandle)
|
||||
assert theDoc.deleteDocument() is True
|
||||
assert not os.path.isfile(docPath)
|
||||
assert not docPath.exists()
|
||||
|
||||
# END Test testCoreDocument_Load
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_Methods(mockGUI, fncDir, mockRnd):
|
||||
"""Test other methods of the NWDoc class.
|
||||
def testCoreDocument_Methods(mockGUI, fncPath, mockRnd):
|
||||
"""Test other methods of the NWDocument class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncDir)
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
theDoc = NWDoc(theProject, C.hSceneDoc)
|
||||
docPath = os.path.join(fncDir, "content", C.hSceneDoc+".nwd")
|
||||
theDoc = NWDocument(theProject, C.hSceneDoc)
|
||||
docPath = fncPath / "content" / f"{C.hSceneDoc}.nwd"
|
||||
|
||||
assert theDoc.readDocument() == "### New Scene\n\n"
|
||||
|
||||
# Check location
|
||||
assert theDoc.getFileLocation() == docPath
|
||||
assert theDoc.getFileLocation() == str(docPath)
|
||||
|
||||
# Check the item
|
||||
assert theDoc.getCurrentItem() is not None
|
||||
|
||||
@@ -19,28 +19,29 @@ 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 json
|
||||
import pytest
|
||||
|
||||
from shutil import copyfile
|
||||
from pathlib import Path
|
||||
|
||||
from mock import causeException
|
||||
from tools import C, buildTestProject, cmpFiles, writeFile
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.core.index import NWIndex, countWords, TagsIndex
|
||||
from novelwriter.core.project import NWProject
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, tstPaths):
|
||||
"""Test core functionality of scaning, saving, loading and checking
|
||||
the index cache file.
|
||||
"""
|
||||
projFile = os.path.join(nwLipsum, "meta", "tagsIndex.json")
|
||||
testFile = os.path.join(outDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||
compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||
projFile = Path(nwLipsum) / "meta" / nwFiles.INDEX_FILE
|
||||
testFile = tstPaths.outDir / "coreIndex_LoadSave_tagsIndex.json"
|
||||
compFile = tstPaths.refDir / "coreIndex_LoadSave_tagsIndex.json"
|
||||
|
||||
theProject = NWProject(mockGUI)
|
||||
assert theProject.openProject(nwLipsum)
|
||||
@@ -61,6 +62,11 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
|
||||
assert theIndex.reIndexHandle(None) is False
|
||||
|
||||
# No folder for saving
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None)
|
||||
assert theIndex.saveIndex() is False
|
||||
|
||||
# Make the save fail
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeException)
|
||||
@@ -85,6 +91,11 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
assert theIndex._tagsIndex._tags == {}
|
||||
assert theIndex._itemIndex._items == {}
|
||||
|
||||
# No folder for sloading
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None)
|
||||
assert theIndex.loadIndex() is False
|
||||
|
||||
# Make the load fail
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(json, "load", causeException)
|
||||
@@ -752,7 +763,6 @@ def testCoreIndex_ExtractData(mockGUI, fncDir, mockRnd):
|
||||
|
||||
assert theIndex.saveIndex() is True
|
||||
assert theProject.saveProject() is True
|
||||
assert theProject.closeProject() is True
|
||||
|
||||
# Header Record
|
||||
bHandle = "0000000000000"
|
||||
@@ -764,6 +774,8 @@ def testCoreIndex_ExtractData(mockGUI, fncDir, mockRnd):
|
||||
("T000001", "H1", "Hello World!"), ("T000011", "H1", "Hello World!")
|
||||
]
|
||||
|
||||
assert theProject.closeProject() is True
|
||||
|
||||
# END Test testCoreIndex_ExtractData
|
||||
|
||||
|
||||
|
||||
@@ -509,23 +509,29 @@ def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||
# File
|
||||
theItem = NWItem(theProject)
|
||||
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,
|
||||
"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": None,
|
||||
"import": None,
|
||||
"active": False,
|
||||
},
|
||||
}) is True
|
||||
|
||||
assert theItem.itemName == "A File"
|
||||
@@ -558,7 +564,7 @@ def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||
"layout": "DOCUMENT",
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": "True",
|
||||
"expanded": "yes",
|
||||
"heading": "H1",
|
||||
"charCount": "100",
|
||||
"wordCount": "20",
|
||||
@@ -568,30 +574,36 @@ def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||
"nameAttr": {
|
||||
"status": "s000000",
|
||||
"import": "i000001",
|
||||
"active": "False",
|
||||
"active": "no",
|
||||
}
|
||||
}
|
||||
|
||||
# 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,
|
||||
"name": "A Folder",
|
||||
"itemAttr": {
|
||||
"handle": "0000000000003",
|
||||
"parent": "0000000000002",
|
||||
"root": "0000000000001",
|
||||
"order": 1,
|
||||
"type": "FOLDER",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT",
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": True,
|
||||
"heading": "H1",
|
||||
"charCount": 100,
|
||||
"wordCount": 20,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 50,
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "",
|
||||
"import": "",
|
||||
"active": True,
|
||||
}
|
||||
}) is True
|
||||
|
||||
assert theItem.itemName == "A Folder"
|
||||
@@ -623,7 +635,7 @@ def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||
"class": "NOVEL",
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": "True",
|
||||
"expanded": "yes",
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s000000",
|
||||
@@ -634,23 +646,29 @@ def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||
# 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,
|
||||
"name": "A Novel",
|
||||
"itemAttr": {
|
||||
"handle": "0000000000003",
|
||||
"parent": "0000000000002",
|
||||
"root": "0000000000001",
|
||||
"order": 1,
|
||||
"type": "ROOT",
|
||||
"class": "NOVEL",
|
||||
"layout": "DOCUMENT",
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": True,
|
||||
"heading": "H1",
|
||||
"charCount": 100,
|
||||
"wordCount": 20,
|
||||
"paraCount": 2,
|
||||
"cursorPos": 50,
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": None,
|
||||
"import": None,
|
||||
"active": True,
|
||||
},
|
||||
}) is True
|
||||
|
||||
assert theItem.itemName == "A Novel"
|
||||
@@ -682,7 +700,7 @@ def testCoreItem_PackUnpack(mockGUI, caplog, mockRnd):
|
||||
"class": "NOVEL",
|
||||
},
|
||||
"metaAttr": {
|
||||
"expanded": "True",
|
||||
"expanded": "yes",
|
||||
},
|
||||
"nameAttr": {
|
||||
"status": "s000000",
|
||||
|
||||
@@ -19,28 +19,30 @@ 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 json
|
||||
import pytest
|
||||
|
||||
from mock import causeOSError
|
||||
from tools import writeFile
|
||||
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.core.options import OptionState
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.gui.noveltree import NovelTreeColumn
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreOptions_LoadSave(monkeypatch, mockGUI, tmpDir):
|
||||
def testCoreOptions_LoadSave(monkeypatch, mockGUI, fncPath):
|
||||
"""Test loading and saving from the OptionState class.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
theOpts = OptionState(theProject)
|
||||
|
||||
metaDir = fncPath / "meta"
|
||||
metaDir.mkdir()
|
||||
|
||||
# Write a test file
|
||||
optFile = os.path.join(tmpDir, nwFiles.OPTS_FILE)
|
||||
writeFile(optFile, json.dumps({
|
||||
optFile = metaDir / nwFiles.OPTS_FILE
|
||||
optFile.write_text(json.dumps({
|
||||
"GuiBuildNovel": {
|
||||
"winWidth": 1000,
|
||||
"winHeight": 700,
|
||||
@@ -52,22 +54,22 @@ def testCoreOptions_LoadSave(monkeypatch, mockGUI, tmpDir):
|
||||
"MockGroup": {
|
||||
"mockItem": None,
|
||||
},
|
||||
}))
|
||||
}), encoding="utf-8")
|
||||
|
||||
# Load and save with no path set
|
||||
theProject.projMeta = None
|
||||
assert not theOpts.loadSettings()
|
||||
assert not theOpts.saveSettings()
|
||||
theProject.storage._runtimePath = None
|
||||
assert theOpts.loadSettings() is False
|
||||
assert theOpts.saveSettings() is False
|
||||
|
||||
# Set path
|
||||
theProject.projMeta = tmpDir
|
||||
assert theProject.projMeta == tmpDir
|
||||
theProject.storage._runtimePath = fncPath
|
||||
assert theProject.storage.getMetaFile(nwFiles.OPTS_FILE) == optFile
|
||||
|
||||
# Cause open() to fail
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeOSError)
|
||||
assert not theOpts.loadSettings()
|
||||
assert not theOpts.saveSettings()
|
||||
assert theOpts.loadSettings() is False
|
||||
assert theOpts.saveSettings() is False
|
||||
|
||||
# Load proper
|
||||
assert theOpts.loadSettings()
|
||||
@@ -108,9 +110,11 @@ def testCoreOptions_SetGet(mockGUI):
|
||||
theProject = NWProject(mockGUI)
|
||||
theOpts = OptionState(theProject)
|
||||
|
||||
nwColHidden = NovelTreeColumn.HIDDEN
|
||||
|
||||
# Set invalid values
|
||||
assert not theOpts.setValue("MockGroup", "mockItem", None)
|
||||
assert not theOpts.setValue("GuiBuildNovel", "mockItem", None)
|
||||
assert theOpts.setValue("MockGroup", "mockItem", None) is False
|
||||
assert theOpts.setValue("GuiBuildNovel", "mockItem", None) is False
|
||||
|
||||
# Set valid value
|
||||
assert theOpts.setValue("GuiBuildNovel", "winWidth", 100)
|
||||
@@ -120,6 +124,7 @@ def testCoreOptions_SetGet(mockGUI):
|
||||
assert theOpts.setValue("GuiBuildNovel", "winHeight", 12.34)
|
||||
assert theOpts.setValue("GuiBuildNovel", "addNovel", True)
|
||||
assert theOpts.setValue("GuiBuildNovel", "textFont", "Cantarell")
|
||||
assert theOpts.setValue("GuiNovelView", "lastCol", nwColHidden)
|
||||
|
||||
# Generic get, doesn't check type
|
||||
assert theOpts.getValue("GuiBuildNovel", "winWidth", None) == 100
|
||||
@@ -139,5 +144,14 @@ def testCoreOptions_SetGet(mockGUI):
|
||||
assert theOpts.getFloat("GuiBuildNovel", "mockItem", None) is None
|
||||
assert theOpts.getBool("GuiBuildNovel", "addNovel", None) is True
|
||||
assert theOpts.getBool("GuiBuildNovel", "mockItem", None) is None
|
||||
assert theOpts.getEnum("GuiNovelView", "lastCol", NovelTreeColumn, None) == nwColHidden
|
||||
|
||||
# Get from non-existent groups
|
||||
assert theOpts.getValue("SomeGroup", "mockItem", None) is None
|
||||
assert theOpts.getString("SomeGroup", "mockItem", None) is None
|
||||
assert theOpts.getInt("SomeGroup", "mockItem", None) is None
|
||||
assert theOpts.getFloat("SomeGroup", "mockItem", None) is None
|
||||
assert theOpts.getBool("SomeGroup", "mockItem", None) is None
|
||||
assert theOpts.getEnum("SomeGroup", "mockItem", NovelTreeColumn, None) is None
|
||||
|
||||
# END Test testCoreOptions_SetGet
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,18 +20,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import pytest
|
||||
import shutil
|
||||
|
||||
from shutil import copyfile
|
||||
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
|
||||
from novelwriter.core.projectdata import NWProjectData
|
||||
|
||||
|
||||
class MockProject:
|
||||
@@ -40,13 +39,14 @@ class MockProject:
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProjectXML_ReadCurrent(monkeypatch, filesDir, fncDir, outDir, refDir):
|
||||
def testCoreProjectXML_ReadCurrent(monkeypatch, tstPaths, fncPath):
|
||||
"""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")
|
||||
refFile = tstPaths.filesDir / "nwProject-1.5.nwx"
|
||||
tstFile = tstPaths.outDir / "ProjectXML_ReadCurrent.nwx"
|
||||
xmlFile = fncPath / "nwProject-1.5.nwx"
|
||||
bakFile = fncPath / "nwProject-1.5.bak"
|
||||
outFile = fncPath / "nwProject.nwx"
|
||||
|
||||
xmlReader = ProjectXMLReader(xmlFile)
|
||||
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||
@@ -81,7 +81,7 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, filesDir, fncDir, outDir, refDir
|
||||
|
||||
# Check parsing of unkown sections
|
||||
writeFile(xmlFile, (
|
||||
"<novelWriterXML fileVersion='1.4'>"
|
||||
"<novelWriterXML fileVersion='1.5'>"
|
||||
" <project>"
|
||||
" <stuff></stuff>"
|
||||
" </project>"
|
||||
@@ -125,13 +125,13 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, filesDir, fncDir, outDir, refDir
|
||||
content = []
|
||||
|
||||
# Parse a valid, complete file
|
||||
shutil.copy(refFile, xmlFile)
|
||||
copyfile(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.xmlVersion == 0x0105
|
||||
assert xmlReader.appVersion == "2.0-rc1"
|
||||
assert xmlReader.hexVersion == "0x020000c1"
|
||||
assert xmlReader.hexVersion == 0x020000c1
|
||||
|
||||
# Check loaded data
|
||||
assert data.name == "Sample Project"
|
||||
@@ -199,8 +199,8 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, filesDir, fncDir, outDir, refDir
|
||||
assert data.itemImport.count("i56be10") == 1
|
||||
|
||||
# Compare content
|
||||
dumpFile = os.path.join(outDir, "projectXML_ReadCurrent.json")
|
||||
compFile = os.path.join(refDir, "projectXML_ReadCurrent.json")
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadCurrent.json"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadCurrent.json"
|
||||
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||
json.dump(content, dump, indent=2)
|
||||
assert cmpFiles(dumpFile, compFile)
|
||||
@@ -215,35 +215,36 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, filesDir, fncDir, outDir, refDir
|
||||
|
||||
# Save the project again, which should produce an identical project xml
|
||||
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||
xmlWriter = ProjectXMLWriter(fncDir)
|
||||
xmlWriter = ProjectXMLWriter(fncPath)
|
||||
|
||||
# Fail saving
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("builtins.open", causeOSError)
|
||||
mp.setattr("pathlib.Path.write_bytes", 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)
|
||||
mp.setattr("pathlib.Path.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)
|
||||
copyfile(outFile, tstFile)
|
||||
assert cmpFiles(tstFile, refFile)
|
||||
|
||||
# END Test testCoreProjectXML_ReadCurrent
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProjectXML_ReadLegacy10(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
def testCoreProjectXML_ReadLegacy10(tstPaths, fncPath, 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)
|
||||
refFile = tstPaths.filesDir / "nwProject-1.0.nwx"
|
||||
xmlFile = fncPath / "nwProject-1.0.nwx"
|
||||
outFile = fncPath / "nwProject.nwx"
|
||||
copyfile(refFile, xmlFile)
|
||||
|
||||
xmlReader = ProjectXMLReader(xmlFile)
|
||||
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||
@@ -256,7 +257,7 @@ def testCoreProjectXML_ReadLegacy10(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||
assert xmlReader.xmlVersion == 0x0100
|
||||
assert xmlReader.appVersion == "0.6.1"
|
||||
assert xmlReader.hexVersion == "0x000601f0"
|
||||
assert xmlReader.hexVersion == 0x000601f0
|
||||
|
||||
# Check loaded data
|
||||
assert data.name == "Sample Project"
|
||||
@@ -324,8 +325,8 @@ def testCoreProjectXML_ReadLegacy10(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
assert data.itemImport.count("i00000a") == 0
|
||||
|
||||
# Compare content
|
||||
dumpFile = os.path.join(outDir, "projectXML_ReadLegacy10.json")
|
||||
compFile = os.path.join(refDir, "projectXML_ReadLegacy10.json")
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy10.json"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy10.json"
|
||||
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||
json.dump(content, dump, indent=2)
|
||||
assert cmpFiles(dumpFile, compFile)
|
||||
@@ -367,22 +368,25 @@ def testCoreProjectXML_ReadLegacy10(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
|
||||
# Save the project again, which should produce an identical project xml
|
||||
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||
xmlWriter = ProjectXMLWriter(fncDir)
|
||||
xmlWriter = ProjectXMLWriter(fncPath)
|
||||
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
|
||||
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||
compFile = os.path.join(refDir, "projectXML_ReadLegacy10.nwx")
|
||||
assert cmpFiles(outFile, compFile)
|
||||
testFile = tstPaths.outDir / "projectXML_ReadLegacy10.nwx"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy10.nwx"
|
||||
copyfile(outFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
# END Test testCoreProjectXML_ReadLegacy10
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProjectXML_ReadLegacy11(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
def testCoreProjectXML_ReadLegacy11(tstPaths, fncPath, 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)
|
||||
refFile = tstPaths.filesDir / "nwProject-1.1.nwx"
|
||||
xmlFile = fncPath / "nwProject-1.1.nwx"
|
||||
outFile = fncPath / "nwProject.nwx"
|
||||
copyfile(refFile, xmlFile)
|
||||
|
||||
xmlReader = ProjectXMLReader(xmlFile)
|
||||
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||
@@ -395,7 +399,7 @@ def testCoreProjectXML_ReadLegacy11(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||
assert xmlReader.xmlVersion == 0x0101
|
||||
assert xmlReader.appVersion == "0.9.2"
|
||||
assert xmlReader.hexVersion == "0x000902f0"
|
||||
assert xmlReader.hexVersion == 0x000902f0
|
||||
|
||||
# Check loaded data
|
||||
assert data.name == "Sample Project"
|
||||
@@ -463,8 +467,8 @@ def testCoreProjectXML_ReadLegacy11(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
assert data.itemImport.count("i00000a") == 0
|
||||
|
||||
# Compare content
|
||||
dumpFile = os.path.join(outDir, "projectXML_ReadLegacy11.json")
|
||||
compFile = os.path.join(refDir, "projectXML_ReadLegacy11.json")
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy11.json"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy11.json"
|
||||
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||
json.dump(content, dump, indent=2)
|
||||
assert cmpFiles(dumpFile, compFile)
|
||||
@@ -506,22 +510,25 @@ def testCoreProjectXML_ReadLegacy11(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
|
||||
# Save the project again, which should produce an identical project xml
|
||||
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||
xmlWriter = ProjectXMLWriter(fncDir)
|
||||
xmlWriter = ProjectXMLWriter(fncPath)
|
||||
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
|
||||
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||
compFile = os.path.join(refDir, "projectXML_ReadLegacy11.nwx")
|
||||
assert cmpFiles(outFile, compFile)
|
||||
testFile = tstPaths.outDir / "projectXML_ReadLegacy11.nwx"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy11.nwx"
|
||||
copyfile(outFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
# END Test testCoreProjectXML_ReadLegacy11
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProjectXML_ReadLegacy12(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
def testCoreProjectXML_ReadLegacy12(tstPaths, fncPath, 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)
|
||||
refFile = tstPaths.filesDir / "nwProject-1.2.nwx"
|
||||
xmlFile = fncPath / "nwProject-1.2.nwx"
|
||||
outFile = fncPath / "nwProject.nwx"
|
||||
copyfile(refFile, xmlFile)
|
||||
|
||||
xmlReader = ProjectXMLReader(xmlFile)
|
||||
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||
@@ -534,7 +541,7 @@ def testCoreProjectXML_ReadLegacy12(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||
assert xmlReader.xmlVersion == 0x0102
|
||||
assert xmlReader.appVersion == "1.4.2"
|
||||
assert xmlReader.hexVersion == "0x010402f0"
|
||||
assert xmlReader.hexVersion == 0x010402f0
|
||||
|
||||
# Check loaded data
|
||||
assert data.name == "Sample Project"
|
||||
@@ -602,8 +609,8 @@ def testCoreProjectXML_ReadLegacy12(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
assert data.itemImport.count("i00000a") == 0
|
||||
|
||||
# Compare content
|
||||
dumpFile = os.path.join(outDir, "projectXML_ReadLegacy12.json")
|
||||
compFile = os.path.join(refDir, "projectXML_ReadLegacy12.json")
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy12.json"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy12.json"
|
||||
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||
json.dump(content, dump, indent=2)
|
||||
assert cmpFiles(dumpFile, compFile)
|
||||
@@ -648,22 +655,25 @@ def testCoreProjectXML_ReadLegacy12(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
|
||||
# Save the project again, which should produce an identical project xml
|
||||
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||
xmlWriter = ProjectXMLWriter(fncDir)
|
||||
xmlWriter = ProjectXMLWriter(fncPath)
|
||||
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
|
||||
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||
compFile = os.path.join(refDir, "projectXML_ReadLegacy12.nwx")
|
||||
assert cmpFiles(outFile, compFile)
|
||||
testFile = tstPaths.outDir / "projectXML_ReadLegacy12.nwx"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy12.nwx"
|
||||
copyfile(outFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
# END Test testCoreProjectXML_ReadLegacy12
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProjectXML_ReadLegacy13(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
def testCoreProjectXML_ReadLegacy13(tstPaths, fncPath, 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)
|
||||
refFile = tstPaths.filesDir / "nwProject-1.3.nwx"
|
||||
xmlFile = fncPath / "nwProject-1.3.nwx"
|
||||
outFile = fncPath / "nwProject.nwx"
|
||||
copyfile(refFile, xmlFile)
|
||||
|
||||
xmlReader = ProjectXMLReader(xmlFile)
|
||||
assert xmlReader.state == XMLReadState.NO_ACTION
|
||||
@@ -676,7 +686,7 @@ def testCoreProjectXML_ReadLegacy13(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||
assert xmlReader.xmlVersion == 0x0103
|
||||
assert xmlReader.appVersion == "1.6.6"
|
||||
assert xmlReader.hexVersion == "0x010606f0"
|
||||
assert xmlReader.hexVersion == 0x010606f0
|
||||
|
||||
# Check loaded data
|
||||
assert data.name == "Sample Project"
|
||||
@@ -744,8 +754,8 @@ def testCoreProjectXML_ReadLegacy13(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
assert data.itemImport.count("i00000a") == 0
|
||||
|
||||
# Compare content
|
||||
dumpFile = os.path.join(outDir, "projectXML_ReadLegacy13.json")
|
||||
compFile = os.path.join(refDir, "projectXML_ReadLegacy13.json")
|
||||
dumpFile = tstPaths.outDir / "projectXML_ReadLegacy13.json"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy13.json"
|
||||
with open(dumpFile, mode="w", encoding="utf-8") as dump:
|
||||
json.dump(content, dump, indent=2)
|
||||
assert cmpFiles(dumpFile, compFile)
|
||||
@@ -790,9 +800,159 @@ def testCoreProjectXML_ReadLegacy13(filesDir, fncDir, outDir, refDir, mockRnd):
|
||||
|
||||
# Save the project again, which should produce an identical project xml
|
||||
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||
xmlWriter = ProjectXMLWriter(fncDir)
|
||||
xmlWriter = ProjectXMLWriter(fncPath)
|
||||
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
|
||||
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||
compFile = os.path.join(refDir, "projectXML_ReadLegacy13.nwx")
|
||||
assert cmpFiles(outFile, compFile)
|
||||
testFile = tstPaths.outDir / "projectXML_ReadLegacy13.nwx"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy13.nwx"
|
||||
copyfile(outFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
# END Test testCoreProjectXML_ReadLegacy13
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProjectXML_ReadLegacy14(tstPaths, fncPath, mockRnd):
|
||||
"""Test reading the version 1.4 XML file format.
|
||||
"""
|
||||
refFile = tstPaths.filesDir / "nwProject-1.4.nwx"
|
||||
xmlFile = fncPath / "nwProject-1.4.nwx"
|
||||
outFile = fncPath / "nwProject.nwx"
|
||||
copyfile(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 == 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") 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("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 = tstPaths.outDir / "projectXML_ReadLegacy14.json"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy14.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": "1st Draft",
|
||||
"6a2d6d5f4f401": "Notes",
|
||||
"636b6aa9b697b": "1st Draft",
|
||||
"bc0cbd2a407f3": "1st Draft",
|
||||
"ba8a28a246524": "Finished",
|
||||
"96b68994dfa3d": "Notes",
|
||||
"88706ddc78b1b": "1st Draft",
|
||||
"ae7339df26ded": "1st Draft",
|
||||
"e5e47ebf63b1c": "New",
|
||||
"bacb7059e3083": "Started",
|
||||
"a520879ca0b45": "1st Draft",
|
||||
"f6622b4617424": "None",
|
||||
"f7e2d9f330615": "None",
|
||||
"14298de4d9524": "Minor",
|
||||
"bb2c23b3c42cc": "Major",
|
||||
"15c4492bd5107": "None",
|
||||
"b3e74dbc1f584": "Main",
|
||||
"f1471bef9f2ae": "Minor",
|
||||
"5eaea4e8cdee8": "Major",
|
||||
"6827118336ac1": "New",
|
||||
"ae9bf3c3ea159": "New",
|
||||
"8a5deb88c0e97": "1st Draft",
|
||||
"98acd8c76c93a": "None",
|
||||
"b8136a5a774a0": "None",
|
||||
}
|
||||
|
||||
# Save the project again, which should produce an identical project xml
|
||||
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
|
||||
xmlWriter = ProjectXMLWriter(fncPath)
|
||||
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
|
||||
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
|
||||
testFile = tstPaths.outDir / "projectXML_ReadLegacy14.nwx"
|
||||
compFile = tstPaths.refDir / "projectXML_ReadLegacy14.nwx"
|
||||
copyfile(outFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
# END Test testCoreProjectXML_ReadLegacy14
|
||||
|
||||
@@ -19,7 +19,6 @@ 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 sys
|
||||
import pytest
|
||||
|
||||
@@ -63,10 +62,10 @@ def testCoreSpell_FakeEnchant(monkeypatch):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreSpell_Enchant(monkeypatch, fncDir):
|
||||
def testCoreSpell_Enchant(monkeypatch, fncPath):
|
||||
"""Test the pyenchant spell checker.
|
||||
"""
|
||||
wList = os.path.join(fncDir, "wordlist.txt")
|
||||
wList = fncPath / "wordlist.txt"
|
||||
writeFile(wList, "a_word\nb_word\nc_word\n")
|
||||
|
||||
# Break the enchant package, and check error handling
|
||||
@@ -134,13 +133,13 @@ def testCoreSpell_Enchant(monkeypatch, fncDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreSpell_SessionWords(fncDir):
|
||||
def testCoreSpell_SessionWords(fncPath):
|
||||
"""Test the handling of the custom word list in the spell checker.
|
||||
New project sessions should not inherit the project word list from
|
||||
other sessions, so this test checks that they don't bleed through.
|
||||
"""
|
||||
wList1 = os.path.join(fncDir, "wordlist1.txt")
|
||||
wList2 = os.path.join(fncDir, "wordlist2.txt")
|
||||
wList1 = fncPath / "wordlist1.txt"
|
||||
wList2 = fncPath / "wordlist2.txt"
|
||||
writeFile(wList1, "a_word\nb_word\nc_word\n")
|
||||
writeFile(wList2, "d_word\ne_word\nf_word\n")
|
||||
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
"""
|
||||
novelWriter – NWStorage 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/>.
|
||||
"""
|
||||
|
||||
from zipfile import ZipFile
|
||||
import pytest
|
||||
|
||||
from mock import causeOSError
|
||||
from tools import C, buildTestProject, writeFile
|
||||
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.storage import NWStorage
|
||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
|
||||
|
||||
|
||||
class MockProject:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStorage_OpenProjectInPlace(mockGUI, fncPath, mockRnd):
|
||||
"""Test opening a project in a folder.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncPath)
|
||||
theProject.closeProject()
|
||||
|
||||
# Create instance
|
||||
storage = NWStorage(theProject)
|
||||
|
||||
# Check defaults
|
||||
assert storage.storagePath is None
|
||||
assert storage.runtimePath is None
|
||||
assert storage.contentPath is None
|
||||
assert storage._openMode == NWStorage.MODE_INACTIVE
|
||||
|
||||
# Check closed project return values
|
||||
assert storage.isOpen() is False
|
||||
assert storage.getXmlReader() is None
|
||||
assert storage.getXmlWriter() is None
|
||||
assert bool(storage.getDocument(C.hSceneDoc)) is False
|
||||
assert storage.getMetaFile("file") is None
|
||||
assert storage.getCacheFile("file") is None
|
||||
|
||||
# Open project as a new project should fail
|
||||
assert storage.openProjectInPlace(fncPath, newProject=True) is False
|
||||
|
||||
# Opening as a no-new project is fine
|
||||
assert storage.openProjectInPlace(fncPath, newProject=False) is True
|
||||
|
||||
# Opening the project file is also fine
|
||||
assert storage.openProjectInPlace(fncPath / nwFiles.PROJ_FILE, newProject=False) is True
|
||||
|
||||
# Check settings
|
||||
assert storage.storagePath == fncPath
|
||||
assert storage.runtimePath == fncPath
|
||||
assert storage.contentPath == fncPath / "content"
|
||||
assert storage._openMode == NWStorage.MODE_INPLACE
|
||||
|
||||
# Open the project itself
|
||||
theProject.openProject(fncPath)
|
||||
storage = theProject.storage
|
||||
|
||||
# Get XML components
|
||||
assert isinstance(storage.getXmlReader(), ProjectXMLReader)
|
||||
assert isinstance(storage.getXmlWriter(), ProjectXMLWriter)
|
||||
|
||||
# Get document
|
||||
assert storage.getDocument(C.hSceneDoc).readDocument() == "### New Scene\n\n"
|
||||
|
||||
# Get paths
|
||||
assert storage.getMetaFile("stuff") == fncPath / "meta" / "stuff"
|
||||
assert storage.getCacheFile("stuff") == fncPath / "cache" / "stuff"
|
||||
|
||||
# Clean up
|
||||
assert theProject.closeProject() is True
|
||||
|
||||
# Check closed project return values (again)
|
||||
assert storage.isOpen() is False
|
||||
assert storage.getXmlReader() is None
|
||||
assert storage.getXmlWriter() is None
|
||||
assert bool(storage.getDocument(C.hSceneDoc)) is False
|
||||
assert storage.getMetaFile("file") is None
|
||||
assert storage.getCacheFile("file") is None
|
||||
|
||||
# END Test testCoreStorage_ProjectInPlace
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStorage_LockFile(monkeypatch, fncPath):
|
||||
"""Test the project lock file.
|
||||
"""
|
||||
monkeypatch.setattr("novelwriter.core.storage.time", lambda: 1000.0)
|
||||
|
||||
storage = NWStorage(MockProject())
|
||||
assert storage.isOpen() is False
|
||||
|
||||
# Project not open, so cannot read/write lock file
|
||||
assert storage.readLockFile() == ["ERROR"]
|
||||
assert storage.writeLockFile() is False
|
||||
assert storage.clearLockFile() is False
|
||||
|
||||
# Set a path to work with
|
||||
lockFilePath = fncPath / nwFiles.PROJ_LOCK
|
||||
storage._lockFilePath = lockFilePath
|
||||
|
||||
# Path is set, but there is no lockfile
|
||||
assert storage.readLockFile() == []
|
||||
|
||||
# Write lockfile fails
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("pathlib.Path.write_text", causeOSError)
|
||||
assert storage.writeLockFile() is False
|
||||
assert not lockFilePath.exists()
|
||||
|
||||
# Successful write
|
||||
assert storage.writeLockFile() is True
|
||||
assert lockFilePath.exists()
|
||||
assert lockFilePath.read_text().split(";")[3] == "1000"
|
||||
|
||||
# Read lockfile fails
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("pathlib.Path.read_text", causeOSError)
|
||||
assert storage.readLockFile() == ["ERROR"]
|
||||
assert lockFilePath.exists()
|
||||
|
||||
# Successful read
|
||||
assert storage.readLockFile() == [
|
||||
storage.mainConf.hostName,
|
||||
storage.mainConf.osType,
|
||||
storage.mainConf.kernelVer,
|
||||
"1000",
|
||||
]
|
||||
|
||||
# Write an invalid lockfile
|
||||
writeFile(lockFilePath, "a;b;c")
|
||||
assert storage.readLockFile() == ["ERROR"]
|
||||
|
||||
# Fail to remove lockfile
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("pathlib.Path.unlink", causeOSError)
|
||||
assert storage.clearLockFile() is False
|
||||
assert lockFilePath.exists()
|
||||
|
||||
# Successful remove
|
||||
assert storage.clearLockFile() is True
|
||||
assert not lockFilePath.exists()
|
||||
|
||||
# END Test testCoreStorage_LockFile
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStorage_PrepareStorage(monkeypatch, fncPath):
|
||||
"""Test the project path preparation functions.
|
||||
"""
|
||||
storage = NWStorage(MockProject())
|
||||
assert storage.isOpen() is False
|
||||
|
||||
# No path set
|
||||
assert storage._prepareStorage() is False
|
||||
|
||||
# Set path to home
|
||||
storage._runtimePath = fncPath
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("pathlib.Path.home", lambda: fncPath)
|
||||
assert storage._prepareStorage() is False
|
||||
|
||||
# Fail on mkdir
|
||||
storage._runtimePath = fncPath
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("pathlib.Path.mkdir", causeOSError)
|
||||
assert storage._prepareStorage() is False
|
||||
|
||||
# Set up the folder
|
||||
storage._runtimePath = fncPath
|
||||
assert storage._prepareStorage(checkLegacy=False) is True
|
||||
assert (fncPath / "content").exists()
|
||||
assert (fncPath / "cache").exists()
|
||||
assert (fncPath / "meta").exists()
|
||||
|
||||
# Add a legacy folder
|
||||
storage._runtimePath = fncPath
|
||||
dataDir = fncPath / "data_0"
|
||||
dataDir.mkdir()
|
||||
assert storage._prepareStorage(checkLegacy=True) is True
|
||||
assert not dataDir.exists()
|
||||
|
||||
# We cannot add a new project here
|
||||
storage._runtimePath = fncPath
|
||||
assert storage._prepareStorage(checkLegacy=False, newProject=True) is False
|
||||
|
||||
# Legacy Data Folder
|
||||
# ==================
|
||||
storage._runtimePath = fncPath
|
||||
|
||||
data = []
|
||||
files = []
|
||||
for c in "0123456789abcdefX":
|
||||
dataDir = fncPath / f"data_{c}"
|
||||
dataDir.mkdir()
|
||||
data.append(dataDir)
|
||||
|
||||
nwdFile = dataDir / f"00000000000{c}_main.nwd"
|
||||
bakFile = dataDir / f"00000000000{c}_main.bak"
|
||||
nwdFile.write_text("#")
|
||||
bakFile.write_text("#")
|
||||
files.append(nwdFile)
|
||||
files.append(bakFile)
|
||||
|
||||
for item in files:
|
||||
assert item.exists()
|
||||
|
||||
# Pollute folder 7 and 8
|
||||
(data[7] / "stuff.txt").write_text("foo")
|
||||
(data[8] / "bar").mkdir()
|
||||
|
||||
# Process folders
|
||||
for i in range(9):
|
||||
storage._legacyDataFolder(fncPath, data[i])
|
||||
|
||||
# Files form 0 to 8 should now be in content
|
||||
for c in "012345678":
|
||||
assert (fncPath / "content" / f"{c}00000000000{c}.nwd").exists()
|
||||
|
||||
# Folders 0 to 6 should be deleted
|
||||
for i in range(7):
|
||||
assert not data[i].exists()
|
||||
|
||||
# While 7 and 8 remain
|
||||
assert data[7].exists()
|
||||
assert data[8].exists()
|
||||
|
||||
# So does folder X, which is invalid
|
||||
storage._legacyDataFolder(fncPath, data[16])
|
||||
assert data[16].exists()
|
||||
|
||||
# Fail cleanup of folder 9
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("pathlib.Path.rename", causeOSError)
|
||||
mp.setattr("pathlib.Path.unlink", causeOSError)
|
||||
storage._legacyDataFolder(fncPath, data[9])
|
||||
assert data[9].exists()
|
||||
assert not (fncPath / "content" / "9000000000009.nwd").exists()
|
||||
|
||||
# Run the remaining through the prepare storage call
|
||||
assert storage._prepareStorage(checkLegacy=True) is True
|
||||
for c in "0123456789abcdef":
|
||||
assert (fncPath / "content" / f"{c}00000000000{c}.nwd").exists()
|
||||
|
||||
# Deprecated Files
|
||||
# ================
|
||||
|
||||
remove = [
|
||||
fncPath / "meta" / "mainOptions.json",
|
||||
fncPath / "meta" / "exportOptions.json",
|
||||
fncPath / "meta" / "outlineOptions.json",
|
||||
fncPath / "meta" / "timelineOptions.json",
|
||||
fncPath / "meta" / "docMergeOptions.json",
|
||||
fncPath / "meta" / "sessionLogOptions.json",
|
||||
fncPath / "ToC.json",
|
||||
]
|
||||
for depFile in remove:
|
||||
depFile.write_text("foo")
|
||||
assert depFile.exists()
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("pathlib.Path.unlink", causeOSError)
|
||||
storage._deleteDeprecatedFiles(fncPath)
|
||||
for depFile in remove:
|
||||
assert depFile.exists()
|
||||
|
||||
storage._deleteDeprecatedFiles(fncPath)
|
||||
for depFile in remove:
|
||||
assert not depFile.exists()
|
||||
|
||||
# END Test testCoreStorage_PrepareStorage
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStorage_ZipIt(monkeypatch, mockGUI, fncPath, tmpPath, mockRnd):
|
||||
"""Test making a zip archive of a project.
|
||||
"""
|
||||
zipFile = tmpPath / "project.zip"
|
||||
|
||||
theProject = NWProject(mockGUI)
|
||||
storage = theProject.storage
|
||||
assert storage.zipIt(zipFile) is False
|
||||
|
||||
# Make a project
|
||||
mockRnd.reset()
|
||||
buildTestProject(theProject, fncPath)
|
||||
|
||||
# Fail to create archive
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.core.storage.ZipFile.write", causeOSError)
|
||||
assert storage.zipIt(zipFile) is False
|
||||
|
||||
# Create archive
|
||||
assert storage.zipIt(zipFile) is True
|
||||
|
||||
# Check content
|
||||
with ZipFile(zipFile, mode="r") as archive:
|
||||
names = archive.namelist()
|
||||
assert nwFiles.PROJ_FILE in names
|
||||
assert f"meta/{nwFiles.OPTS_FILE}" in names
|
||||
assert f"meta/{nwFiles.INDEX_FILE}" in names
|
||||
assert f"content/{C.hTitlePage}.nwd" in names
|
||||
assert f"content/{C.hChapterDoc}.nwd" in names
|
||||
assert f"content/{C.hSceneDoc}.nwd" in names
|
||||
|
||||
theProject.closeProject()
|
||||
|
||||
# END Test testCoreStorage_ZipIt
|
||||
@@ -25,7 +25,6 @@ import pytest
|
||||
from tools import C, buildTestProject, readFile
|
||||
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.document import NWDoc
|
||||
from novelwriter.core.tokenizer import Tokenizer
|
||||
|
||||
|
||||
@@ -156,7 +155,7 @@ def testCoreToken_TextOps(monkeypatch, mockGUI, mockRnd, fncDir):
|
||||
)
|
||||
docTextR = docText.replace("<A>", "this").replace("<B>", "that")
|
||||
|
||||
nDoc = NWDoc(theProject, C.hSceneDoc)
|
||||
nDoc = theProject.storage.getDocument(C.hSceneDoc)
|
||||
assert nDoc.writeDocument(docText)
|
||||
|
||||
theProject.data.setAutoReplace({"A": "this", "B": "that"})
|
||||
|
||||
@@ -19,10 +19,11 @@ 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 pytest
|
||||
import random
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from tools import readFile
|
||||
|
||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
@@ -394,7 +395,7 @@ def testCoreTree_Reorder(mockGUI, mockItems):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpDir):
|
||||
def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpPath):
|
||||
"""Test writing the ToC.txt file.
|
||||
"""
|
||||
theProject = NWProject(mockGUI)
|
||||
@@ -411,24 +412,23 @@ def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpDir):
|
||||
"""Return True for items that are files in novelWriter and
|
||||
should thus also be files in the project folder structure.
|
||||
"""
|
||||
dItem = theTree[fileName[8:21]]
|
||||
dItem = theTree[fileName.name[:13]]
|
||||
assert dItem is not None
|
||||
return dItem.itemType == nwItemType.FILE
|
||||
|
||||
monkeypatch.setattr("os.path.isfile", mockIsFile)
|
||||
monkeypatch.setattr("pathlib.Path.is_file", mockIsFile)
|
||||
|
||||
theProject.projContent = "content"
|
||||
theProject.projPath = None
|
||||
assert not theTree.writeToCFile()
|
||||
theProject._storage._runtimePath = None
|
||||
assert theTree.writeToCFile() is False
|
||||
|
||||
theProject.projPath = tmpDir
|
||||
assert theTree.writeToCFile()
|
||||
theProject._storage._runtimePath = tmpPath
|
||||
assert theTree.writeToCFile() is True
|
||||
|
||||
pathA = os.path.join("content", "c000000000001.nwd")
|
||||
pathB = os.path.join("content", "c000000000002.nwd")
|
||||
pathC = os.path.join("content", "b000000000002.nwd")
|
||||
pathA = str(Path("content") / "c000000000001.nwd")
|
||||
pathB = str(Path("content") / "c000000000002.nwd")
|
||||
pathC = str(Path("content") / "b000000000002.nwd")
|
||||
|
||||
assert readFile(os.path.join(tmpDir, nwFiles.TOC_TXT)) == (
|
||||
assert readFile(tmpPath / nwFiles.TOC_TXT) == (
|
||||
"\n"
|
||||
"Table of Contents\n"
|
||||
"=================\n"
|
||||
|
||||
@@ -170,8 +170,6 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
assert len(nwGUI.theProject.tree._treeOrder) == 0
|
||||
assert len(nwGUI.theProject.tree._treeRoots) == 0
|
||||
assert nwGUI.theProject.tree.trashRoot() is None
|
||||
assert nwGUI.theProject.projPath is None
|
||||
assert nwGUI.theProject.projMeta is None
|
||||
assert nwGUI.theProject.data.name == ""
|
||||
assert nwGUI.theProject.data.title == ""
|
||||
assert nwGUI.theProject.data.authors == []
|
||||
@@ -192,8 +190,6 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
|
||||
assert len(nwGUI.theProject.tree._treeOrder) == 8
|
||||
assert len(nwGUI.theProject.tree._treeRoots) == 4
|
||||
assert nwGUI.theProject.tree.trashRoot() is None
|
||||
assert nwGUI.theProject.projPath == fncProj
|
||||
assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta")
|
||||
assert nwGUI.theProject.data.name == "New Project"
|
||||
assert nwGUI.theProject.data.title == "New Novel"
|
||||
assert nwGUI.theProject.data.authors == ["Jane Doe"]
|
||||
|
||||
@@ -19,10 +19,11 @@ 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 pytest
|
||||
|
||||
from tools import C, buildTestProject, writeFile
|
||||
from pathlib import Path
|
||||
|
||||
from tools import C, buildTestProject
|
||||
|
||||
from PyQt5.QtGui import QFocusEvent
|
||||
from PyQt5.QtCore import Qt, QEvent
|
||||
@@ -46,18 +47,18 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
|
||||
nwGUI.projView.projTree._getTreeItem(C.hCharRoot).setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
|
||||
|
||||
writeFile(
|
||||
os.path.join(nwGUI.theProject.projContent, "0000000000010.nwd"),
|
||||
"# Jane Doe\n\n@tag: Jane\n\n"
|
||||
)
|
||||
writeFile(
|
||||
os.path.join(nwGUI.theProject.projContent, "000000000000f.nwd"), (
|
||||
"### Scene One\n\n"
|
||||
"@pov: Jane\n"
|
||||
"@focus: Jane\n\n"
|
||||
"% Synopsis: This is a scene."
|
||||
)
|
||||
contentPath = nwGUI.theProject.storage.contentPath
|
||||
assert isinstance(contentPath, Path)
|
||||
|
||||
(contentPath / "0000000000010.nwd").write_text(
|
||||
"# Jane Doe\n\n@tag: Jane\n\n", encoding="utf-8"
|
||||
)
|
||||
(contentPath / "000000000000f.nwd").write_text((
|
||||
"### Scene One\n\n"
|
||||
"@pov: Jane\n"
|
||||
"@focus: Jane\n\n"
|
||||
"% Synopsis: This is a scene."
|
||||
), encoding="utf-8")
|
||||
|
||||
novelView = nwGUI.novelView
|
||||
novelTree = novelView.novelTree
|
||||
|
||||
@@ -29,7 +29,6 @@ from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QMessageBox, QMenu, QTreeWidgetItem, QDialog
|
||||
|
||||
from novelwriter.enum import nwItemLayout, nwItemType, nwItemClass
|
||||
from novelwriter.core import NWDoc
|
||||
from novelwriter.gui.projtree import GuiProjectTree
|
||||
from novelwriter.dialogs.docmerge import GuiDocMerge
|
||||
from novelwriter.dialogs.docsplit import GuiDocSplit
|
||||
@@ -714,7 +713,7 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, i
|
||||
|
||||
# The merge goes through
|
||||
assert projTree._mergeDocuments(hChapter1, True) is True
|
||||
assert len(NWDoc(theProject, mergedDoc1).readDocument()) > lenAll
|
||||
assert len(theProject.storage.getDocument(mergedDoc1).readDocument()) > lenAll
|
||||
|
||||
# Merge to Existing Doc
|
||||
# =====================
|
||||
@@ -733,9 +732,9 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, fncDir, mockRnd, i
|
||||
|
||||
# Successful merge, and move to trash
|
||||
mergeData["moveToTrash"] = True
|
||||
assert len(NWDoc(theProject, hChapter1).readDocument()) < lenAll
|
||||
assert len(theProject.storage.getDocument(hChapter1).readDocument()) < lenAll
|
||||
assert projTree._mergeDocuments(hChapter1, False) is True
|
||||
assert len(NWDoc(theProject, hChapter1).readDocument()) > lenAll
|
||||
assert len(theProject.storage.getDocument(hChapter1).readDocument()) > lenAll
|
||||
|
||||
assert theProject.tree.isTrash(hSceneOne11)
|
||||
assert theProject.tree.isTrash(hSceneOne12)
|
||||
|
||||
@@ -25,7 +25,6 @@ import pytest
|
||||
from tools import C, buildTestProject
|
||||
|
||||
from novelwriter.enum import nwState
|
||||
from novelwriter.core.document import NWDoc
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
@@ -34,7 +33,7 @@ def testGuiStatusBar_Main(qtbot, nwGUI, fncProj, mockRnd):
|
||||
"""
|
||||
buildTestProject(nwGUI, fncProj)
|
||||
cHandle = nwGUI.theProject.newFile("A Note", C.hCharRoot)
|
||||
newDoc = NWDoc(nwGUI.theProject, cHandle)
|
||||
newDoc = nwGUI.theProject.storage.getDocument(cHandle)
|
||||
newDoc.writeDocument("# A Note\n\n")
|
||||
nwGUI.projView.projTree.revealNewTreeItem(cHandle)
|
||||
nwGUI.rebuildIndex(beQuiet=True)
|
||||
|
||||
+8
-15
@@ -25,7 +25,7 @@ import shutil
|
||||
|
||||
from PyQt5.QtWidgets import qApp
|
||||
|
||||
XML_IGNORE = ("<novelWriterXML", "<saveCount", "<autoCount", "<editTime")
|
||||
XML_IGNORE = ("<novelWriterXML", "<project")
|
||||
|
||||
|
||||
class C:
|
||||
@@ -156,7 +156,7 @@ def buildTestProject(theObject, projPath):
|
||||
object as the parent.
|
||||
"""
|
||||
from novelwriter.enum import nwItemClass
|
||||
from novelwriter.core import NWProject, NWDoc
|
||||
from novelwriter.core import NWProject
|
||||
|
||||
if isinstance(theObject, NWProject):
|
||||
theGUI = None
|
||||
@@ -166,17 +166,10 @@ def buildTestProject(theObject, projPath):
|
||||
theProject = theObject.theProject
|
||||
|
||||
theProject.clearProject()
|
||||
theProject.setProjectPath(projPath, newProject=True)
|
||||
|
||||
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.storage.openProjectInPlace(projPath)
|
||||
theProject.setDefaultStatusImport()
|
||||
|
||||
theProject.data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
|
||||
theProject.data.setName("New Project")
|
||||
theProject.data.setTitle("New Novel")
|
||||
theProject.data.setAuthors("Jane Doe")
|
||||
@@ -193,15 +186,15 @@ def buildTestProject(theObject, projPath):
|
||||
xHandle[7] = theProject.newFile("New Chapter", xHandle[6])
|
||||
xHandle[8] = theProject.newFile("New Scene", xHandle[6])
|
||||
|
||||
aDoc = NWDoc(theProject, xHandle[5])
|
||||
aDoc = theProject.storage.getDocument(xHandle[5])
|
||||
aDoc.writeDocument("#! New Novel\n\n>> By Jane Doe <<\n")
|
||||
theProject.index.reIndexHandle(xHandle[5])
|
||||
|
||||
aDoc = NWDoc(theProject, xHandle[7])
|
||||
aDoc = theProject.storage.getDocument(xHandle[7])
|
||||
aDoc.writeDocument("## %s\n\n" % theProject.tr("New Chapter"))
|
||||
theProject.index.reIndexHandle(xHandle[7])
|
||||
|
||||
aDoc = NWDoc(theProject, xHandle[8])
|
||||
aDoc = theProject.storage.getDocument(xHandle[8])
|
||||
aDoc.writeDocument("### %s\n\n" % theProject.tr("New Scene"))
|
||||
theProject.index.reIndexHandle(xHandle[8])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user