From e49678eaa5327c731e665bb3a573e17adb5862ee Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 31 May 2020 13:07:28 +0200 Subject: [PATCH 1/6] Some improvements to the word count update --- nw/gui/elements/docdetails.py | 12 ++++++++++++ nw/gui/elements/doceditor.py | 28 +++++++++++++++++++--------- nw/gui/statusbar.py | 2 +- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/nw/gui/elements/docdetails.py b/nw/gui/elements/docdetails.py index 84418802..37077830 100644 --- a/nw/gui/elements/docdetails.py +++ b/nw/gui/elements/docdetails.py @@ -48,6 +48,7 @@ class GuiDocDetails(QFrame): self.theParent = theParent self.theProject = theProject self.theTheme = theParent.theTheme + self.theHandle = None self.mainBox = QGridLayout(self) self.mainBox.setVerticalSpacing(1) @@ -193,9 +194,20 @@ class GuiDocDetails(QFrame): # Class Methods ## + def updateCounts(self, tHandle, cC, wC, pC): + """Just update the counts if the handle is the same as the one + we're already showing. + """ + if tHandle == self.theHandle: + self.cCountData.setText("{:n}".format(cC)) + self.wCountData.setText("{:n}".format(wC)) + self.pCountData.setText("{:n}".format(pC)) + return + def updateViewBox(self, tHandle): """Populate the details box from a given handle. """ + self.theHandle = tHandle nwItem = self.theProject.projTree[tHandle] if nwItem is None: diff --git a/nw/gui/elements/doceditor.py b/nw/gui/elements/doceditor.py index aa3ea1c0..8ceb297e 100644 --- a/nw/gui/elements/doceditor.py +++ b/nw/gui/elements/doceditor.py @@ -300,15 +300,16 @@ class GuiDocEditor(QTextEdit): docText = self.getText() cursPos = self.getCursorPosition() - theItem = self.nwDocument.theItem - theItem.setCharCount(self.charCount) - theItem.setWordCount(self.wordCount) - theItem.setParaCount(self.paraCount) - theItem.setCursorPos(cursPos) + self.nwDocument.theItem.setCharCount(self.charCount) + self.nwDocument.theItem.setWordCount(self.wordCount) + self.nwDocument.theItem.setParaCount(self.paraCount) + self.nwDocument.theItem.setCursorPos(cursPos) self.nwDocument.saveDocument(docText) self.setDocumentChanged(False) - self.theParent.theIndex.scanText(theItem.itemHandle, docText) + self.theParent.theIndex.scanText( + self.nwDocument.theItem.itemHandle, docText + ) return True @@ -737,15 +738,24 @@ class GuiDocEditor(QTextEdit): def _updateCounts(self): """Slot for the word counter's finished signal """ + if self.theHandle is None or self.nwDocument.theItem is None: + return + logger.verbose("Updating word count") - tHandle = self.nwDocument.docHandle self.charCount = self.wCounter.charCount self.wordCount = self.wCounter.wordCount self.paraCount = self.wCounter.paraCount - self.theParent.statusBar.setCounts(self.charCount,self.wordCount,self.paraCount) - self.theParent.treeView.propagateCount(tHandle, self.wordCount) + self.nwDocument.theItem.setCharCount(self.charCount) + self.nwDocument.theItem.setWordCount(self.wordCount) + self.nwDocument.theItem.setParaCount(self.paraCount) + + self.theParent.statusBar.setCounts(self.charCount, self.wordCount, self.paraCount) + self.theParent.treeView.propagateCount(self.theHandle, self.wordCount) self.theParent.treeView.projectWordCount() + self.theParent.treeMeta.updateCounts( + self.theHandle, self.charCount, self.wordCount, self.paraCount + ) self._checkDocSize(self.charCount) return diff --git a/nw/gui/statusbar.py b/nw/gui/statusbar.py index 5fc4c873..dd710aa4 100644 --- a/nw/gui/statusbar.py +++ b/nw/gui/statusbar.py @@ -205,7 +205,7 @@ class GuiMainStatus(QStatusBar): """Update statistics. """ self.statsText.setToolTip( - "D: Document word count
P: Project word count" + "D: Document word count
P: Project word count (session change)" ) self.statsText.setText(( "D:{wC:n} P:{pWC:n} ({sWC:+n})" From 199ecc28e3844bfb16f9215355b8363578225682 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 31 May 2020 13:38:16 +0200 Subject: [PATCH 2/6] The build dialog should show the result from last build when opening --- nw/constants/constants.py | 1 + nw/gui/build.py | 73 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/nw/constants/constants.py b/nw/constants/constants.py index cf0ab4eb..92378a73 100644 --- a/nw/constants/constants.py +++ b/nw/constants/constants.py @@ -45,6 +45,7 @@ class nwFiles(): INDEX_FILE = "tagsIndex.json" OPTS_FILE = "guiOptions.json" RECENT_FILE = "recentProjects.json" + BUILD_CACHE = "prevBuild.json" # END Class nwFiles diff --git a/nw/gui/build.py b/nw/gui/build.py index d30ef7de..6c424ddb 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -26,6 +26,7 @@ """ import logging +import json import nw from os import path @@ -45,7 +46,7 @@ from PyQt5.QtWidgets import ( from nw.gui.additions import QSwitch from nw.core import ToHtml from nw.constants import ( - nwAlert, nwItemType, nwItemLayout, nwItemClass + nwAlert, nwFiles, nwItemType, nwItemLayout, nwItemClass ) logger = logging.getLogger(__name__) @@ -372,6 +373,15 @@ class GuiBuildNovel(QDialog): logger.debug("GuiBuildNovel initialisation complete") + # Load from Cache + if self._loadCache(): + self.docView.setStyleSheet(self.htmlStyle) + self.docView.setContent(self.htmlText) + else: + self.htmlText = [] + self.htmlStyle = [] + self.nwdText = [] + return ## @@ -459,6 +469,8 @@ class GuiBuildNovel(QDialog): self.docView.setStyleSheet(self.htmlStyle) self.docView.setContent(self.htmlText) + self._saveCache() + return def _checkInclude(self, theItem, noteFiles, novelFiles, ignoreFlag): @@ -683,6 +695,60 @@ class GuiBuildNovel(QDialog): self.textSize.setValue(theFont.pointSize()) return + def _loadCache(self): + """Save the current data to cache. + """ + buildCache = path.join(self.theProject.projCache, nwFiles.BUILD_CACHE) + dataCount = 0 + if path.isfile(buildCache): + + logger.debug("Loading build cache") + try: + with open(buildCache, mode="r", encoding="utf8") as inFile: + theJson = inFile.read() + theData = json.loads(theJson) + except Exception as e: + logger.error("Failed to load build cache") + logger.error(str(e)) + return False + + if "htmlText" in theData.keys(): + self.htmlText = theData["htmlText"] + dataCount += 1 + if "htmlStyle" in theData.keys(): + self.htmlStyle = theData["htmlStyle"] + dataCount += 1 + if "nwdText" in theData.keys(): + self.nwdText = theData["nwdText"] + dataCount += 1 + + return dataCount == 3 + + def _saveCache(self): + """Save the current data to cache. + """ + buildCache = path.join(self.theProject.projCache, nwFiles.BUILD_CACHE) + + if self.mainConf.debugInfo: + nIndent = 2 + else: + nIndent = None + + logger.debug("Saving build cache") + try: + with open(buildCache, mode="w+", encoding="utf8") as outFile: + outFile.write(json.dumps({ + "htmlText" : self.htmlText, + "htmlStyle" : self.htmlStyle, + "nwdText" : self.nwdText, + }, indent=nIndent)) + except Exception as e: + logger.error("Failed to save build cache") + logger.error(str(e)) + return False + + return True + def _doClose(self): """Close button was clicked. """ @@ -762,6 +828,11 @@ class GuiBuildNovelDocView(QTextBrowser): self.qDocument = self.document() self.qDocument.setDocumentMargin(self.mainConf.textMargin) + self.setPlaceholderText( + "This area will show the content of the document to be " + "exported or printed. Press the \"Build Novel Project\" " + "button to generate content." + ) theFont = QFont() if self.mainConf.textFont is None: From ce19de31f5428326b4f7f2f8de037bd37e4bc962 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 31 May 2020 14:11:35 +0200 Subject: [PATCH 3/6] Added build files to JSON with HTML or markdown formatting --- nw/gui/build.py | 63 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/nw/gui/build.py b/nw/gui/build.py index 6c424ddb..2616ff83 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -53,12 +53,14 @@ logger = logging.getLogger(__name__) class GuiBuildNovel(QDialog): - FMT_ODT = 1 - FMT_PDF = 2 - FMT_HTM = 3 - FMT_MD = 4 - FMT_NWD = 5 - FMT_TXT = 6 + FMT_ODT = 1 + FMT_PDF = 2 + FMT_HTM = 3 + FMT_MD = 4 + FMT_NWD = 5 + FMT_TXT = 6 + FMT_JSON_H = 7 + FMT_JSON_M = 8 def __init__(self, theParent, theProject): QDialog.__init__(self, theParent) @@ -334,7 +336,7 @@ class GuiBuildNovel(QDialog): self.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD)) self.saveMenu.addAction(self.saveMD) - self.saveNWD = QAction("novelWriter Markdown (.nwd)", self) + self.saveNWD = QAction("%s Markdown (.nwd)" % nw.__package__, self) self.saveNWD.triggered.connect(lambda: self._saveDocument(self.FMT_NWD)) self.saveMenu.addAction(self.saveNWD) @@ -342,6 +344,14 @@ class GuiBuildNovel(QDialog): self.saveTXT.triggered.connect(lambda: self._saveDocument(self.FMT_TXT)) self.saveMenu.addAction(self.saveTXT) + self.saveJsonH = QAction("JSON + %s HTML (.json)" % nw.__package__, self) + self.saveJsonH.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_H)) + self.saveMenu.addAction(self.saveJsonH) + + self.saveJsonM = QAction("JSON + %s Markdown (.json)" % nw.__package__, self) + self.saveJsonM.triggered.connect(lambda: self._saveDocument(self.FMT_JSON_M)) + self.saveMenu.addAction(self.saveJsonM) + self.btnClose = QPushButton("Close") self.btnClose.clicked.connect(self._doClose) @@ -546,7 +556,7 @@ class GuiBuildNovel(QDialog): elif theFormat == self.FMT_NWD: fileExt = "nwd" - textFmt = "%s markdown" % nw.__package__ + textFmt = "%s Markdown" % nw.__package__ outTool = "NW" elif theFormat == self.FMT_TXT: @@ -555,6 +565,16 @@ class GuiBuildNovel(QDialog): textFmt = "Plain Text" outTool = "Qt" + elif theFormat == self.FMT_JSON_H: + fileExt = "json" + textFmt = "JSON + %s HTML" % nw.__package__ + outTool = "NW" + + elif theFormat == self.FMT_JSON_M: + fileExt = "json" + textFmt = "JSON + %s Markdown" % nw.__package__ + outTool = "NW" + else: return False @@ -628,6 +648,33 @@ class GuiBuildNovel(QDialog): for aLine in self.nwdText: outFile.write(aLine) + elif theFormat == self.FMT_JSON_H or theFormat == self.FMT_JSON_M: + jsonData = { + "meta" : { + "workingTitle" : self.theProject.projName, + "novelTitle" : self.theProject.bookTitle, + "authors" : self.theProject.bookAuthors, + } + } + + if theFormat == self.FMT_JSON_H: + theBody = [] + for htmlPage in self.htmlText: + theBody.append(htmlPage.rstrip("\n").split("\n")) + jsonData["text"] = { + "css" : self.htmlStyle, + "html" : theBody, + } + elif theFormat == self.FMT_JSON_M: + theBody = [] + for nwdPage in self.nwdText: + theBody.append(nwdPage.split("\n")) + jsonData["text"] = { + "nwd" : theBody, + } + + outFile.write(json.dumps(jsonData, indent=2)) + wSuccess = True except Exception as e: From 6fc1d09a069f6928154276dd155b7ea8ab953790 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 31 May 2020 14:14:09 +0200 Subject: [PATCH 4/6] Remember to set correct font in build tool before loading previous content --- nw/gui/build.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nw/gui/build.py b/nw/gui/build.py index 2616ff83..3c2e73da 100644 --- a/nw/gui/build.py +++ b/nw/gui/build.py @@ -385,6 +385,11 @@ class GuiBuildNovel(QDialog): # Load from Cache if self._loadCache(): + textFont = self.textFont.text() + textSize = self.textSize.value() + justifyText = self.justifyText.isChecked() + self.docView.setTextFont(textFont, textSize) + self.docView.setJustify(justifyText) self.docView.setStyleSheet(self.htmlStyle) self.docView.setContent(self.htmlText) else: From 72ed6dec6e2eb62c064dc331c5427c477a6352da Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 31 May 2020 14:40:10 +0200 Subject: [PATCH 5/6] Fixed a bug caused by a hardcoded project tree index (just added a column) --- nw/guimain.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/nw/guimain.py b/nw/guimain.py index 67bb9fe0..cad673de 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -1019,14 +1019,15 @@ class GuiMain(QMainWindow): return def _treeDoubleClick(self, tItem, colNo): - tHandle = tItem.text(3) + tHandle = tItem.text(self.treeView.C_HANDLE) logger.verbose("User double clicked tree item with handle %s" % tHandle) nwItem = self.theProject.projTree[tHandle] - if nwItem.itemType == nwItemType.FILE: - logger.verbose("Requested item %s is a file" % tHandle) - self.openDocument(tHandle) - else: - logger.verbose("Requested item %s is a folder" % tHandle) + if nwItem is not None: + if nwItem.itemType == nwItemType.FILE: + logger.verbose("Requested item %s is a file" % tHandle) + self.openDocument(tHandle) + else: + logger.verbose("Requested item %s is a folder" % tHandle) return def _treeKeyPressReturn(self): From 9cd4df1d88bcede697e5022eb94c9fdc5dddd2ba Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sun, 31 May 2020 14:41:49 +0200 Subject: [PATCH 6/6] Added some comments --- nw/guimain.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/nw/guimain.py b/nw/guimain.py index cad673de..bfc98c71 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -1019,6 +1019,9 @@ class GuiMain(QMainWindow): return def _treeDoubleClick(self, tItem, colNo): + """The user double-clicked an item in the tree. If it is a file, + we open it. Otherwise, we do nothing. + """ tHandle = tItem.text(self.treeView.C_HANDLE) logger.verbose("User double clicked tree item with handle %s" % tHandle) nwItem = self.theProject.projTree[tHandle] @@ -1031,14 +1034,18 @@ class GuiMain(QMainWindow): return def _treeKeyPressReturn(self): + """The user pressed return an item in the tree. If it is a file, + we open it. Otherwise, we do nothing. + """ tHandle = self.treeView.getSelectedHandle() logger.verbose("User pressed return on tree item with handle %s" % tHandle) nwItem = self.theProject.projTree[tHandle] - if nwItem.itemType == nwItemType.FILE: - logger.verbose("Requested item %s is a file" % tHandle) - self.openDocument(tHandle) - else: - logger.verbose("Requested item %s is a folder" % tHandle) + if nwItem is not None: + if nwItem.itemType == nwItemType.FILE: + logger.verbose("Requested item %s is a file" % tHandle) + self.openDocument(tHandle) + else: + logger.verbose("Requested item %s is a folder" % tHandle) return def _keyPressEscape(self):