Allow switching between root novel folders

This commit is contained in:
Veronica Berglyd Olsen
2022-06-05 15:04:04 +02:00
parent de07546e3f
commit 2873336dce
7 changed files with 88 additions and 56 deletions
+18 -30
View File
@@ -73,9 +73,8 @@ class NWIndex:
self._indexBroken = False
# TimeStamps
self._timeNovel = 0
self._timeNotes = 0
self._timeIndex = 0
self._indexChange = 0
self._rootChange = {}
return
@@ -99,9 +98,8 @@ class NWIndex:
"""
self._tagsIndex.clear()
self._itemIndex.clear()
self._timeNovel = 0
self._timeNotes = 0
self._timeIndex = 0
self._indexChange = 0
self._rootChange = {}
return
def deleteHandle(self, tHandle):
@@ -129,20 +127,16 @@ class NWIndex:
return True
def novelChangedSince(self, checkTime):
"""Check if the novel index has changed since a given time.
"""
return self._timeNovel > checkTime
def notesChangedSince(self, checkTime):
"""Check if the notes index has changed since a given time.
"""
return self._timeNotes > checkTime
def indexChangedSince(self, checkTime):
"""Check if the index has changed since a given time.
"""
return self._timeIndex > checkTime
return self._indexChange > checkTime
def rootChangedSince(self, rootHandle, checkTime):
"""Check if the index has changed since a given time for a
given root item.
"""
return self._rootChange.get(rootHandle, self._indexChange) > checkTime
##
# Load and Save Index to/from File
@@ -184,10 +178,7 @@ class NWIndex:
logger.warning("Item '%s' is not in the index", fHandle)
self.reIndexHandle(fHandle)
nowTime = round(time())
self._timeNovel = nowTime
self._timeNotes = nowTime
self._timeIndex = nowTime
self._indexChange = round(time())
logger.verbose("Index loaded in %.3f ms", (time() - tStart)*1000)
@@ -307,11 +298,8 @@ class NWIndex:
# Update timestamps for index changes
nowTime = round(time())
self._timeIndex = nowTime
if theItem.itemLayout == nwItemLayout.NOTE:
self._timeNotes = nowTime
else:
self._timeNovel = nowTime
self._indexChange = nowTime
self._rootChange[theItem.itemRoot] = nowTime
return True
@@ -466,14 +454,14 @@ class NWIndex:
# Extract Data
##
def novelStructure(self, skipExcl=True):
def novelStructure(self, rootHandle=None, skipExcl=True):
"""Iterate over all titles in the novel, in the correct order as
they appear in the tree view and in the respective document
files, but skipping all note files.
"""
for tHandle, sTitle, hItem in self._itemIndex.iterNovelStructure(skipExcl=skipExcl):
tKey = f"{tHandle}:{sTitle}"
yield tKey, tHandle, sTitle, hItem
novStruct = self._itemIndex.iterNovelStructure(rootHandle=rootHandle, skipExcl=skipExcl)
for tHandle, sTitle, hItem in novStruct:
yield f"{tHandle}:{sTitle}", tHandle, sTitle, hItem
return
def getNovelWordCount(self, skipExcl=True):
+1 -1
View File
@@ -136,7 +136,7 @@ class GuiNovelTree(QTreeWidget):
"""
logger.verbose("Requesting refresh of the novel tree")
treeChanged = self.theParent.treeView.changedSince(self._lastBuild)
indexChanged = self.theProject.index.novelChangedSince(self._lastBuild)
indexChanged = self.theProject.index.indexChangedSince(self._lastBuild)
if not (treeChanged or indexChanged or overRide):
logger.verbose("No changes have been made to the novel index")
return
+15 -6
View File
@@ -83,6 +83,7 @@ class GuiOutline(QWidget):
self.outlineView.hiddenStateChanged.connect(self._updateMenuColumns)
self.outlineView.activeItemChanged.connect(self.outlineData.showItem)
self.outlineData.itemTagClicked.connect(self._tagClicked)
self.outlineBar.novelRootChanged.connect(self._rootItemChanged)
self.outlineBar.viewColumnToggled.connect(self.outlineView.menuColumnToggled)
self.outlineBar.viewRefreshRequested.connect(
lambda: self.outlineView.refreshTree(overRide=True)
@@ -153,6 +154,13 @@ class GuiOutline(QWidget):
self.loadDocumentTagRequest.emit(link, nwDocMode.VIEW)
return
@pyqtSlot(str)
def _rootItemChanged(self, handle):
"""The root novel handle has been changed.
"""
self.outlineView.refreshTree(rootHandle=handle, overRide=True)
return
# END Class GuiOutline
@@ -394,7 +402,7 @@ class GuiOutlineView(QTreeWidget):
return
def refreshTree(self, overRide=False, novelChanged=False):
def refreshTree(self, rootHandle=None, overRide=False, novelChanged=False):
"""Called whenever the Outline tab is activated and controls
what data to load, and if necessary, force a rebuild of the
tree.
@@ -402,17 +410,17 @@ class GuiOutlineView(QTreeWidget):
# If it's the first time, we always build
if self._firstView or self._firstView and overRide:
self._loadHeaderState()
self._populateTree()
self._populateTree(rootHandle)
self._firstView = False
return
# If the novel index or novel tree has changed since the tree
# was last built, we rebuild the tree from the updated index.
indexChanged = self.theProject.index.novelChangedSince(self._lastBuild)
indexChanged = self.theProject.index.rootChangedSince(rootHandle, self._lastBuild)
doBuild = (novelChanged or indexChanged) and self.theProject.autoOutline
if doBuild or overRide:
logger.debug("Rebuilding Project Outline")
self._populateTree()
self._populateTree(rootHandle)
return
@@ -577,7 +585,7 @@ class GuiOutlineView(QTreeWidget):
return
def _populateTree(self):
def _populateTree(self, rootHandle):
"""Build the tree based on the project index, and the header
based on the defined constants, default values and user selected
width, order and hidden state. All columns are populated, even
@@ -610,7 +618,8 @@ class GuiOutlineView(QTreeWidget):
currChapter = None
currScene = None
for _, tHandle, sTitle, novIdx in self.theProject.index.novelStructure(skipExcl=True):
novStruct = self.theProject.index.novelStructure(rootHandle=rootHandle, skipExcl=True)
for _, tHandle, sTitle, novIdx in novStruct:
tItem = self._createTreeItem(tHandle, sTitle, novIdx)
+17
View File
@@ -0,0 +1,17 @@
%%~name: Chapter One
%%~path: e5e47ebf63b1c/a520879ca0b45
%%~kind: NOVEL/DOCUMENT
### Chapter One
@pov: Jane
% Synopsis: Remember Jane and John?
### Scene One
@pov: Jane
@focus: John
A project can have multiple novel root folders for multiple novels. This is the first scene of a sequel to the first novel.
In this way, the writer can keep the same notes for multiple novels. This can be especially useful if the writer is planning a multi-novel story in advance.
+8
View File
@@ -0,0 +1,8 @@
%%~name: Title Page
%%~path: e5e47ebf63b1c/bacb7059e3083
%%~kind: NOVEL/DOCUMENT
#! Sequel Novel
>> **By Jane Doh** <<
% Synopsis: Jane and John are back in a sequel to My Novel!
+27 -15
View File
@@ -1,13 +1,13 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-05 13:28:34">
<novelWriterXML appVersion="1.7-beta1" hexVersion="0x010700b1" fileVersion="1.4" timeStamp="2022-06-05 15:03:00">
<project>
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
<author>Jay Doh</author>
<saveCount>1331</saveCount>
<autoCount>220</autoCount>
<editTime>67108</editTime>
<saveCount>1334</saveCount>
<autoCount>225</autoCount>
<editTime>67746</editTime>
</project>
<settings>
<doBackup>False</doBackup>
@@ -15,10 +15,10 @@
<spellCheck>True</spellCheck>
<spellLang>None</spellLang>
<autoOutline>True</autoOutline>
<lastEdited>636b6aa9b697b</lastEdited>
<lastEdited>a520879ca0b45</lastEdited>
<lastViewed>636b6aa9b697b</lastViewed>
<lastWordCount>1303</lastWordCount>
<novelWordCount>894</novelWordCount>
<lastWordCount>1363</lastWordCount>
<novelWordCount>954</novelWordCount>
<notesWordCount>409</notesWordCount>
<autoReplace>
<entry key="A">B</entry>
@@ -33,10 +33,10 @@
<section></section>
</titleFormat>
<status>
<entry key="sf12341" count="5" red="100" green="100" blue="100">New</entry>
<entry key="sf12341" count="7" red="100" green="100" blue="100">New</entry>
<entry key="sf24ce6" count="1" red="200" green="50" blue="0">Notes</entry>
<entry key="sc24b8f" count="2" red="182" green="60" blue="0">Started</entry>
<entry key="s90e6c9" count="5" red="193" green="129" blue="0">1st Draft</entry>
<entry key="s90e6c9" count="6" red="193" green="129" blue="0">1st Draft</entry>
<entry key="sd51c5b" count="1" red="193" green="129" blue="0">2nd Draft</entry>
<entry key="s8ae72a" count="0" red="193" green="129" blue="0">3rd Draft</entry>
<entry key="s78ea90" count="0" red="58" green="180" blue="58">Finished</entry>
@@ -48,13 +48,13 @@
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
</importance>
</settings>
<content count="24">
<content count="27">
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
<meta expanded="True"/>
<name status="sc24b8f" import="ia857f0">Novel</name>
</item>
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="93" wordCount="19" paraCount="2" cursorPos="2"/>
<meta expanded="False" charCount="93" wordCount="19" paraCount="2" cursorPos="119"/>
<name status="sc24b8f" import="ia857f0" exported="True">Title Page</name>
</item>
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
@@ -93,7 +93,19 @@
<meta expanded="False" charCount="189" wordCount="37" paraCount="1" cursorPos="224"/>
<name status="s90e6c9" import="ia857f0" exported="True">We Found John!</name>
</item>
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="1" type="ROOT" class="CHARACTER">
<item handle="e5e47ebf63b1c" parent="None" root="e5e47ebf63b1c" order="1" type="ROOT" class="NOVEL">
<meta expanded="True"/>
<name status="sf12341" import="ia857f0">Sequel</name>
</item>
<item handle="bacb7059e3083" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="27" wordCount="5" paraCount="1" cursorPos="100"/>
<name status="sf12341" import="ia857f0" exported="True">Title Page</name>
</item>
<item handle="a520879ca0b45" parent="e5e47ebf63b1c" root="e5e47ebf63b1c" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta expanded="False" charCount="299" wordCount="55" paraCount="2" cursorPos="104"/>
<name status="s90e6c9" import="ia857f0" exported="True">Chapter One</name>
</item>
<item handle="f6622b4617424" parent="None" root="f6622b4617424" order="2" type="ROOT" class="CHARACTER">
<meta expanded="True"/>
<name status="sf12341" import="ia857f0">Characters</name>
</item>
@@ -109,7 +121,7 @@
<meta expanded="False" charCount="55" wordCount="9" paraCount="1" cursorPos="25"/>
<name status="sf12341" import="i2d7a54" exported="True">Jane Smith</name>
</item>
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="2" type="ROOT" class="WORLD">
<item handle="15c4492bd5107" parent="None" root="15c4492bd5107" order="3" type="ROOT" class="WORLD">
<meta expanded="True"/>
<name status="sf12341" import="ia857f0">Locations</name>
</item>
@@ -125,7 +137,7 @@
<meta expanded="False" charCount="28" wordCount="6" paraCount="1" cursorPos="45"/>
<name status="sf12341" import="i2d7a54" exported="True">Mars</name>
</item>
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="3" type="ROOT" class="ARCHIVE">
<item handle="6827118336ac1" parent="None" root="6827118336ac1" order="4" type="ROOT" class="ARCHIVE">
<meta expanded="True"/>
<name status="sf12341" import="ia857f0">Archive</name>
</item>
@@ -137,7 +149,7 @@
<meta expanded="False" charCount="232" wordCount="42" paraCount="1" cursorPos="239"/>
<name status="s90e6c9" import="ia857f0" exported="True">Old File</name>
</item>
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="4" type="ROOT" class="TRASH">
<item handle="98acd8c76c93a" parent="None" root="98acd8c76c93a" order="5" type="ROOT" class="TRASH">
<meta expanded="True"/>
<name status="sf12341" import="ia857f0">Trash</name>
</item>
+2 -4
View File
@@ -197,8 +197,7 @@ def testCoreIndex_CheckThese(mockGUI, fncDir, mockRnd):
nItem = theProject.tree[nHandle]
cItem = theProject.tree[cHandle]
assert theIndex.novelChangedSince(0) is False
assert theIndex.notesChangedSince(0) is False
assert theIndex.rootChangedSince("0000000000010", 0) is False
assert theIndex.indexChangedSince(0) is False
assert theIndex.scanText(cHandle, (
@@ -228,8 +227,7 @@ def testCoreIndex_CheckThese(mockGUI, fncDir, mockRnd):
"@time": []
}
assert theIndex.novelChangedSince(0) is True
assert theIndex.notesChangedSince(0) is True
assert theIndex.rootChangedSince("0000000000010", 0) is True
assert theIndex.indexChangedSince(0) is True
assert theIndex.getHandleHeaderLevel(cHandle) == "H1"