From 56541702787b4d4379f93e1216956d07dce0273a Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 2 Feb 2021 16:36:19 +0100 Subject: [PATCH 1/6] Let the main gui handle the novel tree refresh decision --- nw/gui/doceditor.py | 4 +--- nw/gui/noveltree.py | 3 ++- nw/guimain.py | 8 ++++++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 91a4b255..fb2a07e6 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -416,9 +416,7 @@ class GuiDocEditor(QTextEdit): self.theIndex.scanText(tHandle, docText) if self._updateHeaders(checkLevel=True): - if self.theParent.projTabs.currentIndex() == self.theParent.idxNovelView: - logger.verbose("Document headers have changed, updating novel tree") - self.theParent.novelView.refreshTree() + self.theParent.requestNovelTreeRefresh() else: self.theParent.novelView.updateWordCounts(tHandle) diff --git a/nw/gui/noveltree.py b/nw/gui/noveltree.py index 7a7990f8..0ceaf9ec 100644 --- a/nw/gui/noveltree.py +++ b/nw/gui/noveltree.py @@ -131,10 +131,11 @@ class GuiNovelTree(QTreeWidget): def refreshTree(self, overRide=False): """Called whenever the Novel tab is activated. """ + logger.verbose("Requesting refresh of the novel tree") treeChanged = self.theParent.treeView.changedSince(self._lastBuild) indexChanged = self.theIndex.novelChangedSince(self._lastBuild) 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 selItem = self.selectedItems() diff --git a/nw/guimain.py b/nw/guimain.py index f6538bf0..3500f87a 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -817,6 +817,14 @@ class GuiMain(QMainWindow): self.novelView.refreshTree() 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): """Rebuild the entire index. """ From 6949481198913dbad8a4a9822e9552a2c7608aac Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 2 Feb 2021 16:36:45 +0100 Subject: [PATCH 2/6] Stop lookup error on none in project tree meta panel --- nw/gui/itemdetails.py | 79 ++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/nw/gui/itemdetails.py b/nw/gui/itemdetails.py index a5415939..52584d40 100644 --- a/nw/gui/itemdetails.py +++ b/nw/gui/itemdetails.py @@ -227,55 +227,58 @@ class GuiItemDetails(QWidget): def updateViewBox(self, tHandle): """Populate the details box from a given handle. """ - self.theHandle = tHandle - nwItem = self.theProject.projTree[tHandle] + if tHandle is None: + self.clearDetails() + return + nwItem = self.theProject.projTree[tHandle] if nwItem is None: 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: - theLabel = nwItem.itemName - if len(theLabel) > 100: - theLabel = theLabel[:96].rstrip()+" ..." + itStatus = self.theProject.importItems.checkEntry(itStatus) # Make sure it's valid + flagIcon = self.theParent.importIcons[itStatus] - itStatus = nwItem.itemStatus - if nwItem.itemClass == nwItemClass.NOVEL: - itStatus = self.theProject.statusItems.checkEntry(itStatus) # Make sure it's valid - flagIcon = self.theParent.statusIcons[itStatus] + if nwItem.itemType == nwItemType.FILE: + if nwItem.isExported: + self.labelFlag.setPixmap(self.expCheck) else: - itStatus = self.theProject.importItems.checkEntry(itStatus) # Make sure it's valid - flagIcon = self.theParent.importIcons[itStatus] + self.labelFlag.setPixmap(self.expCross) + else: + self.labelFlag.setPixmap(QPixmap(1, 1)) - if nwItem.itemType == nwItemType.FILE: - if nwItem.isExported: - self.labelFlag.setPixmap(self.expCheck) - else: - self.labelFlag.setPixmap(self.expCross) - else: - self.labelFlag.setPixmap(QPixmap(1, 1)) + iPx = int(round(0.8*self.theTheme.baseIconSize)) + self.statusFlag.setPixmap(flagIcon.pixmap(iPx, iPx)) + self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass]) - iPx = int(round(0.8*self.theTheme.baseIconSize)) - self.statusFlag.setPixmap(flagIcon.pixmap(iPx, iPx)) - self.classFlag.setText(nwLabels.CLASS_FLAG[nwItem.itemClass]) + if nwItem.itemLayout == nwItemLayout.NO_LAYOUT: + self.layoutFlag.setText("-") + else: + self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout]) - if nwItem.itemLayout == nwItemLayout.NO_LAYOUT: - self.layoutFlag.setText("-") - else: - self.layoutFlag.setText(nwLabels.LAYOUT_FLAG[nwItem.itemLayout]) + self.labelData.setText(theLabel) + self.statusData.setText(nwItem.itemStatus) + self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass]) + self.layoutData.setText(nwLabels.LAYOUT_NAME[nwItem.itemLayout]) - self.labelData.setText(theLabel) - self.statusData.setText(nwItem.itemStatus) - self.classData.setText(nwLabels.CLASS_NAME[nwItem.itemClass]) - self.layoutData.setText(nwLabels.LAYOUT_NAME[nwItem.itemLayout]) - - if nwItem.itemType == nwItemType.FILE: - self.cCountData.setText(f"{nwItem.charCount:n}") - self.wCountData.setText(f"{nwItem.wordCount:n}") - self.pCountData.setText(f"{nwItem.paraCount:n}") - else: - self.cCountData.setText("–") - self.wCountData.setText("–") - self.pCountData.setText("–") + if nwItem.itemType == nwItemType.FILE: + self.cCountData.setText(f"{nwItem.charCount:n}") + self.wCountData.setText(f"{nwItem.wordCount:n}") + self.pCountData.setText(f"{nwItem.paraCount:n}") + else: + self.cCountData.setText("–") + self.wCountData.setText("–") + self.pCountData.setText("–") return From 4562a0130a5fc3dfb636cc9ef5485df37e2e838b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 2 Feb 2021 22:06:02 +0100 Subject: [PATCH 3/6] Add a button to the Project Open to remove listed projects, and clarify the dialog message --- nw/gui/projload.py | 16 ++++++++++------ tests/test_gui/test_gui_projload.py | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/nw/gui/projload.py b/nw/gui/projload.py index d8d72c7e..29401de3 100644 --- a/nw/gui/projload.py +++ b/nw/gui/projload.py @@ -129,6 +129,9 @@ class GuiProjectLoad(QDialog): self.newButton = self.buttonBox.addButton("New", QDialogButtonBox.ActionRole) 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.addWidget(self.buttonBox) self.setLayout(self.outerBox) @@ -138,7 +141,7 @@ class GuiProjectLoad(QDialog): keyDelete = QShortcut(self.listBox) keyDelete.setKey(QKeySequence(Qt.Key_Delete)) - keyDelete.activated.connect(self._keyPressDelete) + keyDelete.activated.connect(self._doDeleteRecent) logger.debug("GuiProjectLoad initialisation complete") @@ -212,15 +215,16 @@ class GuiProjectLoad(QDialog): self.accept() return - def _keyPressDelete(self): + def _doDeleteRecent(self): """Remove an entry from the recent projects list. """ selList = self.listBox.selectedItems() if selList: - msgYes = self.theParent.askQuestion( - "Remove Entry", - "Remove the selected entry from the recent projects list?" - ) + projName = selList[0].text(self.C_NAME) + msgYes = self.theParent.askQuestion("Remove Entry", ( + "Remove '%s' from the recent projects list? " + "The project files will not be deleted." + ) % projName) if msgYes: self.mainConf.removeFromRecentCache( selList[0].data(self.C_NAME, Qt.UserRole) diff --git a/tests/test_gui/test_gui_projload.py b/tests/test_gui/test_gui_projload.py index 1776a6a7..41458d9d 100644 --- a/tests/test_gui/test_gui_projload.py +++ b/tests/test_gui/test_gui_projload.py @@ -102,7 +102,7 @@ def testGuiLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal): qtbot.wait(stepDelay) nwLoad.show() - nwLoad._keyPressDelete() + nwLoad._doDeleteRecent() assert nwLoad.listBox.topLevelItemCount() == recentCount - 1 getFile = os.path.join(nwMinimal, "nwProject.nwx") From 190e74b4d27f773f7ab3efb29e8b4cd49731dcf1 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 6 Feb 2021 01:41:56 +0100 Subject: [PATCH 4/6] Clarify and cross-reference the two sections of the docs describing references and tags --- docs/source/usage_notes.rst | 3 ++- docs/source/usage_structure.rst | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/source/usage_notes.rst b/docs/source/usage_notes.rst index 38e6a7ce..b0b46143 100644 --- a/docs/source/usage_notes.rst +++ b/docs/source/usage_notes.rst @@ -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 ``@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 -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 allowed, that is, the tag is unique. Duplicate tags should be detected as long as the index is up diff --git a/docs/source/usage_structure.rst b/docs/source/usage_structure.rst index 385d16d6..8bfe81d5 100644 --- a/docs/source/usage_structure.rst +++ b/docs/source/usage_structure.rst @@ -86,10 +86,11 @@ more details. Tag References ============== -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 -an outline view on the :guilabel:`Outline` tab of how the different parts of the novel are -connected. +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 an +outline view on the :guilabel:`Outline` tab of how the different parts of the novel are 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 navigate between reference notes while writing. Clicked links are always opened in the view panel. From 40b2efb339c158296785dd1fa29596a97bbe0662 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:35:20 +0100 Subject: [PATCH 5/6] Remove some redundant line breaks --- nw/constants/constants.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nw/constants/constants.py b/nw/constants/constants.py index 10c44bac..9d9bd4d1 100644 --- a/nw/constants/constants.py +++ b/nw/constants/constants.py @@ -210,7 +210,6 @@ class nwQuotes(): """Allowed quotation marks. Source: https://en.wikipedia.org/wiki/Quotation_mark """ - SYMBOLS = { "\u0027" : "Straight single quotation mark", "\u0022" : "Straight double quotation mark", @@ -241,7 +240,6 @@ class nwQuotes(): class nwUnicode: """Supported unicode character constants and their HTML equivalents. """ - # Unicode Constants # ================= From dee8ccd8b35222a3e678b976570898844bd1963b Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 6 Feb 2021 13:35:53 +0100 Subject: [PATCH 6/6] Search box should select the text on open #645 --- nw/gui/doceditor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index fb2a07e6..9e4b8b89 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -1988,6 +1988,7 @@ class GuiDocEditSearch(QFrame): if theText is not None: self.searchBox.setText(theText) self.searchBox.setFocus() + self.searchBox.selectAll() if self.isRegEx: self._alertSearchValid(True) logger.verbose("Setting search text to '%s'" % theText)