@@ -24,7 +24,8 @@ Tags in Notes
|
|||||||
Each new heading in a note can have a tag associated with it. The format of a tag is
|
Each new heading in a note can have a tag associated with it. The format of a tag is
|
||||||
``@tag: tagname``, where tagname is a unique identifier. Tags can then be referenced in the novel
|
``@tag: tagname``, where tagname is a unique identifier. Tags can then be referenced in the novel
|
||||||
documents, or cross-referenced in other notes, and will show up in the outline view and in the
|
documents, or cross-referenced in other notes, and will show up in the outline view and in the
|
||||||
back-reference panel when a document is being viewed.
|
back-reference panel when a document is being viewed. See :ref:`a_struct_tags` for how to reference
|
||||||
|
notes.
|
||||||
|
|
||||||
The syntax highlighter will alert the user that the keyword is correctly used and that the tag is
|
The syntax highlighter will alert the user that the keyword is correctly used and that the tag is
|
||||||
allowed, that is, the tag is unique. Duplicate tags should be detected as long as the index is up
|
allowed, that is, the tag is unique. Duplicate tags should be detected as long as the index is up
|
||||||
|
|||||||
@@ -86,10 +86,11 @@ more details.
|
|||||||
Tag References
|
Tag References
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Each text partition indicated by a heading of any level, can contain references to tags set in the
|
Each text partition, indicated by a heading of any level, can contain references to tags set in the
|
||||||
supporting notes of the project. The references are gathered by the indexer and used to generate
|
supporting notes of the project. The references are gathered by the indexer and used to generate an
|
||||||
an outline view on the :guilabel:`Outline` tab of how the different parts of the novel are
|
outline view on the :guilabel:`Outline` tab of how the different parts of the novel are connected.
|
||||||
connected.
|
This section covers how to set references to tags. See :ref:`a_notes_tags` for how to define tags
|
||||||
|
the references can point to.
|
||||||
|
|
||||||
References and tags are also clickable in the document editor and viewer, making it easy to
|
References and tags are also clickable in the document editor and viewer, making it easy to
|
||||||
navigate between reference notes while writing. Clicked links are always opened in the view panel.
|
navigate between reference notes while writing. Clicked links are always opened in the view panel.
|
||||||
|
|||||||
@@ -210,7 +210,6 @@ class nwQuotes():
|
|||||||
"""Allowed quotation marks.
|
"""Allowed quotation marks.
|
||||||
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SYMBOLS = {
|
SYMBOLS = {
|
||||||
"\u0027" : "Straight single quotation mark",
|
"\u0027" : "Straight single quotation mark",
|
||||||
"\u0022" : "Straight double quotation mark",
|
"\u0022" : "Straight double quotation mark",
|
||||||
@@ -241,7 +240,6 @@ class nwQuotes():
|
|||||||
class nwUnicode:
|
class nwUnicode:
|
||||||
"""Supported unicode character constants and their HTML equivalents.
|
"""Supported unicode character constants and their HTML equivalents.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Unicode Constants
|
# Unicode Constants
|
||||||
# =================
|
# =================
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -416,9 +416,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
|
|
||||||
self.theIndex.scanText(tHandle, docText)
|
self.theIndex.scanText(tHandle, docText)
|
||||||
if self._updateHeaders(checkLevel=True):
|
if self._updateHeaders(checkLevel=True):
|
||||||
if self.theParent.projTabs.currentIndex() == self.theParent.idxNovelView:
|
self.theParent.requestNovelTreeRefresh()
|
||||||
logger.verbose("Document headers have changed, updating novel tree")
|
|
||||||
self.theParent.novelView.refreshTree()
|
|
||||||
else:
|
else:
|
||||||
self.theParent.novelView.updateWordCounts(tHandle)
|
self.theParent.novelView.updateWordCounts(tHandle)
|
||||||
|
|
||||||
@@ -1990,6 +1988,7 @@ class GuiDocEditSearch(QFrame):
|
|||||||
if theText is not None:
|
if theText is not None:
|
||||||
self.searchBox.setText(theText)
|
self.searchBox.setText(theText)
|
||||||
self.searchBox.setFocus()
|
self.searchBox.setFocus()
|
||||||
|
self.searchBox.selectAll()
|
||||||
if self.isRegEx:
|
if self.isRegEx:
|
||||||
self._alertSearchValid(True)
|
self._alertSearchValid(True)
|
||||||
logger.verbose("Setting search text to '%s'" % theText)
|
logger.verbose("Setting search text to '%s'" % theText)
|
||||||
|
|||||||
+41
-38
@@ -227,55 +227,58 @@ class GuiItemDetails(QWidget):
|
|||||||
def updateViewBox(self, tHandle):
|
def updateViewBox(self, tHandle):
|
||||||
"""Populate the details box from a given handle.
|
"""Populate the details box from a given handle.
|
||||||
"""
|
"""
|
||||||
self.theHandle = tHandle
|
if tHandle is None:
|
||||||
nwItem = self.theProject.projTree[tHandle]
|
self.clearDetails()
|
||||||
|
return
|
||||||
|
|
||||||
|
nwItem = self.theProject.projTree[tHandle]
|
||||||
if nwItem is None:
|
if nwItem is None:
|
||||||
self.clearDetails()
|
self.clearDetails()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.theHandle = tHandle
|
||||||
|
theLabel = nwItem.itemName
|
||||||
|
if len(theLabel) > 100:
|
||||||
|
theLabel = theLabel[:96].rstrip()+" ..."
|
||||||
|
|
||||||
|
itStatus = nwItem.itemStatus
|
||||||
|
if nwItem.itemClass == nwItemClass.NOVEL:
|
||||||
|
itStatus = self.theProject.statusItems.checkEntry(itStatus) # Make sure it's valid
|
||||||
|
flagIcon = self.theParent.statusIcons[itStatus]
|
||||||
else:
|
else:
|
||||||
theLabel = nwItem.itemName
|
itStatus = self.theProject.importItems.checkEntry(itStatus) # Make sure it's valid
|
||||||
if len(theLabel) > 100:
|
flagIcon = self.theParent.importIcons[itStatus]
|
||||||
theLabel = theLabel[:96].rstrip()+" ..."
|
|
||||||
|
|
||||||
itStatus = nwItem.itemStatus
|
if nwItem.itemType == nwItemType.FILE:
|
||||||
if nwItem.itemClass == nwItemClass.NOVEL:
|
if nwItem.isExported:
|
||||||
itStatus = self.theProject.statusItems.checkEntry(itStatus) # Make sure it's valid
|
self.labelFlag.setPixmap(self.expCheck)
|
||||||
flagIcon = self.theParent.statusIcons[itStatus]
|
|
||||||
else:
|
else:
|
||||||
itStatus = self.theProject.importItems.checkEntry(itStatus) # Make sure it's valid
|
self.labelFlag.setPixmap(self.expCross)
|
||||||
flagIcon = self.theParent.importIcons[itStatus]
|
else:
|
||||||
|
self.labelFlag.setPixmap(QPixmap(1, 1))
|
||||||
|
|
||||||
if nwItem.itemType == nwItemType.FILE:
|
iPx = int(round(0.8*self.theTheme.baseIconSize))
|
||||||
if nwItem.isExported:
|
self.statusFlag.setPixmap(flagIcon.pixmap(iPx, iPx))
|
||||||
self.labelFlag.setPixmap(self.expCheck)
|
self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass])
|
||||||
else:
|
|
||||||
self.labelFlag.setPixmap(self.expCross)
|
|
||||||
else:
|
|
||||||
self.labelFlag.setPixmap(QPixmap(1, 1))
|
|
||||||
|
|
||||||
iPx = int(round(0.8*self.theTheme.baseIconSize))
|
if nwItem.itemLayout == nwItemLayout.NO_LAYOUT:
|
||||||
self.statusFlag.setPixmap(flagIcon.pixmap(iPx, iPx))
|
self.layoutFlag.setText("-")
|
||||||
self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass])
|
else:
|
||||||
|
self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout])
|
||||||
|
|
||||||
if nwItem.itemLayout == nwItemLayout.NO_LAYOUT:
|
self.labelData.setText(theLabel)
|
||||||
self.layoutFlag.setText("-")
|
self.statusData.setText(nwItem.itemStatus)
|
||||||
else:
|
self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass])
|
||||||
self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout])
|
self.layoutData.setText(nwLabels.LAYOUT_NAME[nwItem.itemLayout])
|
||||||
|
|
||||||
self.labelData.setText(theLabel)
|
if nwItem.itemType == nwItemType.FILE:
|
||||||
self.statusData.setText(nwItem.itemStatus)
|
self.cCountData.setText(f"{nwItem.charCount:n}")
|
||||||
self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass])
|
self.wCountData.setText(f"{nwItem.wordCount:n}")
|
||||||
self.layoutData.setText(nwLabels.LAYOUT_NAME[nwItem.itemLayout])
|
self.pCountData.setText(f"{nwItem.paraCount:n}")
|
||||||
|
else:
|
||||||
if nwItem.itemType == nwItemType.FILE:
|
self.cCountData.setText("–")
|
||||||
self.cCountData.setText(f"{nwItem.charCount:n}")
|
self.wCountData.setText("–")
|
||||||
self.wCountData.setText(f"{nwItem.wordCount:n}")
|
self.pCountData.setText("–")
|
||||||
self.pCountData.setText(f"{nwItem.paraCount:n}")
|
|
||||||
else:
|
|
||||||
self.cCountData.setText("–")
|
|
||||||
self.wCountData.setText("–")
|
|
||||||
self.pCountData.setText("–")
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -131,10 +131,11 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
def refreshTree(self, overRide=False):
|
def refreshTree(self, overRide=False):
|
||||||
"""Called whenever the Novel tab is activated.
|
"""Called whenever the Novel tab is activated.
|
||||||
"""
|
"""
|
||||||
|
logger.verbose("Requesting refresh of the novel tree")
|
||||||
treeChanged = self.theParent.treeView.changedSince(self._lastBuild)
|
treeChanged = self.theParent.treeView.changedSince(self._lastBuild)
|
||||||
indexChanged = self.theIndex.novelChangedSince(self._lastBuild)
|
indexChanged = self.theIndex.novelChangedSince(self._lastBuild)
|
||||||
if not (treeChanged or indexChanged):
|
if not (treeChanged or indexChanged):
|
||||||
logger.verbose("No changes made to the novel")
|
logger.verbose("No changes have been made to the novel index")
|
||||||
return
|
return
|
||||||
|
|
||||||
selItem = self.selectedItems()
|
selItem = self.selectedItems()
|
||||||
|
|||||||
+10
-6
@@ -129,6 +129,9 @@ class GuiProjectLoad(QDialog):
|
|||||||
self.newButton = self.buttonBox.addButton("New", QDialogButtonBox.ActionRole)
|
self.newButton = self.buttonBox.addButton("New", QDialogButtonBox.ActionRole)
|
||||||
self.newButton.clicked.connect(self._doNewProject)
|
self.newButton.clicked.connect(self._doNewProject)
|
||||||
|
|
||||||
|
self.delButton = self.buttonBox.addButton("Remove", QDialogButtonBox.ActionRole)
|
||||||
|
self.delButton.clicked.connect(self._doDeleteRecent)
|
||||||
|
|
||||||
self.outerBox.addLayout(self.innerBox)
|
self.outerBox.addLayout(self.innerBox)
|
||||||
self.outerBox.addWidget(self.buttonBox)
|
self.outerBox.addWidget(self.buttonBox)
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
@@ -138,7 +141,7 @@ class GuiProjectLoad(QDialog):
|
|||||||
|
|
||||||
keyDelete = QShortcut(self.listBox)
|
keyDelete = QShortcut(self.listBox)
|
||||||
keyDelete.setKey(QKeySequence(Qt.Key_Delete))
|
keyDelete.setKey(QKeySequence(Qt.Key_Delete))
|
||||||
keyDelete.activated.connect(self._keyPressDelete)
|
keyDelete.activated.connect(self._doDeleteRecent)
|
||||||
|
|
||||||
logger.debug("GuiProjectLoad initialisation complete")
|
logger.debug("GuiProjectLoad initialisation complete")
|
||||||
|
|
||||||
@@ -212,15 +215,16 @@ class GuiProjectLoad(QDialog):
|
|||||||
self.accept()
|
self.accept()
|
||||||
return
|
return
|
||||||
|
|
||||||
def _keyPressDelete(self):
|
def _doDeleteRecent(self):
|
||||||
"""Remove an entry from the recent projects list.
|
"""Remove an entry from the recent projects list.
|
||||||
"""
|
"""
|
||||||
selList = self.listBox.selectedItems()
|
selList = self.listBox.selectedItems()
|
||||||
if selList:
|
if selList:
|
||||||
msgYes = self.theParent.askQuestion(
|
projName = selList[0].text(self.C_NAME)
|
||||||
"Remove Entry",
|
msgYes = self.theParent.askQuestion("Remove Entry", (
|
||||||
"Remove the selected entry from the recent projects list?"
|
"Remove '%s' from the recent projects list? "
|
||||||
)
|
"The project files will not be deleted."
|
||||||
|
) % projName)
|
||||||
if msgYes:
|
if msgYes:
|
||||||
self.mainConf.removeFromRecentCache(
|
self.mainConf.removeFromRecentCache(
|
||||||
selList[0].data(self.C_NAME, Qt.UserRole)
|
selList[0].data(self.C_NAME, Qt.UserRole)
|
||||||
|
|||||||
@@ -817,6 +817,14 @@ class GuiMain(QMainWindow):
|
|||||||
self.novelView.refreshTree()
|
self.novelView.refreshTree()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def requestNovelTreeRefresh(self):
|
||||||
|
"""Update the novel tree, but only if it is visible.
|
||||||
|
"""
|
||||||
|
if self.projTabs.currentIndex() == self.idxNovelView and self.hasProject:
|
||||||
|
self.novelView.refreshTree()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def rebuildIndex(self, beQuiet=False):
|
def rebuildIndex(self, beQuiet=False):
|
||||||
"""Rebuild the entire index.
|
"""Rebuild the entire index.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ def testGuiLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
|
|
||||||
qtbot.wait(stepDelay)
|
qtbot.wait(stepDelay)
|
||||||
nwLoad.show()
|
nwLoad.show()
|
||||||
nwLoad._keyPressDelete()
|
nwLoad._doDeleteRecent()
|
||||||
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
|
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
|
||||||
|
|
||||||
getFile = os.path.join(nwMinimal, "nwProject.nwx")
|
getFile = os.path.join(nwMinimal, "nwProject.nwx")
|
||||||
|
|||||||
Reference in New Issue
Block a user