Add a setting for using character count as main count (#2323)
This commit is contained in:
+20
-4
@@ -919,9 +919,10 @@ class RecentProjects:
|
|||||||
puuid = str(entry.get("uuid", ""))
|
puuid = str(entry.get("uuid", ""))
|
||||||
title = str(entry.get("title", ""))
|
title = str(entry.get("title", ""))
|
||||||
words = checkInt(entry.get("words", 0), 0)
|
words = checkInt(entry.get("words", 0), 0)
|
||||||
|
chars = checkInt(entry.get("chars", 0), 0)
|
||||||
saved = checkInt(entry.get("time", 0), 0)
|
saved = checkInt(entry.get("time", 0), 0)
|
||||||
if path and title:
|
if path and title:
|
||||||
self._setEntry(puuid, path, title, words, saved)
|
self._setEntry(puuid, path, title, words, chars, saved)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not load recent project cache")
|
logger.error("Could not load recent project cache")
|
||||||
logException()
|
logException()
|
||||||
@@ -954,7 +955,14 @@ class RecentProjects:
|
|||||||
try:
|
try:
|
||||||
if (remove := self._map.get(data.uuid)) and (remove != str(path)):
|
if (remove := self._map.get(data.uuid)) and (remove != str(path)):
|
||||||
self.remove(remove)
|
self.remove(remove)
|
||||||
self._setEntry(data.uuid, str(path), data.name, sum(data.currCounts), int(saved))
|
self._setEntry(
|
||||||
|
data.uuid,
|
||||||
|
str(path),
|
||||||
|
data.name,
|
||||||
|
sum(data.currCounts[:2]),
|
||||||
|
sum(data.currCounts[2:]),
|
||||||
|
int(saved),
|
||||||
|
)
|
||||||
self.saveCache()
|
self.saveCache()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -967,9 +975,17 @@ class RecentProjects:
|
|||||||
self.saveCache()
|
self.saveCache()
|
||||||
return
|
return
|
||||||
|
|
||||||
def _setEntry(self, puuid: str, path: str, title: str, words: int, saved: int) -> None:
|
def _setEntry(
|
||||||
|
self, puuid: str, path: str, title: str, words: int, chars: int, saved: int
|
||||||
|
) -> None:
|
||||||
"""Set an entry in the recent projects record."""
|
"""Set an entry in the recent projects record."""
|
||||||
self._data[path] = {"uuid": puuid, "title": title, "words": words, "time": saved}
|
self._data[path] = {
|
||||||
|
"uuid": puuid,
|
||||||
|
"title": title,
|
||||||
|
"words": words,
|
||||||
|
"chars": chars,
|
||||||
|
"time": saved,
|
||||||
|
}
|
||||||
if puuid:
|
if puuid:
|
||||||
self._map[puuid] = path
|
self._map[puuid] = path
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -352,6 +352,10 @@ class nwLabels:
|
|||||||
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
|
nwStats.WORDS_TEXT: QT_TRANSLATE_NOOP("Stats", "Words in Text"),
|
||||||
nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"),
|
nwStats.WORDS_TITLE: QT_TRANSLATE_NOOP("Stats", "Words in Headings"),
|
||||||
}
|
}
|
||||||
|
STATS_DISPLAY: Final[dict[str, str]] = {
|
||||||
|
nwStats.CHARS: QT_TRANSLATE_NOOP("Stats", "Characters: {0} ({1})"),
|
||||||
|
nwStats.WORDS: QT_TRANSLATE_NOOP("Stats", "Words: {0} ({1})"),
|
||||||
|
}
|
||||||
BUILD_FMT: Final[dict[nwBuildFmt, str]] = {
|
BUILD_FMT: Final[dict[nwBuildFmt, str]] = {
|
||||||
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
|
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
|
||||||
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
|
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
|
||||||
|
|||||||
+30
-23
@@ -53,10 +53,10 @@ class NWItem:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_active", "_charCount", "_class", "_cursorPos", "_expanded",
|
"_active", "_charCount", "_charInit", "_class", "_cursorPos",
|
||||||
"_handle", "_heading", "_import", "_initCount", "_layout", "_name",
|
"_expanded", "_handle", "_heading", "_import", "_layout", "_name",
|
||||||
"_order", "_paraCount", "_parent", "_project", "_root", "_status",
|
"_order", "_paraCount", "_parent", "_project", "_root", "_status",
|
||||||
"_type", "_wordCount",
|
"_type", "_wordCount", "_wordInit",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, project: NWProject, handle: str) -> None:
|
def __init__(self, project: NWProject, handle: str) -> None:
|
||||||
@@ -81,7 +81,8 @@ class NWItem:
|
|||||||
self._wordCount = 0 # Current word count
|
self._wordCount = 0 # Current word count
|
||||||
self._paraCount = 0 # Current paragraph count
|
self._paraCount = 0 # Current paragraph count
|
||||||
self._cursorPos = 0 # Last cursor position
|
self._cursorPos = 0 # Last cursor position
|
||||||
self._initCount = 0 # Initial word count
|
self._wordInit = 0 # Initial character count
|
||||||
|
self._charInit = 0 # Initial word count
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -164,9 +165,13 @@ class NWItem:
|
|||||||
def paraCount(self) -> int:
|
def paraCount(self) -> int:
|
||||||
return self._paraCount
|
return self._paraCount
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mainCount(self) -> int:
|
||||||
|
return self._charCount if CONFIG.useCharCount else self._wordCount
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def initCount(self) -> int:
|
def initCount(self) -> int:
|
||||||
return self._initCount
|
return self._wordInit if CONFIG.useCharCount else self._charInit
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cursorPos(self) -> int:
|
def cursorPos(self) -> int:
|
||||||
@@ -257,7 +262,8 @@ class NWItem:
|
|||||||
self._paraCount = 0
|
self._paraCount = 0
|
||||||
self._cursorPos = 0
|
self._cursorPos = 0
|
||||||
|
|
||||||
self._initCount = self._wordCount
|
self._wordInit = self._charCount
|
||||||
|
self._charInit = self._wordCount
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -265,23 +271,24 @@ class NWItem:
|
|||||||
def duplicate(cls, source: NWItem, handle: str) -> NWItem:
|
def duplicate(cls, source: NWItem, handle: str) -> NWItem:
|
||||||
"""Make a copy of an item."""
|
"""Make a copy of an item."""
|
||||||
new = cls(source._project, handle)
|
new = cls(source._project, handle)
|
||||||
new._name = source._name
|
new._name = source._name
|
||||||
new._parent = source._parent
|
new._parent = source._parent
|
||||||
new._root = source._root
|
new._root = source._root
|
||||||
new._order = source._order
|
new._order = source._order
|
||||||
new._type = source._type
|
new._type = source._type
|
||||||
new._class = source._class
|
new._class = source._class
|
||||||
new._layout = source._layout
|
new._layout = source._layout
|
||||||
new._status = source._status
|
new._status = source._status
|
||||||
new._import = source._import
|
new._import = source._import
|
||||||
new._active = source._active
|
new._active = source._active
|
||||||
new._expanded = source._expanded
|
new._expanded = source._expanded
|
||||||
new._heading = source._heading
|
new._heading = source._heading
|
||||||
new._charCount = source._charCount
|
new._charCount = source._charCount
|
||||||
new._wordCount = source._wordCount
|
new._wordCount = source._wordCount
|
||||||
new._paraCount = source._paraCount
|
new._paraCount = source._paraCount
|
||||||
new._cursorPos = source._cursorPos
|
new._cursorPos = source._cursorPos
|
||||||
new._initCount = source._initCount
|
new._wordInit = source._wordInit
|
||||||
|
new._charInit = source._charInit
|
||||||
return new
|
return new
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ class ProjectNode:
|
|||||||
|
|
||||||
def updateCount(self, propagate: bool = True) -> None:
|
def updateCount(self, propagate: bool = True) -> None:
|
||||||
"""Update counts, and propagate upwards in the tree."""
|
"""Update counts, and propagate upwards in the tree."""
|
||||||
self._count = self._item.wordCount + sum(c._count for c in self._children) # noqa: SLF001
|
self._count = self._item.mainCount + sum(c._count for c in self._children) # noqa: SLF001
|
||||||
self._cache[C_COUNT_TEXT] = f"{self._count:n}"
|
self._cache[C_COUNT_TEXT] = f"{self._count:n}"
|
||||||
if propagate and (parent := self._parent):
|
if propagate and (parent := self._parent):
|
||||||
parent.updateCount()
|
parent.updateCount()
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ class NWProject:
|
|||||||
# Often, the index needs to be rebuilt when updating format
|
# Often, the index needs to be rebuilt when updating format
|
||||||
self._index.rebuild()
|
self._index.rebuild()
|
||||||
|
|
||||||
self.updateWordCounts()
|
self.updateCounts()
|
||||||
self._session.startSession()
|
self._session.startSession()
|
||||||
self.setProjectChanged(False)
|
self.setProjectChanged(False)
|
||||||
self._valid = True
|
self._valid = True
|
||||||
@@ -397,7 +397,7 @@ class NWProject:
|
|||||||
else:
|
else:
|
||||||
self._data.incSaveCount()
|
self._data.incSaveCount()
|
||||||
|
|
||||||
self.updateWordCounts()
|
self.updateCounts()
|
||||||
self.countStatus()
|
self.countStatus()
|
||||||
|
|
||||||
xmlWriter = self._storage.getXmlWriter()
|
xmlWriter = self._storage.getXmlWriter()
|
||||||
@@ -515,10 +515,10 @@ class NWProject:
|
|||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateWordCounts(self) -> None:
|
def updateCounts(self) -> None:
|
||||||
"""Update the total word count values."""
|
"""Update the total word and character count values."""
|
||||||
novel, notes = self._tree.sumWords()
|
wNovel, wNotes, cNovel, cNotes = self._tree.sumCounts()
|
||||||
self._data.setCurrCounts(novel=novel, notes=notes)
|
self._data.setCurrCounts(wNovel=wNovel, wNotes=wNotes, cNovel=cNovel, cNotes=cNotes)
|
||||||
return
|
return
|
||||||
|
|
||||||
def countStatus(self) -> None:
|
def countStatus(self) -> None:
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ class NWProjectData:
|
|||||||
self._spellLang = None
|
self._spellLang = None
|
||||||
|
|
||||||
# Project Dictionaries
|
# Project Dictionaries
|
||||||
self._initCounts = [0, 0]
|
self._initCounts = [0, 0, 0, 0]
|
||||||
self._currCounts = [0, 0]
|
self._currCounts = [0, 0, 0, 0]
|
||||||
self._lastHandle: dict[str, str | None] = {
|
self._lastHandle: dict[str, str | None] = {
|
||||||
"editor": None,
|
"editor": None,
|
||||||
"viewer": None,
|
"viewer": None,
|
||||||
@@ -148,18 +148,18 @@ class NWProjectData:
|
|||||||
return self._spellLang
|
return self._spellLang
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def initCounts(self) -> tuple[int, int]:
|
def initCounts(self) -> tuple[int, int, int, int]:
|
||||||
"""Return the initial count of words for novel and note
|
"""Return the initial count of words and characters for novel
|
||||||
documents.
|
and note documents.
|
||||||
"""
|
"""
|
||||||
return self._initCounts[0], self._initCounts[1]
|
return self._initCounts[0], self._initCounts[1], self._initCounts[2], self._initCounts[3]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def currCounts(self) -> tuple[int, int]:
|
def currCounts(self) -> tuple[int, int, int, int]:
|
||||||
"""Return the current count of words for novel and note
|
"""Return the current count of words and characters for novel
|
||||||
documents.
|
and note documents.
|
||||||
"""
|
"""
|
||||||
return self._currCounts[0], self._currCounts[1]
|
return self._currCounts[0], self._currCounts[1], self._currCounts[2], self._currCounts[3]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lastHandle(self) -> dict[str, str | None]:
|
def lastHandle(self) -> dict[str, str | None]:
|
||||||
@@ -301,22 +301,40 @@ class NWProjectData:
|
|||||||
self._project.setProjectChanged(True)
|
self._project.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setInitCounts(self, novel: Any = None, notes: Any = None) -> None:
|
def setInitCounts(
|
||||||
"""Set the word count totals for novel and note files."""
|
self, wNovel: Any = None, wNotes: Any = None, cNovel: Any = None, cNotes: Any = None
|
||||||
if novel is not None:
|
) -> None:
|
||||||
self._initCounts[0] = checkInt(novel, 0)
|
"""Set the count totals for novel and note files."""
|
||||||
self._currCounts[0] = checkInt(novel, 0)
|
if wNovel is not None:
|
||||||
if notes is not None:
|
count = checkInt(wNovel, 0)
|
||||||
self._initCounts[1] = checkInt(notes, 0)
|
self._initCounts[0] = count
|
||||||
self._currCounts[1] = checkInt(notes, 0)
|
self._currCounts[0] = count
|
||||||
|
if wNotes is not None:
|
||||||
|
count = checkInt(wNotes, 0)
|
||||||
|
self._initCounts[1] = count
|
||||||
|
self._currCounts[1] = count
|
||||||
|
if cNovel is not None:
|
||||||
|
count = checkInt(cNovel, 0)
|
||||||
|
self._initCounts[2] = count
|
||||||
|
self._currCounts[2] = count
|
||||||
|
if cNotes is not None:
|
||||||
|
count = checkInt(cNotes, 0)
|
||||||
|
self._initCounts[3] = count
|
||||||
|
self._currCounts[3] = count
|
||||||
return
|
return
|
||||||
|
|
||||||
def setCurrCounts(self, novel: Any = None, notes: Any = None) -> None:
|
def setCurrCounts(
|
||||||
"""Set the word count totals for novel and note files."""
|
self, wNovel: Any = None, wNotes: Any = None, cNovel: Any = None, cNotes: Any = None
|
||||||
if novel is not None:
|
) -> None:
|
||||||
self._currCounts[0] = checkInt(novel, 0)
|
"""Set the count totals for novel and note files."""
|
||||||
if notes is not None:
|
if wNovel is not None:
|
||||||
self._currCounts[1] = checkInt(notes, 0)
|
self._currCounts[0] = checkInt(wNovel, 0)
|
||||||
|
if wNotes is not None:
|
||||||
|
self._currCounts[1] = checkInt(wNotes, 0)
|
||||||
|
if cNovel is not None:
|
||||||
|
self._currCounts[2] = checkInt(cNovel, 0)
|
||||||
|
if cNotes is not None:
|
||||||
|
self._currCounts[3] = checkInt(cNotes, 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setAutoReplace(self, value: dict) -> None:
|
def setAutoReplace(self, value: dict) -> None:
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ if TYPE_CHECKING:
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
FILE_VERSION = "1.5" # The current project file format version
|
FILE_VERSION = "1.5" # The current project file format version
|
||||||
FILE_REVISION = "4" # The current project file format revision
|
FILE_REVISION = "5" # The current project file format revision
|
||||||
HEX_VERSION = 0x0105
|
HEX_VERSION = 0x0105
|
||||||
|
|
||||||
NUM_VERSION = {
|
NUM_VERSION = {
|
||||||
@@ -109,6 +109,8 @@ class ProjectXMLReader:
|
|||||||
Rev 3: Added TEMPLATE class. 2.3.
|
Rev 3: Added TEMPLATE class. 2.3.
|
||||||
Rev 4: Added shape attribute to status and importance entry
|
Rev 4: Added shape attribute to status and importance entry
|
||||||
nodes. 2.5.
|
nodes. 2.5.
|
||||||
|
Rev 5: Added novelChars and notesChars attributes to content
|
||||||
|
node. 2.7 RC 1.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path: str | Path) -> None:
|
def __init__(self, path: str | Path) -> None:
|
||||||
@@ -286,9 +288,9 @@ class ProjectXMLReader:
|
|||||||
elif xItem.tag == "spellLang": # Changed to spellChecking in 1.5
|
elif xItem.tag == "spellLang": # Changed to spellChecking in 1.5
|
||||||
data.setSpellLang(xItem.text)
|
data.setSpellLang(xItem.text)
|
||||||
elif xItem.tag == "novelWordCount": # Moved to content attribute in 1.5
|
elif xItem.tag == "novelWordCount": # Moved to content attribute in 1.5
|
||||||
data.setInitCounts(novel=xItem.text)
|
data.setInitCounts(wNovel=xItem.text)
|
||||||
elif xItem.tag == "notesWordCount": # Moved to content attribute in 1.5
|
elif xItem.tag == "notesWordCount": # Moved to content attribute in 1.5
|
||||||
data.setInitCounts(notes=xItem.text)
|
data.setInitCounts(wNotes=xItem.text)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -298,8 +300,13 @@ class ProjectXMLReader:
|
|||||||
"""Parse the content section of the XML file."""
|
"""Parse the content section of the XML file."""
|
||||||
logger.debug("Parsing <content> section")
|
logger.debug("Parsing <content> section")
|
||||||
|
|
||||||
data.setInitCounts(novel=xSection.attrib.get("novelWords", None)) # Moved in 1.5
|
# Moved in 1.5
|
||||||
data.setInitCounts(notes=xSection.attrib.get("notesWords", None)) # Moved in 1.5
|
data.setInitCounts(
|
||||||
|
wNovel=xSection.attrib.get("novelWords", None),
|
||||||
|
wNotes=xSection.attrib.get("notesWords", None),
|
||||||
|
cNovel=xSection.attrib.get("novelChars", None),
|
||||||
|
cNotes=xSection.attrib.get("notesChars", None),
|
||||||
|
)
|
||||||
|
|
||||||
for xItem in xSection:
|
for xItem in xSection:
|
||||||
if xItem.tag != "item":
|
if xItem.tag != "item":
|
||||||
@@ -527,10 +534,13 @@ class ProjectXMLWriter:
|
|||||||
self._packSingleValue(xImport, "entry", label, attrib=attrib)
|
self._packSingleValue(xImport, "entry", label, attrib=attrib)
|
||||||
|
|
||||||
# Save Tree Content
|
# Save Tree Content
|
||||||
|
counts = data.currCounts
|
||||||
contAttr = {
|
contAttr = {
|
||||||
"items": str(len(content)),
|
"items": str(len(content)),
|
||||||
"novelWords": str(data.currCounts[0]),
|
"novelWords": str(counts[0]),
|
||||||
"notesWords": str(data.currCounts[1]),
|
"notesWords": str(counts[1]),
|
||||||
|
"novelChars": str(counts[2]),
|
||||||
|
"notesChars": str(counts[3]),
|
||||||
}
|
}
|
||||||
|
|
||||||
xContent = ET.SubElement(xRoot, "content", attrib=contAttr)
|
xContent = ET.SubElement(xRoot, "content", attrib=contAttr)
|
||||||
|
|||||||
@@ -79,29 +79,36 @@ class NWSessionLog:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
now = time()
|
now = time()
|
||||||
iNovel, iNotes = self._project.data.initCounts
|
iWNovel, iWNotes, iCNovel, iCNotes = self._project.data.initCounts
|
||||||
cNovel, cNotes = self._project.data.currCounts
|
cWNovel, cWNotes, cCNovel, cCNotes = self._project.data.currCounts
|
||||||
iTotal = iNovel + iNotes
|
iWTotal = iWNovel + iWNotes
|
||||||
wDiff = cNovel + cNotes - iTotal
|
iCTotal = iCNovel + iCNotes
|
||||||
|
wDiff = cWNovel + cWNotes - iWTotal
|
||||||
|
cDiff = cCNovel + cCNotes - iCTotal
|
||||||
sTime = now - self._start
|
sTime = now - self._start
|
||||||
|
|
||||||
logger.info("The session lasted %d sec and added %d words", int(sTime), wDiff)
|
logger.info(
|
||||||
if sTime < 300 and wDiff == 0:
|
"The session lasted %d sec and added %d words abd %d characters",
|
||||||
|
int(sTime), wDiff, cDiff
|
||||||
|
)
|
||||||
|
if sTime < 300 and (wDiff == 0 or cDiff == 0):
|
||||||
logger.info("Session too short, skipping log entry")
|
logger.info("Session too short, skipping log entry")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not sessFile.exists():
|
if not sessFile.exists():
|
||||||
with open(sessFile, mode="w", encoding="utf-8") as fObj:
|
with open(sessFile, mode="w", encoding="utf-8") as fObj:
|
||||||
fObj.write(self.createInitial(iTotal))
|
fObj.write(self.createInitial(iWTotal))
|
||||||
|
|
||||||
with open(sessFile, mode="a+", encoding="utf-8") as fObj:
|
with open(sessFile, mode="a+", encoding="utf-8") as fObj:
|
||||||
fObj.write(self.createRecord(
|
fObj.write(self.createRecord(
|
||||||
start=formatTimeStamp(self._start),
|
start=formatTimeStamp(self._start),
|
||||||
end=formatTimeStamp(now),
|
end=formatTimeStamp(now),
|
||||||
novel=cNovel,
|
novel=cWNovel,
|
||||||
notes=cNotes,
|
notes=cWNotes,
|
||||||
idle=round(idleTime)
|
idle=round(idleTime),
|
||||||
|
cnovel=cCNovel,
|
||||||
|
cnotes=cCNotes,
|
||||||
))
|
))
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -129,10 +136,19 @@ class NWSessionLog:
|
|||||||
data = json.dumps({"type": "initial", "offset": total})
|
data = json.dumps({"type": "initial", "offset": total})
|
||||||
return f"{data}\n"
|
return f"{data}\n"
|
||||||
|
|
||||||
def createRecord(self, start: str, end: str, novel: int, notes: int, idle: int) -> str:
|
def createRecord(
|
||||||
|
self, start: str, end: str, novel: int, notes: int, idle: int,
|
||||||
|
cnovel: int = 0, cnotes: int = 0,
|
||||||
|
) -> str:
|
||||||
"""Low level function to create a log record."""
|
"""Low level function to create a log record."""
|
||||||
data = json.dumps({
|
data = json.dumps({
|
||||||
"type": "record", "start": start, "end": end,
|
"type": "record",
|
||||||
"novel": novel, "notes": notes, "idle": idle,
|
"start": start,
|
||||||
|
"end": end,
|
||||||
|
"novel": novel,
|
||||||
|
"notes": notes,
|
||||||
|
"cnovel": cnovel,
|
||||||
|
"cnotes": cnotes,
|
||||||
|
"idle": idle,
|
||||||
})
|
})
|
||||||
return f"{data}\n"
|
return f"{data}\n"
|
||||||
|
|||||||
@@ -410,16 +410,20 @@ class NWTree:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def sumWords(self) -> tuple[int, int]:
|
def sumCounts(self) -> tuple[int, int, int, int]:
|
||||||
"""Loop over all entries and add up the word counts."""
|
"""Loop over all entries and add up the word and char counts."""
|
||||||
noteWords = 0
|
|
||||||
novelWords = 0
|
novelWords = 0
|
||||||
|
notesWords = 0
|
||||||
|
novelChars = 0
|
||||||
|
notesChars = 0
|
||||||
for item in self._items.values():
|
for item in self._items.values():
|
||||||
if item.itemLayout == nwItemLayout.NOTE:
|
if item.itemLayout == nwItemLayout.NOTE:
|
||||||
noteWords += item.wordCount
|
notesWords += item.wordCount
|
||||||
|
notesChars += item.charCount
|
||||||
elif item.itemLayout == nwItemLayout.DOCUMENT:
|
elif item.itemLayout == nwItemLayout.DOCUMENT:
|
||||||
novelWords += item.wordCount
|
novelWords += item.wordCount
|
||||||
return novelWords, noteWords
|
novelChars += item.charCount
|
||||||
|
return novelWords, notesWords, novelChars, notesChars
|
||||||
|
|
||||||
##
|
##
|
||||||
# Tree Item Methods
|
# Tree Item Methods
|
||||||
|
|||||||
@@ -226,6 +226,14 @@ class GuiPreferences(NDialog):
|
|||||||
self.tr("Turn off to use the Qt font dialog, which may have more options.")
|
self.tr("Turn off to use the Qt font dialog, which may have more options.")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Use Character Count
|
||||||
|
self.useCharCount = NSwitch(self)
|
||||||
|
self.useCharCount.setChecked(CONFIG.useCharCount)
|
||||||
|
self.mainForm.addRow(
|
||||||
|
self.tr("Prefer character count over word count"), self.useCharCount,
|
||||||
|
self.tr("Display character count instead where available.")
|
||||||
|
)
|
||||||
|
|
||||||
# Document Style
|
# Document Style
|
||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
@@ -957,21 +965,24 @@ class GuiPreferences(NDialog):
|
|||||||
refreshTree = False
|
refreshTree = False
|
||||||
|
|
||||||
# Appearance
|
# Appearance
|
||||||
guiLocale = self.guiLocale.currentData()
|
guiLocale = self.guiLocale.currentData()
|
||||||
guiTheme = self.guiTheme.currentData()
|
guiTheme = self.guiTheme.currentData()
|
||||||
iconTheme = self.iconTheme.currentData()
|
iconTheme = self.iconTheme.currentData()
|
||||||
|
useCharCount = self.useCharCount.isChecked()
|
||||||
|
|
||||||
updateTheme |= CONFIG.guiTheme != guiTheme
|
updateTheme |= CONFIG.guiTheme != guiTheme
|
||||||
updateTheme |= CONFIG.iconTheme != iconTheme
|
updateTheme |= CONFIG.iconTheme != iconTheme
|
||||||
needsRestart |= CONFIG.guiLocale != guiLocale
|
needsRestart |= CONFIG.guiLocale != guiLocale
|
||||||
needsRestart |= CONFIG.guiFont != self._guiFont
|
needsRestart |= CONFIG.guiFont != self._guiFont
|
||||||
|
refreshTree |= CONFIG.useCharCount != useCharCount
|
||||||
|
|
||||||
CONFIG.guiLocale = guiLocale
|
CONFIG.guiLocale = guiLocale
|
||||||
CONFIG.guiTheme = guiTheme
|
CONFIG.guiTheme = guiTheme
|
||||||
CONFIG.iconTheme = iconTheme
|
CONFIG.iconTheme = iconTheme
|
||||||
CONFIG.hideVScroll = self.hideVScroll.isChecked()
|
CONFIG.hideVScroll = self.hideVScroll.isChecked()
|
||||||
CONFIG.hideHScroll = self.hideHScroll.isChecked()
|
CONFIG.hideHScroll = self.hideHScroll.isChecked()
|
||||||
CONFIG.nativeFont = self.nativeFont.isChecked()
|
CONFIG.nativeFont = self.nativeFont.isChecked()
|
||||||
|
CONFIG.useCharCount = useCharCount
|
||||||
CONFIG.setGuiFont(self._guiFont)
|
CONFIG.setGuiFont(self._guiFont)
|
||||||
|
|
||||||
# Document Style
|
# Document Style
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ from novelwriter import CONFIG, SHARED
|
|||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
decodeMimeHandles, fontMatcher, minmax, qtAddAction, qtLambda, transferCase
|
decodeMimeHandles, fontMatcher, minmax, qtAddAction, qtLambda, transferCase
|
||||||
)
|
)
|
||||||
from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode
|
from novelwriter.constants import (
|
||||||
|
nwConst, nwKeyWords, nwLabels, nwShortcode, nwStats, nwUnicode, trStats
|
||||||
|
)
|
||||||
from novelwriter.core.document import NWDocument
|
from novelwriter.core.document import NWDocument
|
||||||
from novelwriter.enum import (
|
from novelwriter.enum import (
|
||||||
nwChange, nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass,
|
nwChange, nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass,
|
||||||
@@ -321,6 +323,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
"""
|
"""
|
||||||
# Auto-Replace
|
# Auto-Replace
|
||||||
self._autoReplace.initSettings()
|
self._autoReplace.initSettings()
|
||||||
|
self.docFooter.initSettings()
|
||||||
|
|
||||||
# Reload spell check and dictionaries
|
# Reload spell check and dictionaries
|
||||||
SHARED.updateSpellCheckLanguage()
|
SHARED.updateSpellCheckLanguage()
|
||||||
@@ -1233,7 +1236,8 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
"""Process the word counter's finished signal."""
|
"""Process the word counter's finished signal."""
|
||||||
if self._docHandle and self._nwItem:
|
if self._docHandle and self._nwItem:
|
||||||
logger.debug("Updating word count")
|
logger.debug("Updating word count")
|
||||||
needsRefresh = wCount != self._nwItem.wordCount
|
mCount = cCount if CONFIG.useCharCount else wCount
|
||||||
|
needsRefresh = mCount != self._nwItem.mainCount
|
||||||
self._nwItem.setCharCount(cCount)
|
self._nwItem.setCharCount(cCount)
|
||||||
self._nwItem.setWordCount(wCount)
|
self._nwItem.setWordCount(wCount)
|
||||||
self._nwItem.setParaCount(pCount)
|
self._nwItem.setParaCount(pCount)
|
||||||
@@ -1241,7 +1245,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
self._nwItem.notifyToRefresh()
|
self._nwItem.notifyToRefresh()
|
||||||
if not self.textCursor().hasSelection():
|
if not self.textCursor().hasSelection():
|
||||||
# Selection counter should take precedence (#2155)
|
# Selection counter should take precedence (#2155)
|
||||||
self.docFooter.updateWordCount(wCount, False)
|
self.docFooter.updateMainCount(mCount, False)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
@@ -1254,7 +1258,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
self._timerSel.start()
|
self._timerSel.start()
|
||||||
else:
|
else:
|
||||||
self._timerSel.stop()
|
self._timerSel.stop()
|
||||||
self.docFooter.updateWordCount(0, False)
|
self.docFooter.updateMainCount(0, False)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
@@ -1271,8 +1275,7 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
def _updateSelCounts(self, cCount: int, wCount: int, pCount: int) -> None:
|
def _updateSelCounts(self, cCount: int, wCount: int, pCount: int) -> None:
|
||||||
"""Update the counts on the counter's finished signal."""
|
"""Update the counts on the counter's finished signal."""
|
||||||
if self._docHandle and self._nwItem:
|
if self._docHandle and self._nwItem:
|
||||||
logger.debug("User selected %d words", wCount)
|
self.docFooter.updateMainCount(cCount if CONFIG.useCharCount else wCount, True)
|
||||||
self.docFooter.updateWordCount(wCount, True)
|
|
||||||
self._timerSel.stop()
|
self._timerSel.stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -3045,9 +3048,9 @@ class GuiDocEditFooter(QWidget):
|
|||||||
fPx = int(0.9*SHARED.theme.fontPixelSize)
|
fPx = int(0.9*SHARED.theme.fontPixelSize)
|
||||||
|
|
||||||
# Cached Translations
|
# Cached Translations
|
||||||
|
self.initSettings()
|
||||||
self._trLineCount = self.tr("Line: {0} ({1})")
|
self._trLineCount = self.tr("Line: {0} ({1})")
|
||||||
self._trWordCount = self.tr("Words: {0} ({1})")
|
self._trSelectCount = self.tr("Selected: {0}")
|
||||||
self._trSelectCount = self.tr("Words: {0} selected")
|
|
||||||
|
|
||||||
# Main Widget Settings
|
# Main Widget Settings
|
||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
@@ -3108,7 +3111,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
# Initialise Info
|
# Initialise Info
|
||||||
self.updateWordCount(0, False)
|
self.updateMainCount(0, False)
|
||||||
|
|
||||||
logger.debug("Ready: GuiDocEditFooter")
|
logger.debug("Ready: GuiDocEditFooter")
|
||||||
|
|
||||||
@@ -3118,6 +3121,13 @@ class GuiDocEditFooter(QWidget):
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def initSettings(self) -> None:
|
||||||
|
"""Apply user settings."""
|
||||||
|
self._trMainCount = trStats(nwLabels.STATS_DISPLAY[
|
||||||
|
nwStats.CHARS if CONFIG.useCharCount else nwStats.WORDS
|
||||||
|
])
|
||||||
|
return
|
||||||
|
|
||||||
def updateFont(self) -> None:
|
def updateFont(self) -> None:
|
||||||
"""Update the font settings."""
|
"""Update the font settings."""
|
||||||
self.setFont(SHARED.theme.guiFont)
|
self.setFont(SHARED.theme.guiFont)
|
||||||
@@ -3162,7 +3172,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
self._tItem = SHARED.project.tree[self._docHandle]
|
self._tItem = SHARED.project.tree[self._docHandle]
|
||||||
|
|
||||||
self.updateInfo()
|
self.updateInfo()
|
||||||
self.updateWordCount(0, False)
|
self.updateMainCount(0, False)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -3193,15 +3203,15 @@ class GuiDocEditFooter(QWidget):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
def updateWordCount(self, wCount: int, selection: bool) -> None:
|
def updateMainCount(self, count: int, selection: bool) -> None:
|
||||||
"""Update word counter information."""
|
"""Update main counter information."""
|
||||||
if selection and wCount:
|
if selection and count:
|
||||||
wText = self._trSelectCount.format(f"{wCount:n}")
|
text = self._trSelectCount.format(f"{count:n}")
|
||||||
elif self._tItem:
|
elif self._tItem:
|
||||||
wCount = self._tItem.wordCount
|
count = self._tItem.mainCount
|
||||||
wDiff = wCount - self._tItem.initCount
|
diff = count - self._tItem.initCount
|
||||||
wText = self._trWordCount.format(f"{wCount:n}", f"{wDiff:+n}")
|
text = self._trMainCount.format(f"{count:n}", f"{diff:+n}")
|
||||||
else:
|
else:
|
||||||
wText = self._trWordCount.format("0", "+0")
|
text = self._trMainCount.format("0", "+0")
|
||||||
self.wordsText.setText(wText)
|
self.wordsText.setText(text)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ class GuiNovelView(QWidget):
|
|||||||
# Function Mappings
|
# Function Mappings
|
||||||
self.setActive = self.novelBar.setActive
|
self.setActive = self.novelBar.setActive
|
||||||
self.getSelectedHandle = self.novelTree.getSelectedHandle
|
self.getSelectedHandle = self.novelTree.getSelectedHandle
|
||||||
|
self.refreshCurrentTree = self.novelBar.forceRefreshNovelTree
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -209,7 +210,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
# Refresh Button
|
# Refresh Button
|
||||||
self.tbRefresh = NIconToolButton(self, iSz)
|
self.tbRefresh = NIconToolButton(self, iSz)
|
||||||
self.tbRefresh.setToolTip(self.tr("Refresh"))
|
self.tbRefresh.setToolTip(self.tr("Refresh"))
|
||||||
self.tbRefresh.clicked.connect(self._forceRefreshNovelTree)
|
self.tbRefresh.clicked.connect(self.forceRefreshNovelTree)
|
||||||
|
|
||||||
# More Options Menu
|
# More Options Menu
|
||||||
self.mMore = QMenu(self)
|
self.mMore = QMenu(self)
|
||||||
@@ -274,7 +275,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
self.novelValue.updateTheme()
|
self.novelValue.updateTheme()
|
||||||
self.tbNovel.setVisible(self.novelValue.count() > 1)
|
self.tbNovel.setVisible(self.novelValue.count() > 1)
|
||||||
|
|
||||||
self._forceRefreshNovelTree()
|
self.forceRefreshNovelTree()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -306,7 +307,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
self.aLastCol[colType].setChecked(True)
|
self.aLastCol[colType].setChecked(True)
|
||||||
self.novelView.novelTree.setLastColType(colType)
|
self.novelView.novelTree.setLastColType(colType)
|
||||||
if doRefresh:
|
if doRefresh:
|
||||||
self._forceRefreshNovelTree()
|
self.forceRefreshNovelTree()
|
||||||
self.novelView.novelTree.resizeColumns()
|
self.novelView.novelTree.resizeColumns()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -324,11 +325,11 @@ class GuiNovelToolBar(QWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Private Slots
|
# Public Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _forceRefreshNovelTree(self) -> None:
|
def forceRefreshNovelTree(self) -> None:
|
||||||
"""Rebuild the current tree."""
|
"""Rebuild the current tree."""
|
||||||
if tHandle := self.novelValue.handle:
|
if tHandle := self.novelValue.handle:
|
||||||
self.novelView.setCurrentNovel(tHandle)
|
self.novelView.setCurrentNovel(tHandle)
|
||||||
@@ -336,6 +337,10 @@ class GuiNovelToolBar(QWidget):
|
|||||||
self._refresh[tHandle] = False
|
self._refresh[tHandle] = False
|
||||||
return
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Private Slots
|
||||||
|
##
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def _refreshNovelTree(self, tHandle: str) -> None:
|
def _refreshNovelTree(self, tHandle: str) -> None:
|
||||||
"""Refresh or schedule refresh of a novel tree."""
|
"""Refresh or schedule refresh of a novel tree."""
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from PyQt6.QtWidgets import QApplication, QLabel, QStatusBar, QWidget
|
|||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.common import formatTime
|
from novelwriter.common import formatTime
|
||||||
from novelwriter.constants import nwConst
|
from novelwriter.constants import nwConst, nwLabels, nwStats, trStats
|
||||||
from novelwriter.extensions.modified import NClickableLabel
|
from novelwriter.extensions.modified import NClickableLabel
|
||||||
from novelwriter.extensions.statusled import StatusLED
|
from novelwriter.extensions.statusled import StatusLED
|
||||||
|
|
||||||
@@ -108,11 +108,22 @@ class GuiMainStatus(QStatusBar):
|
|||||||
|
|
||||||
logger.debug("Ready: GuiMainStatus")
|
logger.debug("Ready: GuiMainStatus")
|
||||||
|
|
||||||
|
self.initSettings()
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
self.clearStatus()
|
self.clearStatus()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def initSettings(self) -> None:
|
||||||
|
"""Apply user settings."""
|
||||||
|
if CONFIG.useCharCount:
|
||||||
|
self._trStatsCount = trStats(nwLabels.STATS_DISPLAY[nwStats.CHARS])
|
||||||
|
self._trStatsTip = self.tr("Total character count (session change)")
|
||||||
|
else:
|
||||||
|
self._trStatsCount = trStats(nwLabels.STATS_DISPLAY[nwStats.WORDS])
|
||||||
|
self._trStatsTip = self.tr("Total word count (session change)")
|
||||||
|
return
|
||||||
|
|
||||||
def clearStatus(self) -> None:
|
def clearStatus(self) -> None:
|
||||||
"""Reset all widgets on the status bar to default values."""
|
"""Reset all widgets on the status bar to default values."""
|
||||||
self.setRefTime(-1.0)
|
self.setRefTime(-1.0)
|
||||||
@@ -173,11 +184,8 @@ class GuiMainStatus(QStatusBar):
|
|||||||
|
|
||||||
def setProjectStats(self, pWC: int, sWC: int) -> None:
|
def setProjectStats(self, pWC: int, sWC: int) -> None:
|
||||||
"""Update the current project statistics."""
|
"""Update the current project statistics."""
|
||||||
self.statsText.setText(self.tr("Words: {0} ({1})").format(f"{pWC:n}", f"{sWC:+n}"))
|
self.statsText.setText(self._trStatsCount.format(f"{pWC:n}", f"{sWC:+n}"))
|
||||||
if CONFIG.incNotesWCount:
|
self.statsText.setToolTip(self._trStatsTip)
|
||||||
self.statsText.setToolTip(self.tr("Project word count (session change)"))
|
|
||||||
else:
|
|
||||||
self.statsText.setToolTip(self.tr("Novel word count (session change)"))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def updateTime(self, idleTime: float = 0.0) -> None:
|
def updateTime(self, idleTime: float = 0.0) -> None:
|
||||||
|
|||||||
+19
-8
@@ -1048,8 +1048,10 @@ class GuiMain(QMainWindow):
|
|||||||
self.initMain()
|
self.initMain()
|
||||||
self.saveDocument()
|
self.saveDocument()
|
||||||
|
|
||||||
if tree:
|
if tree and not theme:
|
||||||
|
# These are also updated by a theme refresh
|
||||||
SHARED.project.tree.refreshAllItems()
|
SHARED.project.tree.refreshAllItems()
|
||||||
|
self.novelView.refreshCurrentTree()
|
||||||
|
|
||||||
if theme:
|
if theme:
|
||||||
SHARED.theme.loadTheme()
|
SHARED.theme.loadTheme()
|
||||||
@@ -1075,6 +1077,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.projView.initSettings()
|
self.projView.initSettings()
|
||||||
self.novelView.initSettings()
|
self.novelView.initSettings()
|
||||||
self.outlineView.initSettings()
|
self.outlineView.initSettings()
|
||||||
|
self.mainStatus.initSettings()
|
||||||
|
|
||||||
# Force update of word count
|
# Force update of word count
|
||||||
self._lastTotalCount = 0
|
self._lastTotalCount = 0
|
||||||
@@ -1261,15 +1264,23 @@ class GuiMain(QMainWindow):
|
|||||||
if self._lastTotalCount != currentTotalCount:
|
if self._lastTotalCount != currentTotalCount:
|
||||||
self._lastTotalCount = currentTotalCount
|
self._lastTotalCount = currentTotalCount
|
||||||
|
|
||||||
SHARED.project.updateWordCounts()
|
SHARED.project.updateCounts()
|
||||||
if CONFIG.incNotesWCount:
|
if CONFIG.incNotesWCount:
|
||||||
iTotal = sum(SHARED.project.data.initCounts)
|
if CONFIG.useCharCount:
|
||||||
cTotal = sum(SHARED.project.data.currCounts)
|
iTotal = sum(SHARED.project.data.initCounts[2:])
|
||||||
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
cTotal = sum(SHARED.project.data.currCounts[2:])
|
||||||
|
else:
|
||||||
|
iTotal = sum(SHARED.project.data.initCounts[:2])
|
||||||
|
cTotal = sum(SHARED.project.data.currCounts[:2])
|
||||||
else:
|
else:
|
||||||
iNovel, _ = SHARED.project.data.initCounts
|
if CONFIG.useCharCount:
|
||||||
cNovel, _ = SHARED.project.data.currCounts
|
iTotal = SHARED.project.data.initCounts[2]
|
||||||
self.mainStatus.setProjectStats(cNovel, cNovel - iNovel)
|
cTotal = SHARED.project.data.currCounts[2]
|
||||||
|
else:
|
||||||
|
iTotal = SHARED.project.data.initCounts[0]
|
||||||
|
cTotal = SHARED.project.data.currCounts[0]
|
||||||
|
|
||||||
|
self.mainStatus.setProjectStats(cTotal, cTotal - iTotal)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -257,8 +257,8 @@ class _OverviewPage(NScrollablePage):
|
|||||||
def updateProjectData(self) -> None:
|
def updateProjectData(self) -> None:
|
||||||
"""Load information about the project."""
|
"""Load information about the project."""
|
||||||
project = SHARED.project
|
project = SHARED.project
|
||||||
project.updateWordCounts()
|
project.updateCounts()
|
||||||
wcNovel, wcNotes = project.data.currCounts
|
wcNovel, wcNotes, _, _ = project.data.currCounts
|
||||||
|
|
||||||
self.projName.setText(project.data.name)
|
self.projName.setText(project.data.name)
|
||||||
self.projRevisions.setText(f"{project.data.saveCount:n}")
|
self.projRevisions.setText(f"{project.data.saveCount:n}")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="4" timeStamp="2025-04-20 19:01:45">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:07:59">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2178" autoCount="285" editTime="96586">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2179" autoCount="285" editTime="96588">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
</project>
|
</project>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<entry key="i56be10" count="1" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i56be10" count="1" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="31" novelWords="1016" notesWords="416">
|
<content items="31" novelWords="1016" notesWords="416" novelChars="5602" notesChars="2285">
|
||||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2022-11-07 13:00:48">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:07:59">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="5" autoCount="10" editTime="1000">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2179" autoCount="285" editTime="1000">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
<doBackup>yes</doBackup>
|
<doBackup>no</doBackup>
|
||||||
<language>en_GB</language>
|
<language>en_GB</language>
|
||||||
<spellChecking auto="yes">en_GB</spellChecking>
|
<spellChecking auto="yes">None</spellChecking>
|
||||||
<lastHandle>
|
<lastHandle>
|
||||||
<entry key="editor">636b6aa9b697b</entry>
|
<entry key="editor">636b6aa9b697b</entry>
|
||||||
<entry key="viewer">636b6aa9b697b</entry>
|
<entry key="viewer">636b6aa9b697b</entry>
|
||||||
@@ -20,32 +20,33 @@
|
|||||||
<entry key="C">D</entry>
|
<entry key="C">D</entry>
|
||||||
</autoReplace>
|
</autoReplace>
|
||||||
<status>
|
<status>
|
||||||
<entry key="sf12341" count="4" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
<entry key="sf12341" count="8" red="100" green="100" blue="100" shape="SQUARE">New</entry>
|
||||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
<entry key="sf24ce6" count="2" red="200" green="50" blue="0" shape="SQUARE">Notes</entry>
|
||||||
<entry key="sc24b8f" count="3" red="182" green="60" blue="0" shape="SQUARE">Started</entry>
|
<entry key="sc24b8f" count="3" red="182" green="60" blue="0" shape="BARS_1">Started</entry>
|
||||||
<entry key="s90e6c9" count="7" red="193" green="129" blue="0" shape="SQUARE">1st Draft</entry>
|
<entry key="s90e6c9" count="5" red="193" green="129" blue="0" shape="BARS_2">1st Draft</entry>
|
||||||
<entry key="sd51c5b" count="0" red="193" green="129" blue="0" shape="SQUARE">2nd Draft</entry>
|
<entry key="sd51c5b" count="1" red="193" green="129" blue="0" shape="BARS_3">2nd Draft</entry>
|
||||||
<entry key="s8ae72a" count="0" red="193" green="129" blue="0" shape="SQUARE">3rd Draft</entry>
|
<entry key="s8ae72a" count="1" red="193" green="129" blue="0" shape="BARS_4">3rd Draft</entry>
|
||||||
<entry key="s78ea90" count="1" red="58" green="180" blue="58" shape="SQUARE">Finished</entry>
|
<entry key="s78ea90" count="1" red="58" green="180" blue="58" shape="STAR">Finished</entry>
|
||||||
</status>
|
</status>
|
||||||
<importance>
|
<importance>
|
||||||
<entry key="ia857f0" count="5" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
<entry key="ia857f0" count="5" red="100" green="100" blue="100" shape="SQUARE">None</entry>
|
||||||
<entry key="icfb3a5" count="2" red="0" green="122" blue="188" shape="SQUARE">Minor</entry>
|
<entry key="i4a1d39" count="1" red="220" green="138" blue="221" shape="BLOCK_1">Background</entry>
|
||||||
<entry key="i2d7a54" count="2" red="21" green="0" blue="180" shape="SQUARE">Major</entry>
|
<entry key="icfb3a5" count="1" red="220" green="138" blue="221" shape="BLOCK_2">Minor</entry>
|
||||||
<entry key="i56be10" count="1" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
<entry key="i2d7a54" count="2" red="220" green="138" blue="221" shape="BLOCK_3">Major</entry>
|
||||||
|
<entry key="i56be10" count="1" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="27" novelWords="954" notesWords="409">
|
<content items="31" novelWords="1016" notesWords="416" novelChars="5602" notesChars="2285">
|
||||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H1" charCount="93" wordCount="19" paraCount="2" cursorPos="119" />
|
<meta expanded="no" heading="H1" charCount="148" wordCount="29" paraCount="4" cursorPos="178" />
|
||||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="277" />
|
<meta expanded="no" heading="H0" charCount="233" wordCount="47" paraCount="2" cursorPos="194" />
|
||||||
<name status="sf12341" import="ia857f0" active="yes">Page</name>
|
<name status="sf12341" import="ia857f0" active="yes">Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
@@ -57,28 +58,28 @@
|
|||||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="67" />
|
<meta expanded="no" heading="H3" charCount="2999" wordCount="530" paraCount="16" cursorPos="159" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="465" />
|
<meta expanded="no" heading="H3" charCount="548" wordCount="108" paraCount="3" cursorPos="650" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">Another Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="yes">Another Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="310" />
|
<meta expanded="no" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="1182" />
|
||||||
<name status="s78ea90" import="ia857f0" active="yes">Interlude</name>
|
<name status="s78ea90" import="ia857f0" active="yes">Interlude</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
<meta expanded="no" heading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="0" />
|
<meta expanded="no" heading="H1" charCount="1909" wordCount="346" paraCount="7" cursorPos="1940" />
|
||||||
<name status="sf24ce6" import="ia857f0" active="no">A Note on Structure</name>
|
<name status="sf24ce6" import="ia857f0" active="no">A Note on Structure</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="88706ddc78b1b" parent="7031beac91f75" root="7031beac91f75" order="6" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="yes" heading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="188" />
|
<meta expanded="yes" heading="H2" charCount="139" wordCount="28" paraCount="1" cursorPos="356" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">Chapter Two</name>
|
<name status="s90e6c9" import="ia857f0" active="yes">Chapter Two</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="ae7339df26ded" parent="88706ddc78b1b" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="0" />
|
<meta expanded="no" heading="H3" charCount="189" wordCount="37" paraCount="1" cursorPos="237" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">We Found John!</name>
|
<name status="sd51c5b" import="ia857f0" active="yes">We Found John!</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
@@ -89,8 +90,8 @@
|
|||||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="104" />
|
<meta expanded="no" heading="H2" charCount="299" wordCount="55" paraCount="2" cursorPos="387" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">Chapter One</name>
|
<name status="s8ae72a" import="ia857f0" active="yes">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
@@ -101,28 +102,28 @@
|
|||||||
<name status="sf12341" import="ia857f0">Main Characters</name>
|
<name status="sf12341" import="ia857f0">Main Characters</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="14298de4d9524" parent="f7e2d9f330615" root="f6622b4617424" order="0" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="24" />
|
<meta expanded="no" heading="H1" charCount="49" wordCount="9" paraCount="1" cursorPos="23" />
|
||||||
<name status="sf12341" import="icfb3a5" active="yes">John Smith</name>
|
<name status="sf12341" import="i2d7a54" active="yes">John Smith</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
<item handle="bb2c23b3c42cc" parent="f7e2d9f330615" root="f6622b4617424" order="1" type="FILE" class="CHARACTER" layout="NOTE">
|
||||||
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="25" />
|
<meta expanded="no" heading="H1" charCount="55" wordCount="9" paraCount="1" cursorPos="31" />
|
||||||
<name status="sf12341" import="i2d7a54" active="yes">Jane Smith</name>
|
<name status="sf12341" import="i56be10" active="yes">Jane Smith</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="sf12341" import="ia857f0">Locations</name>
|
<name status="sf12341" import="ia857f0">Locations</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="b3e74dbc1f584" parent="15c4492bd5107" root="15c4492bd5107" order="0" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="no" heading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="20" />
|
<meta expanded="no" heading="H1" charCount="76" wordCount="15" paraCount="1" cursorPos="111" />
|
||||||
<name status="sf12341" import="i56be10" active="yes">Earth</name>
|
<name status="sf12341" import="i2d7a54" active="yes">Earth</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="f1471bef9f2ae" parent="15c4492bd5107" root="15c4492bd5107" order="1" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="no" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="133" />
|
<meta expanded="no" heading="H1" charCount="115" wordCount="24" paraCount="1" cursorPos="0" />
|
||||||
<name status="sf12341" import="icfb3a5" active="yes">Space</name>
|
<name status="sf12341" import="i4a1d39" active="yes">Space</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
<item handle="5eaea4e8cdee8" parent="15c4492bd5107" root="15c4492bd5107" order="2" type="FILE" class="WORLD" layout="NOTE">
|
||||||
<meta expanded="no" heading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="45" />
|
<meta expanded="no" heading="H1" charCount="28" wordCount="6" paraCount="1" cursorPos="62" />
|
||||||
<name status="sf12341" import="i2d7a54" active="yes">Mars</name>
|
<name status="sf12341" import="icfb3a5" active="yes">Mars</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
@@ -136,7 +137,23 @@
|
|||||||
<meta expanded="no" heading="H3" charCount="232" wordCount="42" paraCount="1" cursorPos="239" />
|
<meta expanded="no" heading="H3" charCount="232" wordCount="42" paraCount="1" cursorPos="239" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">Old File</name>
|
<name status="s90e6c9" import="ia857f0" active="yes">Old File</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
|
<item handle="f4ed1ae756a1f" parent="None" root="f4ed1ae756a1f" order="5" type="ROOT" class="TEMPLATE">
|
||||||
|
<meta expanded="yes" />
|
||||||
|
<name status="sf12341" import="ia857f0">Templates</name>
|
||||||
|
</item>
|
||||||
|
<item handle="5aec885635c85" parent="f4ed1ae756a1f" root="f4ed1ae756a1f" order="0" type="FILE" class="TEMPLATE" layout="DOCUMENT">
|
||||||
|
<meta expanded="no" heading="H3" charCount="9" wordCount="2" paraCount="1" cursorPos="78" />
|
||||||
|
<name status="sf12341" import="ia857f0" active="yes">Scene</name>
|
||||||
|
</item>
|
||||||
|
<item handle="2a60782759c6f" parent="f4ed1ae756a1f" root="f4ed1ae756a1f" order="1" type="FILE" class="TEMPLATE" layout="DOCUMENT">
|
||||||
|
<meta expanded="no" heading="H2" charCount="11" wordCount="2" paraCount="1" cursorPos="81" />
|
||||||
|
<name status="sf12341" import="ia857f0" active="yes">Chapter</name>
|
||||||
|
</item>
|
||||||
|
<item handle="5ee8aebcdebc9" parent="f4ed1ae756a1f" root="f4ed1ae756a1f" order="2" type="FILE" class="TEMPLATE" layout="NOTE">
|
||||||
|
<meta expanded="no" heading="H1" charCount="53" wordCount="7" paraCount="1" cursorPos="75" />
|
||||||
|
<name status="sf12341" import="ia857f0" active="yes">Character Note</name>
|
||||||
|
</item>
|
||||||
|
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="6" type="ROOT" class="TRASH">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="sf12341" import="ia857f0">Trash</name>
|
<name status="sf12341" import="ia857f0">Trash</name>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.6b1" hexVersion="0x020600b1" fileVersion="1.5" fileRevision="4" timeStamp="2024-11-20 19:22:22">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:45:09">
|
||||||
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="51" autoCount="29" editTime="2456">
|
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="52" autoCount="29" editTime="2465">
|
||||||
<name>Lorem Ipsum</name>
|
<name>Lorem Ipsum</name>
|
||||||
<author>lipsum.com</author>
|
<author>lipsum.com</author>
|
||||||
</project>
|
</project>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<entry key="id6b1d0" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
<entry key="id6b1d0" count="0" red="50" green="200" blue="0" shape="SQUARE">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="22" novelWords="3115" notesWords="738">
|
<content items="22" novelWords="3115" notesWords="738" novelChars="20774" notesChars="5003">
|
||||||
<item handle="b3643d0f92e32" parent="None" root="b3643d0f92e32" order="0" type="ROOT" class="NOVEL">
|
<item handle="b3643d0f92e32" parent="None" root="b3643d0f92e32" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="sbaa94f" import="i613591">Novel</name>
|
<name status="sbaa94f" import="i613591">Novel</name>
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
<meta expanded="no" heading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="47" />
|
<meta expanded="no" heading="H1" charCount="1770" wordCount="259" paraCount="3" cursorPos="47" />
|
||||||
<name status="sbaa94f" import="i613591" active="yes">Ancient Europe</name>
|
<name status="sbaa94f" import="i613591" active="yes">Ancient Europe</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="1ace7ab1a0fc6" parent="None" root="1ace7ab1a0fc6" order="0" type="ROOT" class="TRASH">
|
<item handle="1ace7ab1a0fc6" parent="None" root="1ace7ab1a0fc6" order="4" type="ROOT" class="TRASH">
|
||||||
<meta expanded="no" />
|
<meta expanded="no" />
|
||||||
<name status="sbaa94f" import="i613591">Trash</name>
|
<name status="sbaa94f" import="i613591">Trash</name>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="4" timeStamp="2025-05-11 12:42:48">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:42:54">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="12" novelWords="10" notesWords="6">
|
<content items="12" novelWords="10" notesWords="6" novelChars="45" notesChars="22">
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="no" />
|
<meta expanded="no" />
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="4" timeStamp="2025-05-11 12:42:48">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:42:54">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="16" novelWords="9" notesWords="0">
|
<content items="16" novelWords="9" notesWords="0" novelChars="40" notesChars="0">
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="no" />
|
<meta expanded="no" />
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="4" timeStamp="2025-05-11 12:42:46">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:42:52">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="19" novelWords="28" notesWords="0">
|
<content items="19" novelWords="28" notesWords="0" novelChars="129" notesChars="0">
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="no" />
|
<meta expanded="no" />
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="4" timeStamp="2025-05-11 12:42:46">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:42:52">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||||
<name>Test Project A</name>
|
<name>Test Project A</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="22" novelWords="0" notesWords="0">
|
<content items="22" novelWords="0" notesWords="0" novelChars="0" notesChars="0">
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="no" />
|
<meta expanded="no" />
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="4" timeStamp="2025-05-11 12:42:46">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:42:52">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||||
<name>Test Project B</name>
|
<name>Test Project B</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="16" novelWords="0" notesWords="0">
|
<content items="16" novelWords="0" notesWords="0" novelChars="0" notesChars="0">
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="no" />
|
<meta expanded="no" />
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<ns0:Properties xmlns:ns0="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
|
<ns0:Properties xmlns:ns0="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties">
|
||||||
<ns0:TotalTime>40</ns0:TotalTime>
|
<ns0:TotalTime>41</ns0:TotalTime>
|
||||||
<ns0:Application>novelWriter/2.6a3</ns0:Application>
|
<ns0:Application>novelWriter/2.7b1</ns0:Application>
|
||||||
<ns0:Words>4035</ns0:Words>
|
<ns0:Words>4035</ns0:Words>
|
||||||
<ns0:Characters>21296</ns0:Characters>
|
<ns0:Characters>21296</ns0:Characters>
|
||||||
<ns0:CharactersWithSpaces>24964</ns0:CharactersWithSpaces>
|
<ns0:CharactersWithSpaces>24964</ns0:CharactersWithSpaces>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<dcterms:created xsi:type="dcterms:W3CDTF">2024-11-20T19:45:15</dcterms:created>
|
<dcterms:created xsi:type="dcterms:W3CDTF">2025-04-29T22:46:36</dcterms:created>
|
||||||
<dcterms:modified xsi:type="dcterms:W3CDTF">2024-11-20T19:45:15</dcterms:modified>
|
<dcterms:modified xsi:type="dcterms:W3CDTF">2025-04-29T22:46:36</dcterms:modified>
|
||||||
<dc:creator>lipsum.com</dc:creator>
|
<dc:creator>lipsum.com</dc:creator>
|
||||||
<dc:title>Lorem Ipsum</dc:title>
|
<dc:title>Lorem Ipsum</dc:title>
|
||||||
<dc:language>en_GB</dc:language>
|
<dc:language>en_GB</dc:language>
|
||||||
<cp:revision>51</cp:revision>
|
<cp:revision>52</cp:revision>
|
||||||
<cp:lastModifiedBy>lipsum.com</cp:lastModifiedBy>
|
<cp:lastModifiedBy>lipsum.com</cp:lastModifiedBy>
|
||||||
</cp:coreProperties>
|
</cp:coreProperties>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="4" timeStamp="2025-05-11 12:42:59">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:08:41">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="4">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="4">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="12" novelWords="173" notesWords="27">
|
<content items="12" novelWords="173" notesWords="27" novelChars="1007" notesChars="133">
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="4" timeStamp="2025-05-11 12:39:41">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2025-04-29 22:01:24">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="2" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="2" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i000007" count="0" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="8" novelWords="9" notesWords="0">
|
<content items="8" novelWords="9" notesWords="0" novelChars="40" notesChars="0">
|
||||||
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="no" />
|
<meta expanded="no" />
|
||||||
<name status="s000000" import="i000004">Novel</name>
|
<name status="s000000" import="i000004">Novel</name>
|
||||||
|
|||||||
@@ -38,10 +38,10 @@
|
|||||||
"metaAttr": {
|
"metaAttr": {
|
||||||
"expanded": false,
|
"expanded": false,
|
||||||
"heading": "H1",
|
"heading": "H1",
|
||||||
"charCount": 93,
|
"charCount": 148,
|
||||||
"wordCount": 19,
|
"wordCount": 29,
|
||||||
"paraCount": 2,
|
"paraCount": 4,
|
||||||
"cursorPos": 119
|
"cursorPos": 178
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "sc24b8f",
|
"status": "sc24b8f",
|
||||||
@@ -63,10 +63,10 @@
|
|||||||
"metaAttr": {
|
"metaAttr": {
|
||||||
"expanded": false,
|
"expanded": false,
|
||||||
"heading": "H0",
|
"heading": "H0",
|
||||||
"charCount": 251,
|
"charCount": 233,
|
||||||
"wordCount": 50,
|
"wordCount": 47,
|
||||||
"paraCount": 2,
|
"paraCount": 2,
|
||||||
"cursorPos": 277
|
"cursorPos": 194
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "sf12341",
|
"status": "sf12341",
|
||||||
@@ -138,10 +138,10 @@
|
|||||||
"metaAttr": {
|
"metaAttr": {
|
||||||
"expanded": false,
|
"expanded": false,
|
||||||
"heading": "H3",
|
"heading": "H3",
|
||||||
"charCount": 2687,
|
"charCount": 2999,
|
||||||
"wordCount": 479,
|
"wordCount": 530,
|
||||||
"paraCount": 14,
|
"paraCount": 16,
|
||||||
"cursorPos": 67
|
"cursorPos": 159
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "s90e6c9",
|
"status": "s90e6c9",
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
"charCount": 548,
|
"charCount": 548,
|
||||||
"wordCount": 108,
|
"wordCount": 108,
|
||||||
"paraCount": 3,
|
"paraCount": 3,
|
||||||
"cursorPos": 465
|
"cursorPos": 650
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "s90e6c9",
|
"status": "s90e6c9",
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
"charCount": 617,
|
"charCount": 617,
|
||||||
"wordCount": 101,
|
"wordCount": 101,
|
||||||
"paraCount": 3,
|
"paraCount": 3,
|
||||||
"cursorPos": 310
|
"cursorPos": 1182
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "s78ea90",
|
"status": "s78ea90",
|
||||||
@@ -216,7 +216,7 @@
|
|||||||
"charCount": 1909,
|
"charCount": 1909,
|
||||||
"wordCount": 346,
|
"wordCount": 346,
|
||||||
"paraCount": 7,
|
"paraCount": 7,
|
||||||
"cursorPos": 0
|
"cursorPos": 1940
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "sf24ce6",
|
"status": "sf24ce6",
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
"charCount": 139,
|
"charCount": 139,
|
||||||
"wordCount": 28,
|
"wordCount": 28,
|
||||||
"paraCount": 1,
|
"paraCount": 1,
|
||||||
"cursorPos": 188
|
"cursorPos": 356
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "s90e6c9",
|
"status": "s90e6c9",
|
||||||
@@ -266,10 +266,10 @@
|
|||||||
"charCount": 189,
|
"charCount": 189,
|
||||||
"wordCount": 37,
|
"wordCount": 37,
|
||||||
"paraCount": 1,
|
"paraCount": 1,
|
||||||
"cursorPos": 0
|
"cursorPos": 237
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "s90e6c9",
|
"status": "sd51c5b",
|
||||||
"import": "ia857f0",
|
"import": "ia857f0",
|
||||||
"active": true
|
"active": true
|
||||||
}
|
}
|
||||||
@@ -341,10 +341,10 @@
|
|||||||
"charCount": 299,
|
"charCount": 299,
|
||||||
"wordCount": 55,
|
"wordCount": 55,
|
||||||
"paraCount": 2,
|
"paraCount": 2,
|
||||||
"cursorPos": 104
|
"cursorPos": 387
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "s90e6c9",
|
"status": "s8ae72a",
|
||||||
"import": "ia857f0",
|
"import": "ia857f0",
|
||||||
"active": true
|
"active": true
|
||||||
}
|
}
|
||||||
@@ -416,11 +416,11 @@
|
|||||||
"charCount": 49,
|
"charCount": 49,
|
||||||
"wordCount": 9,
|
"wordCount": 9,
|
||||||
"paraCount": 1,
|
"paraCount": 1,
|
||||||
"cursorPos": 24
|
"cursorPos": 23
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "sf12341",
|
"status": "sf12341",
|
||||||
"import": "icfb3a5",
|
"import": "i2d7a54",
|
||||||
"active": true
|
"active": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -441,11 +441,11 @@
|
|||||||
"charCount": 55,
|
"charCount": 55,
|
||||||
"wordCount": 9,
|
"wordCount": 9,
|
||||||
"paraCount": 1,
|
"paraCount": 1,
|
||||||
"cursorPos": 25
|
"cursorPos": 31
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "sf12341",
|
"status": "sf12341",
|
||||||
"import": "i2d7a54",
|
"import": "i56be10",
|
||||||
"active": true
|
"active": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -491,11 +491,11 @@
|
|||||||
"charCount": 76,
|
"charCount": 76,
|
||||||
"wordCount": 15,
|
"wordCount": 15,
|
||||||
"paraCount": 1,
|
"paraCount": 1,
|
||||||
"cursorPos": 20
|
"cursorPos": 111
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "sf12341",
|
"status": "sf12341",
|
||||||
"import": "i56be10",
|
"import": "i2d7a54",
|
||||||
"active": true
|
"active": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -516,11 +516,11 @@
|
|||||||
"charCount": 115,
|
"charCount": 115,
|
||||||
"wordCount": 24,
|
"wordCount": 24,
|
||||||
"paraCount": 1,
|
"paraCount": 1,
|
||||||
"cursorPos": 133
|
"cursorPos": 0
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "sf12341",
|
"status": "sf12341",
|
||||||
"import": "icfb3a5",
|
"import": "i4a1d39",
|
||||||
"active": true
|
"active": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -541,11 +541,11 @@
|
|||||||
"charCount": 28,
|
"charCount": 28,
|
||||||
"wordCount": 6,
|
"wordCount": 6,
|
||||||
"paraCount": 1,
|
"paraCount": 1,
|
||||||
"cursorPos": 45
|
"cursorPos": 62
|
||||||
},
|
},
|
||||||
"nameAttr": {
|
"nameAttr": {
|
||||||
"status": "sf12341",
|
"status": "sf12341",
|
||||||
"import": "i2d7a54",
|
"import": "icfb3a5",
|
||||||
"active": true
|
"active": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -624,13 +624,113 @@
|
|||||||
"active": true
|
"active": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Templates",
|
||||||
|
"itemAttr": {
|
||||||
|
"handle": "f4ed1ae756a1f",
|
||||||
|
"parent": null,
|
||||||
|
"root": "f4ed1ae756a1f",
|
||||||
|
"order": 5,
|
||||||
|
"type": "ROOT",
|
||||||
|
"class": "TEMPLATE",
|
||||||
|
"layout": "NO_LAYOUT"
|
||||||
|
},
|
||||||
|
"metaAttr": {
|
||||||
|
"expanded": true,
|
||||||
|
"heading": "H0",
|
||||||
|
"charCount": 0,
|
||||||
|
"wordCount": 0,
|
||||||
|
"paraCount": 0,
|
||||||
|
"cursorPos": 0
|
||||||
|
},
|
||||||
|
"nameAttr": {
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Scene",
|
||||||
|
"itemAttr": {
|
||||||
|
"handle": "5aec885635c85",
|
||||||
|
"parent": "f4ed1ae756a1f",
|
||||||
|
"root": "f4ed1ae756a1f",
|
||||||
|
"order": 0,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "TEMPLATE",
|
||||||
|
"layout": "DOCUMENT"
|
||||||
|
},
|
||||||
|
"metaAttr": {
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H3",
|
||||||
|
"charCount": 9,
|
||||||
|
"wordCount": 2,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 78
|
||||||
|
},
|
||||||
|
"nameAttr": {
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Chapter",
|
||||||
|
"itemAttr": {
|
||||||
|
"handle": "2a60782759c6f",
|
||||||
|
"parent": "f4ed1ae756a1f",
|
||||||
|
"root": "f4ed1ae756a1f",
|
||||||
|
"order": 1,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "TEMPLATE",
|
||||||
|
"layout": "DOCUMENT"
|
||||||
|
},
|
||||||
|
"metaAttr": {
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H2",
|
||||||
|
"charCount": 11,
|
||||||
|
"wordCount": 2,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 81
|
||||||
|
},
|
||||||
|
"nameAttr": {
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Character Note",
|
||||||
|
"itemAttr": {
|
||||||
|
"handle": "5ee8aebcdebc9",
|
||||||
|
"parent": "f4ed1ae756a1f",
|
||||||
|
"root": "f4ed1ae756a1f",
|
||||||
|
"order": 2,
|
||||||
|
"type": "FILE",
|
||||||
|
"class": "TEMPLATE",
|
||||||
|
"layout": "NOTE"
|
||||||
|
},
|
||||||
|
"metaAttr": {
|
||||||
|
"expanded": false,
|
||||||
|
"heading": "H1",
|
||||||
|
"charCount": 53,
|
||||||
|
"wordCount": 7,
|
||||||
|
"paraCount": 1,
|
||||||
|
"cursorPos": 75
|
||||||
|
},
|
||||||
|
"nameAttr": {
|
||||||
|
"status": "sf12341",
|
||||||
|
"import": "ia857f0",
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Trash",
|
"name": "Trash",
|
||||||
"itemAttr": {
|
"itemAttr": {
|
||||||
"handle": "98acd8c76c93a",
|
"handle": "98acd8c76c93a",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"root": "98acd8c76c93a",
|
"root": "98acd8c76c93a",
|
||||||
"order": 5,
|
"order": 6,
|
||||||
"type": "ROOT",
|
"type": "ROOT",
|
||||||
"class": "TRASH",
|
"class": "TRASH",
|
||||||
"layout": "NO_LAYOUT"
|
"layout": "NO_LAYOUT"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2020-05-28 09:59:15">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2020-05-28 09:59:15">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="0" autoCount="0" editTime="1000">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="0" autoCount="0" editTime="1000">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="22" novelWords="0" notesWords="0">
|
<content items="22" novelWords="0" notesWords="0" novelChars="0" notesChars="0">
|
||||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="s000002" import="i000007">Novel</name>
|
<name status="s000002" import="i000007">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2020-06-26 21:20:24">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2020-06-26 21:20:24">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="22" novelWords="0" notesWords="0">
|
<content items="22" novelWords="0" notesWords="0" novelChars="0" notesChars="0">
|
||||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="s000002" import="i000007">Novel</name>
|
<name status="s000002" import="i000007">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2021-08-30 23:33:44">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2021-08-30 23:33:44">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="25" novelWords="840" notesWords="376">
|
<content items="25" novelWords="840" notesWords="376" novelChars="0" notesChars="0">
|
||||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="s000002" import="i000007">Novel</name>
|
<name status="s000002" import="i000007">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2022-10-25 18:26:15">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2022-10-25 18:26:15">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
<entry key="i00000a" count="0" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="25" novelWords="830" notesWords="376">
|
<content items="25" novelWords="830" notesWords="376" novelChars="0" notesChars="0">
|
||||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="s000002" import="i000007">Novel</name>
|
<name status="s000002" import="i000007">Novel</name>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="4" timeStamp="2022-10-15 12:12:59">
|
<novelWriterXML appVersion="2.7b1" hexVersion="0x020700b1" fileVersion="1.5" fileRevision="5" timeStamp="2022-10-15 12:12:59">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="5" autoCount="10" editTime="1000">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jay Doh</author>
|
<author>Jay Doh</author>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<entry key="i56be10" count="1" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
<entry key="i56be10" count="1" red="117" green="0" blue="175" shape="SQUARE">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="27" novelWords="954" notesWords="409">
|
<content items="27" novelWords="954" notesWords="409" novelChars="0" notesChars="0">
|
||||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import pytest
|
|||||||
|
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon
|
||||||
|
|
||||||
|
from novelwriter import CONFIG
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
|
||||||
@@ -168,6 +169,15 @@ def testCoreItem_Setters(mockGUI, mockRnd, fncPath):
|
|||||||
item.setParaCount(1)
|
item.setParaCount(1)
|
||||||
assert item.paraCount == 1
|
assert item.paraCount == 1
|
||||||
|
|
||||||
|
# MainCount
|
||||||
|
item.setWordCount(123)
|
||||||
|
item.setCharCount(1234)
|
||||||
|
CONFIG.useCharCount = False
|
||||||
|
assert item.mainCount == 123
|
||||||
|
CONFIG.useCharCount = True
|
||||||
|
assert item.mainCount == 1234
|
||||||
|
CONFIG.useCharCount = False
|
||||||
|
|
||||||
# CursorPos
|
# CursorPos
|
||||||
item.setCursorPos(None)
|
item.setCursorPos(None)
|
||||||
assert item.cursorPos == 0
|
assert item.cursorPos == 0
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ class MockProject:
|
|||||||
@pytest.fixture(scope="function", autouse=True)
|
@pytest.fixture(scope="function", autouse=True)
|
||||||
def mockVersion(monkeypatch):
|
def mockVersion(monkeypatch):
|
||||||
"""Mock the version info to prevent diff from failing."""
|
"""Mock the version info to prevent diff from failing."""
|
||||||
monkeypatch.setattr("novelwriter.core.projectxml.__version__", "2.0-rc1")
|
monkeypatch.setattr("novelwriter.core.projectxml.__version__", "2.7b1")
|
||||||
monkeypatch.setattr("novelwriter.core.projectxml.__hexversion__", "0x020000c1")
|
monkeypatch.setattr("novelwriter.core.projectxml.__hexversion__", "0x020700b1")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -137,23 +137,23 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, mockGUI, tstPaths, fncPath):
|
|||||||
assert xmlReader.state == XMLReadState.PARSED_OK
|
assert xmlReader.state == XMLReadState.PARSED_OK
|
||||||
assert xmlReader.xmlRoot == "novelWriterXML"
|
assert xmlReader.xmlRoot == "novelWriterXML"
|
||||||
assert xmlReader.xmlVersion == 0x0105
|
assert xmlReader.xmlVersion == 0x0105
|
||||||
assert xmlReader.xmlRevision == 4
|
assert xmlReader.xmlRevision == 5
|
||||||
assert xmlReader.appVersion == "2.0-rc1"
|
assert xmlReader.appVersion == "2.7b1"
|
||||||
assert xmlReader.hexVersion == 0x020000c1
|
assert xmlReader.hexVersion == 0x020700b1
|
||||||
|
|
||||||
# Check loaded data
|
# Check loaded data
|
||||||
assert data.name == "Sample Project"
|
assert data.name == "Sample Project"
|
||||||
assert data.author == "Jane Smith"
|
assert data.author == "Jane Smith"
|
||||||
assert data.saveCount == 5
|
assert data.saveCount == 2179
|
||||||
assert data.autoCount == 10
|
assert data.autoCount == 285
|
||||||
assert data.editTime == 1000
|
assert data.editTime == 1000
|
||||||
|
|
||||||
assert data.doBackup is True
|
assert data.doBackup is False
|
||||||
assert data.language == "en_GB"
|
assert data.language == "en_GB"
|
||||||
assert data.spellCheck is True
|
assert data.spellCheck is True
|
||||||
assert data.spellLang == "en_GB"
|
assert data.spellLang is None
|
||||||
assert data.initCounts == (954, 409)
|
assert data.initCounts == (1016, 416, 5602, 2285)
|
||||||
assert data.currCounts == (954, 409)
|
assert data.currCounts == (1016, 416, 5602, 2285)
|
||||||
|
|
||||||
assert data.getLastHandle("editor") == "636b6aa9b697b"
|
assert data.getLastHandle("editor") == "636b6aa9b697b"
|
||||||
assert data.getLastHandle("viewer") == "636b6aa9b697b"
|
assert data.getLastHandle("viewer") == "636b6aa9b697b"
|
||||||
@@ -182,33 +182,36 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, mockGUI, tstPaths, fncPath):
|
|||||||
assert data.itemStatus["s78ea90"].color == QColor(58, 180, 58)
|
assert data.itemStatus["s78ea90"].color == QColor(58, 180, 58)
|
||||||
|
|
||||||
assert data.itemImport["ia857f0"].color == QColor(100, 100, 100)
|
assert data.itemImport["ia857f0"].color == QColor(100, 100, 100)
|
||||||
assert data.itemImport["icfb3a5"].color == QColor(0, 122, 188)
|
assert data.itemImport["i4a1d39"].color == QColor(220, 138, 221)
|
||||||
assert data.itemImport["i2d7a54"].color == QColor(21, 0, 180)
|
assert data.itemImport["icfb3a5"].color == QColor(220, 138, 221)
|
||||||
assert data.itemImport["i56be10"].color == QColor(117, 0, 175)
|
assert data.itemImport["i2d7a54"].color == QColor(220, 138, 221)
|
||||||
|
assert data.itemImport["i56be10"].color == QColor(220, 138, 221)
|
||||||
|
|
||||||
assert data.itemStatus["sf12341"].shape == nwStatusShape.SQUARE
|
assert data.itemStatus["sf12341"].shape == nwStatusShape.SQUARE
|
||||||
assert data.itemStatus["sf24ce6"].shape == nwStatusShape.SQUARE
|
assert data.itemStatus["sf24ce6"].shape == nwStatusShape.SQUARE
|
||||||
assert data.itemStatus["sc24b8f"].shape == nwStatusShape.SQUARE
|
assert data.itemStatus["sc24b8f"].shape == nwStatusShape.BARS_1
|
||||||
assert data.itemStatus["s90e6c9"].shape == nwStatusShape.SQUARE
|
assert data.itemStatus["s90e6c9"].shape == nwStatusShape.BARS_2
|
||||||
assert data.itemStatus["sd51c5b"].shape == nwStatusShape.SQUARE
|
assert data.itemStatus["sd51c5b"].shape == nwStatusShape.BARS_3
|
||||||
assert data.itemStatus["s8ae72a"].shape == nwStatusShape.SQUARE
|
assert data.itemStatus["s8ae72a"].shape == nwStatusShape.BARS_4
|
||||||
assert data.itemStatus["s78ea90"].shape == nwStatusShape.SQUARE
|
assert data.itemStatus["s78ea90"].shape == nwStatusShape.STAR
|
||||||
|
|
||||||
assert data.itemImport["ia857f0"].shape == nwStatusShape.SQUARE
|
assert data.itemImport["ia857f0"].shape == nwStatusShape.SQUARE
|
||||||
assert data.itemImport["icfb3a5"].shape == nwStatusShape.SQUARE
|
assert data.itemImport["i4a1d39"].shape == nwStatusShape.BLOCK_1
|
||||||
assert data.itemImport["i2d7a54"].shape == nwStatusShape.SQUARE
|
assert data.itemImport["icfb3a5"].shape == nwStatusShape.BLOCK_2
|
||||||
assert data.itemImport["i56be10"].shape == nwStatusShape.SQUARE
|
assert data.itemImport["i2d7a54"].shape == nwStatusShape.BLOCK_3
|
||||||
|
assert data.itemImport["i56be10"].shape == nwStatusShape.BLOCK_4
|
||||||
|
|
||||||
assert data.itemStatus["sf12341"].count == 4
|
assert data.itemStatus["sf12341"].count == 8
|
||||||
assert data.itemStatus["sf24ce6"].count == 2
|
assert data.itemStatus["sf24ce6"].count == 2
|
||||||
assert data.itemStatus["sc24b8f"].count == 3
|
assert data.itemStatus["sc24b8f"].count == 3
|
||||||
assert data.itemStatus["s90e6c9"].count == 7
|
assert data.itemStatus["s90e6c9"].count == 5
|
||||||
assert data.itemStatus["sd51c5b"].count == 0
|
assert data.itemStatus["sd51c5b"].count == 1
|
||||||
assert data.itemStatus["s8ae72a"].count == 0
|
assert data.itemStatus["s8ae72a"].count == 1
|
||||||
assert data.itemStatus["s78ea90"].count == 1
|
assert data.itemStatus["s78ea90"].count == 1
|
||||||
|
|
||||||
assert data.itemImport["ia857f0"].count == 5
|
assert data.itemImport["ia857f0"].count == 5
|
||||||
assert data.itemImport["icfb3a5"].count == 2
|
assert data.itemImport["i4a1d39"].count == 1
|
||||||
|
assert data.itemImport["icfb3a5"].count == 1
|
||||||
assert data.itemImport["i2d7a54"].count == 2
|
assert data.itemImport["i2d7a54"].count == 2
|
||||||
assert data.itemImport["i56be10"].count == 1
|
assert data.itemImport["i56be10"].count == 1
|
||||||
|
|
||||||
@@ -281,8 +284,8 @@ def testCoreProjectXML_ReadLegacy10(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
assert data.language is None # Doesn't exist in 1.0
|
assert data.language is None # Doesn't exist in 1.0
|
||||||
assert data.spellCheck is True
|
assert data.spellCheck is True
|
||||||
assert data.spellLang is None # Doesn't exist in 1.0
|
assert data.spellLang is None # Doesn't exist in 1.0
|
||||||
assert data.initCounts == (0, 0)
|
assert data.initCounts == (0, 0, 0, 0)
|
||||||
assert data.currCounts == (0, 0)
|
assert data.currCounts == (0, 0, 0, 0)
|
||||||
|
|
||||||
assert data.getLastHandle("editor") is None # Dropped by conversion
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
@@ -426,8 +429,8 @@ def testCoreProjectXML_ReadLegacy11(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
assert data.language is None # Doesn't exist in 1.1
|
assert data.language is None # Doesn't exist in 1.1
|
||||||
assert data.spellCheck is True
|
assert data.spellCheck is True
|
||||||
assert data.spellLang is None # Doesn't exist in 1.1
|
assert data.spellLang is None # Doesn't exist in 1.1
|
||||||
assert data.initCounts == (0, 0)
|
assert data.initCounts == (0, 0, 0, 0)
|
||||||
assert data.currCounts == (0, 0)
|
assert data.currCounts == (0, 0, 0, 0)
|
||||||
|
|
||||||
assert data.getLastHandle("editor") is None # Dropped by conversion
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
@@ -571,8 +574,8 @@ def testCoreProjectXML_ReadLegacy12(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
assert data.language == "en_GB"
|
assert data.language == "en_GB"
|
||||||
assert data.spellCheck is True
|
assert data.spellCheck is True
|
||||||
assert data.spellLang == "en_GB"
|
assert data.spellLang == "en_GB"
|
||||||
assert data.initCounts == (840, 376)
|
assert data.initCounts == (840, 376, 0, 0)
|
||||||
assert data.currCounts == (840, 376)
|
assert data.currCounts == (840, 376, 0, 0)
|
||||||
|
|
||||||
assert data.getLastHandle("editor") is None # Dropped by conversion
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
@@ -719,8 +722,8 @@ def testCoreProjectXML_ReadLegacy13(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
assert data.language == "en_GB"
|
assert data.language == "en_GB"
|
||||||
assert data.spellCheck is True
|
assert data.spellCheck is True
|
||||||
assert data.spellLang == "en_GB"
|
assert data.spellLang == "en_GB"
|
||||||
assert data.initCounts == (830, 376)
|
assert data.initCounts == (830, 376, 0, 0)
|
||||||
assert data.currCounts == (830, 376)
|
assert data.currCounts == (830, 376, 0, 0)
|
||||||
|
|
||||||
assert data.getLastHandle("editor") is None # Dropped by conversion
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
@@ -867,8 +870,8 @@ def testCoreProjectXML_ReadLegacy14(tstPaths, fncPath, mockGUI, mockRnd):
|
|||||||
assert data.language == "en_GB"
|
assert data.language == "en_GB"
|
||||||
assert data.spellCheck is True
|
assert data.spellCheck is True
|
||||||
assert data.spellLang == "en_GB"
|
assert data.spellLang == "en_GB"
|
||||||
assert data.initCounts == (954, 409)
|
assert data.initCounts == (954, 409, 0, 0)
|
||||||
assert data.currCounts == (954, 409)
|
assert data.currCounts == (954, 409, 0, 0)
|
||||||
|
|
||||||
assert data.getLastHandle("editor") is None # Dropped by conversion
|
assert data.getLastHandle("editor") is None # Dropped by conversion
|
||||||
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
assert data.getLastHandle("viewer") is None # Dropped by conversion
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ def testCoreSessions_Main(monkeypatch, mockGUI, fncPath):
|
|||||||
assert isinstance(logFile, Path)
|
assert isinstance(logFile, Path)
|
||||||
|
|
||||||
# Set some mock word counts
|
# Set some mock word counts
|
||||||
project.data.setInitCounts(50, 60)
|
project.data.setInitCounts(50, 60, 500, 600)
|
||||||
project.data.setCurrCounts(160, 150)
|
project.data.setCurrCounts(160, 150, 1600, 1500)
|
||||||
|
|
||||||
# The project init should already have created the session
|
# The project init should already have created the session
|
||||||
sessLog = project.session
|
sessLog = project.session
|
||||||
@@ -71,17 +71,19 @@ def testCoreSessions_Main(monkeypatch, mockGUI, fncPath):
|
|||||||
assert records[1]["type"] == "record"
|
assert records[1]["type"] == "record"
|
||||||
assert records[1]["novel"] == 160
|
assert records[1]["novel"] == 160
|
||||||
assert records[1]["notes"] == 150
|
assert records[1]["notes"] == 150
|
||||||
|
assert records[1]["cnovel"] == 1600
|
||||||
|
assert records[1]["cnotes"] == 1500
|
||||||
assert records[1]["idle"] == 1 # Should be rounded to full seconds
|
assert records[1]["idle"] == 1 # Should be rounded to full seconds
|
||||||
|
|
||||||
# Adding another record without changing word count should do nothing
|
# Adding another record without changing word count should do nothing
|
||||||
project.data.setInitCounts(160, 150)
|
project.data.setInitCounts(160, 150, 1600, 1500)
|
||||||
project.data.setCurrCounts(160, 150)
|
project.data.setCurrCounts(160, 150, 1600, 1500)
|
||||||
assert sessLog.appendSession(1.6) is False
|
assert sessLog.appendSession(1.6) is False
|
||||||
assert len(list(sessLog.iterRecords())) == 2
|
assert len(list(sessLog.iterRecords())) == 2
|
||||||
|
|
||||||
# But adding when count has changed should
|
# But adding when count has changed should
|
||||||
project.data.setInitCounts(160, 150)
|
project.data.setInitCounts(160, 150, 1600, 1500)
|
||||||
project.data.setCurrCounts(270, 240)
|
project.data.setCurrCounts(270, 240, 2700, 2400)
|
||||||
sessLog._start -= 350.0 # Backdate the session start to allow logging
|
sessLog._start -= 350.0 # Backdate the session start to allow logging
|
||||||
assert sessLog.appendSession(1.6) is True
|
assert sessLog.appendSession(1.6) is True
|
||||||
records = list(sessLog.iterRecords())
|
records = list(sessLog.iterRecords())
|
||||||
|
|||||||
@@ -435,8 +435,8 @@ def testCoreStorage_OldFormatConvert(monkeypatch, mockGUI, fncPath):
|
|||||||
sessLogOld.write_text((
|
sessLogOld.write_text((
|
||||||
"# Offset 150\n"
|
"# Offset 150\n"
|
||||||
"# Start Time End Time Novel Notes Idle\n"
|
"# Start Time End Time Novel Notes Idle\n"
|
||||||
"2021-02-02 02:02:02 2021-02-02 03:03:03 200 200 10\n"
|
"2021-02-02 02:02:02 2021-02-02 03:03:03 200 200 10\n"
|
||||||
"2021-03-03 03:03:03 2021-03-03 04:04:04 300 300 20\n"
|
"2021-03-03 03:03:03 2021-03-03 04:04:04 300 300 20\n"
|
||||||
), encoding="utf-8")
|
), encoding="utf-8")
|
||||||
|
|
||||||
assert sessLogOld.exists() is True
|
assert sessLogOld.exists() is True
|
||||||
@@ -496,6 +496,8 @@ def testCoreStorage_OldFormatConvert(monkeypatch, mockGUI, fncPath):
|
|||||||
"end": "2021-02-02 03:03:03",
|
"end": "2021-02-02 03:03:03",
|
||||||
"novel": 200,
|
"novel": 200,
|
||||||
"notes": 200,
|
"notes": 200,
|
||||||
|
"cnovel": 0,
|
||||||
|
"cnotes": 0,
|
||||||
"idle": 10,
|
"idle": 10,
|
||||||
}
|
}
|
||||||
assert data[2] == {
|
assert data[2] == {
|
||||||
@@ -504,6 +506,8 @@ def testCoreStorage_OldFormatConvert(monkeypatch, mockGUI, fncPath):
|
|||||||
"end": "2021-03-03 04:04:04",
|
"end": "2021-03-03 04:04:04",
|
||||||
"novel": 300,
|
"novel": 300,
|
||||||
"notes": 300,
|
"notes": 300,
|
||||||
|
"cnovel": 0,
|
||||||
|
"cnotes": 0,
|
||||||
"idle": 20,
|
"idle": 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ def testCoreTree_OtherMethods(qtbot, monkeypatch, mockGUI, fncPath, mockRnd):
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Refresh All
|
# Refresh All
|
||||||
assert tree.sumWords() == (9, 0)
|
assert tree.sumCounts() == (9, 0, 40, 0)
|
||||||
assert tree.model.root.count == 9
|
assert tree.model.root.count == 9
|
||||||
|
|
||||||
for node in tree.nodes.values():
|
for node in tree.nodes.values():
|
||||||
|
|||||||
@@ -163,14 +163,17 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
|
|||||||
mp.setattr(QFontDialog, "getFont", lambda *a, **k: (QFont(), True))
|
mp.setattr(QFontDialog, "getFont", lambda *a, **k: (QFont(), True))
|
||||||
prefs.nativeFont.setChecked(True) # Use OS font dialog
|
prefs.nativeFont.setChecked(True) # Use OS font dialog
|
||||||
prefs.guiFontButton.click()
|
prefs.guiFontButton.click()
|
||||||
|
|
||||||
prefs.hideVScroll.setChecked(True)
|
prefs.hideVScroll.setChecked(True)
|
||||||
prefs.hideHScroll.setChecked(True)
|
prefs.hideHScroll.setChecked(True)
|
||||||
|
prefs.useCharCount.setChecked(True)
|
||||||
|
|
||||||
assert CONFIG.guiLocale != "en_US"
|
assert CONFIG.guiLocale != "en_US"
|
||||||
assert CONFIG.guiTheme != "default_dark"
|
assert CONFIG.guiTheme != "default_dark"
|
||||||
assert CONFIG.guiFont.family() != ""
|
assert CONFIG.guiFont.family() != ""
|
||||||
assert CONFIG.hideVScroll is False
|
assert CONFIG.hideVScroll is False
|
||||||
assert CONFIG.hideHScroll is False
|
assert CONFIG.hideHScroll is False
|
||||||
|
assert CONFIG.useCharCount is False
|
||||||
|
|
||||||
# Document Style
|
# Document Style
|
||||||
prefs.guiSyntax.setCurrentIndex(prefs.guiSyntax.findData("default_dark"))
|
prefs.guiSyntax.setCurrentIndex(prefs.guiSyntax.findData("default_dark"))
|
||||||
@@ -178,6 +181,7 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
|
|||||||
mp.setattr(QFontDialog, "getFont", lambda *a, **k: (QFont(), True))
|
mp.setattr(QFontDialog, "getFont", lambda *a, **k: (QFont(), True))
|
||||||
prefs.nativeFont.setChecked(False) # Use Qt font dialog
|
prefs.nativeFont.setChecked(False) # Use Qt font dialog
|
||||||
prefs.textFontButton.click()
|
prefs.textFontButton.click()
|
||||||
|
|
||||||
prefs.showFullPath.setChecked(False)
|
prefs.showFullPath.setChecked(False)
|
||||||
prefs.incNotesWCount.setChecked(False)
|
prefs.incNotesWCount.setChecked(False)
|
||||||
|
|
||||||
@@ -344,6 +348,7 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
|
|||||||
assert CONFIG.guiFont == QFont()
|
assert CONFIG.guiFont == QFont()
|
||||||
assert CONFIG.hideVScroll is True
|
assert CONFIG.hideVScroll is True
|
||||||
assert CONFIG.hideHScroll is True
|
assert CONFIG.hideHScroll is True
|
||||||
|
assert CONFIG.useCharCount is True
|
||||||
|
|
||||||
# Document Style
|
# Document Style
|
||||||
assert CONFIG.guiSyntax == "default_dark"
|
assert CONFIG.guiSyntax == "default_dark"
|
||||||
|
|||||||
@@ -1957,7 +1957,7 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m
|
|||||||
assert docEditor.docFooter.wordsText.text() == "Words: 0 (+0)"
|
assert docEditor.docFooter.wordsText.text() == "Words: 0 (+0)"
|
||||||
|
|
||||||
# Open a document and populate it
|
# Open a document and populate it
|
||||||
SHARED.project.tree[C.hSceneDoc]._initCount = 0 # type: ignore
|
SHARED.project.tree[C.hSceneDoc]._wordInit = 0 # type: ignore
|
||||||
SHARED.project.tree[C.hSceneDoc]._wordCount = 0 # type: ignore
|
SHARED.project.tree[C.hSceneDoc]._wordCount = 0 # type: ignore
|
||||||
assert nwGUI.openDocument(C.hSceneDoc) is True
|
assert nwGUI.openDocument(C.hSceneDoc) is True
|
||||||
|
|
||||||
@@ -1981,7 +1981,6 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m
|
|||||||
assert threadPool.objectID() == id(docEditor._wCounterDoc)
|
assert threadPool.objectID() == id(docEditor._wCounterDoc)
|
||||||
|
|
||||||
docEditor._wCounterDoc.run()
|
docEditor._wCounterDoc.run()
|
||||||
# docEditor._updateDocCounts(cC, wC, pC)
|
|
||||||
assert SHARED.project.tree[C.hSceneDoc]._charCount == cC # type: ignore
|
assert SHARED.project.tree[C.hSceneDoc]._charCount == cC # type: ignore
|
||||||
assert SHARED.project.tree[C.hSceneDoc]._wordCount == wC # type: ignore
|
assert SHARED.project.tree[C.hSceneDoc]._wordCount == wC # type: ignore
|
||||||
assert SHARED.project.tree[C.hSceneDoc]._paraCount == pC # type: ignore
|
assert SHARED.project.tree[C.hSceneDoc]._paraCount == pC # type: ignore
|
||||||
@@ -1993,7 +1992,7 @@ def testGuiEditor_WordCounters(qtbot, monkeypatch, nwGUI, projPath, ipsumText, m
|
|||||||
assert threadPool.objectID() == id(docEditor._wCounterSel)
|
assert threadPool.objectID() == id(docEditor._wCounterSel)
|
||||||
|
|
||||||
docEditor._wCounterSel.run()
|
docEditor._wCounterSel.run()
|
||||||
assert docEditor.docFooter.wordsText.text() == f"Words: {wC} selected"
|
assert docEditor.docFooter.wordsText.text() == f"Selected: {wC}"
|
||||||
|
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
|
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ def testGuiMain_UpdateTheme(qtbot, nwGUI):
|
|||||||
CONFIG.guiSyntax = "default_dark"
|
CONFIG.guiSyntax = "default_dark"
|
||||||
mainTheme.loadTheme()
|
mainTheme.loadTheme()
|
||||||
mainTheme.loadSyntax()
|
mainTheme.loadSyntax()
|
||||||
|
nwGUI._processConfigChanges(False, True, False, False)
|
||||||
nwGUI._processConfigChanges(True, True, True, True)
|
nwGUI._processConfigChanges(True, True, True, True)
|
||||||
|
|
||||||
syntax = SHARED.theme.syntaxTheme
|
syntax = SHARED.theme.syntaxTheme
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ def testGuiNovelView_Content(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
|||||||
assert novelTree._getModel() is None
|
assert novelTree._getModel() is None
|
||||||
|
|
||||||
# Reload
|
# Reload
|
||||||
novelBar._forceRefreshNovelTree()
|
novelView.refreshCurrentTree()
|
||||||
model = novelTree._getModel()
|
model = novelTree._getModel()
|
||||||
assert isinstance(model, NovelModel)
|
assert isinstance(model, NovelModel)
|
||||||
|
|
||||||
|
|||||||
@@ -109,4 +109,18 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
|||||||
nwGUI._timeTick()
|
nwGUI._timeTick()
|
||||||
assert status.statsText.text() == "Words: 11 (+11)"
|
assert status.statsText.text() == "Words: 11 (+11)"
|
||||||
|
|
||||||
|
# Switch to character count
|
||||||
|
CONFIG.useCharCount = True
|
||||||
|
status.initSettings()
|
||||||
|
with monkeypatch.context() as mp:
|
||||||
|
mp.setattr("novelwriter.guimain.time", lambda *a: 50.0)
|
||||||
|
CONFIG.incNotesWCount = True
|
||||||
|
nwGUI._lastTotalCount = 0
|
||||||
|
nwGUI._timeTick()
|
||||||
|
assert status.statsText.text() == "Characters: 46 (+46)"
|
||||||
|
CONFIG.incNotesWCount = False
|
||||||
|
nwGUI._lastTotalCount = 0
|
||||||
|
nwGUI._timeTick()
|
||||||
|
assert status.statsText.text() == "Characters: 40 (+40)"
|
||||||
|
|
||||||
# qtbot.stop()
|
# qtbot.stop()
|
||||||
|
|||||||
Reference in New Issue
Block a user