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):