Cleanup (#820)
* Update instructions and readmes * Cleanup in base files * Cleanup in core files * Cleanup in gui files * Cleanup in dialog files * Cleanup in tools files * Cleanup in test files
This commit is contained in:
committed by
GitHub
parent
1881b3d32c
commit
6d7ca2bb86
+5
-1
@@ -22,8 +22,12 @@ either `testing` or `dev` branch.
|
||||
### What Branch to Use for Contributions
|
||||
|
||||
* If your contribution is a fix for the latest stable release, branch from the `main` branch.
|
||||
* If your contribution is a fix for the latest testing release, branch from the `testing` branch.
|
||||
* If your contribution is a new feature, branch from the `dev` branch.
|
||||
* The `testing` branch is used infrequently. It is only used for pre-releases when new code is
|
||||
being developed that is not a part of the current testing release.
|
||||
|
||||
The current status of each branch is described in a pinned issue titled
|
||||
"[Development Flow & Status](https://github.com/vkbo/novelWriter/issues/707)".
|
||||
|
||||
Please do not make your changes on a branch with the same name as any of the above mentioned
|
||||
branches. You should make a unique and descriptive branch name in your fork for your changes.
|
||||
|
||||
@@ -75,6 +75,7 @@ In addition, novelWriter adds the following syntax used for its additional featu
|
||||
be used to make simple tables and lists. Note that for HTML exports, most browsers will treat a
|
||||
tab as a space, so it may not show up like expected. Open Document exports should produce the
|
||||
expected result.
|
||||
* Paragraph alignment and indendtation is supported by a set of tags using `>` and `<` markers.
|
||||
|
||||
The core export formats of novelWriter are Open Document and HTML5. Open Document is an open
|
||||
standard for office type documents that is supported by most office applications. See
|
||||
@@ -105,7 +106,7 @@ four can be used internally in each scene to create separate sections.
|
||||
|
||||
Each novel file can be assigned a layout format, which shows up as a flag next to the item in the
|
||||
project tree. These are mostly to help the user track what they contain, but they also have some
|
||||
impact on the format of the exported document. See the
|
||||
impact on the layout of the exported document. See the
|
||||
[documentation](https://novelwriter.readthedocs.io) for further details.
|
||||
|
||||
### Project Notes
|
||||
|
||||
+6
-6
@@ -3,14 +3,14 @@
|
||||
The `i18n` folder contains the translation files for the Qt5 GUI. There are two types of files
|
||||
involved: the `nw_XX.ts` and the `nw_XX.qm` files. The `nw_XX.ts` files are located in the `i18n`
|
||||
folder at the root of the repository, and the `nw_XX.qm` together with the `project_XX.json` files
|
||||
are located in `nw/assets/i18n`. The latter are JSON files translation maps for the novelWriter
|
||||
projects, used by the Build Novel Project tool.
|
||||
are located in `nw/assets/i18n`. The latter are JSON translation maps for the novelWriter projects,
|
||||
used by the Build Novel Project tool.
|
||||
|
||||
**Note**
|
||||
|
||||
When making a new translation, or updating an existing one, only commit the `nw_XX.ts` you have
|
||||
made changes to. The `qtlupdate` command mentioned below will likely modify all `nw_XX.ts`
|
||||
slightly, but please _don't_ commit those changes and add them to the pull request.
|
||||
slightly, but please _don't_ commit those changes to the pull request.
|
||||
|
||||
## Qt GUI Localisation
|
||||
|
||||
@@ -39,8 +39,8 @@ When you're done editing, you can build the `nw/assets/i18n/nw_XX.qm` file and t
|
||||
novelWriter. The Preferences dialog should list the newly added language, and you can select it and
|
||||
test it.
|
||||
|
||||
Please do not submit the `.qm` files to the repository. Only the `.ts` files in the `i18n` folder
|
||||
are needed.
|
||||
Please do not submit the `.qm` files to the repository. Only the `.ts` file you just added in the
|
||||
`i18n` folder is needed.
|
||||
|
||||
**Note**
|
||||
|
||||
@@ -54,7 +54,7 @@ sudo apt install qttools5-dev-tools pyqt5-dev-tools
|
||||
|
||||
### Missing QtBase Translations
|
||||
|
||||
The default Qt dialogs also have translations, for instance for standard buttons for "Yes", "No",
|
||||
The default Qt dialogs also have translations, for instance for standard buttons like "Yes", "No",
|
||||
"Ok", "Cancel", etc. Generally, these translations files are installed with the Qt libraries on
|
||||
your system, and novelWriter will collect those translations from there. However, these
|
||||
translations are missing for many languages.
|
||||
|
||||
+9
-9
@@ -39,9 +39,9 @@ from nw.config import Config
|
||||
# Generally follows PEP 440
|
||||
# Hex Version:
|
||||
# - Digit 1,2 : Major Version (01-ff)
|
||||
# = Digit 3,4 : Minor Version (01-ff)
|
||||
# - Digit 3,4 : Minor Version (01-ff)
|
||||
# - Digit 5,6 : Patch Version (01-ff)
|
||||
# = Digit 7 : Release Type (a: aplha, b: beta, c: candidate, f: final)
|
||||
# - Digit 7 : Release Type (a: aplha, b: beta, c: candidate, f: final)
|
||||
# - Digit 8 : Release Number (0-f)
|
||||
#
|
||||
# Example : Full Short Description
|
||||
@@ -111,7 +111,7 @@ CONFIG = Config()
|
||||
|
||||
|
||||
def main(sysArgs=None):
|
||||
"""Parses command line, sets up logging, and launches main GUI.
|
||||
"""Parse command line, set up logging, and launches main GUI.
|
||||
"""
|
||||
if sysArgs is None:
|
||||
sysArgs = sys.argv[1:]
|
||||
@@ -155,13 +155,13 @@ def main(sysArgs=None):
|
||||
)
|
||||
|
||||
# Defaults
|
||||
logLevel = logging.WARN
|
||||
logLevel = logging.WARN
|
||||
logFormat = "{levelname:8} {message:}"
|
||||
confPath = None
|
||||
dataPath = None
|
||||
testMode = False
|
||||
qtStyle = "Fusion"
|
||||
cmdOpen = None
|
||||
confPath = None
|
||||
dataPath = None
|
||||
testMode = False
|
||||
qtStyle = "Fusion"
|
||||
cmdOpen = None
|
||||
|
||||
# Parse Options
|
||||
try:
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ def splitVersionNumber(vString):
|
||||
vMajor = 0
|
||||
vMinor = 0
|
||||
vPatch = 0
|
||||
vInt = 0
|
||||
vInt = 0
|
||||
|
||||
vBits = vString.split(".")
|
||||
nBits = len(vBits)
|
||||
|
||||
+12
-12
@@ -23,12 +23,12 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import logging
|
||||
import configparser
|
||||
import shutil
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
from time import time
|
||||
|
||||
@@ -433,9 +433,9 @@ class Config:
|
||||
return False
|
||||
|
||||
cnfParse = configparser.ConfigParser()
|
||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||
try:
|
||||
with open(cnfPath, mode="r", encoding="utf8") as inFile:
|
||||
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
||||
cnfParse.read_file(inFile)
|
||||
except Exception as e:
|
||||
logger.error("Could not load config file")
|
||||
@@ -807,7 +807,7 @@ class Config:
|
||||
# Write config file
|
||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||
try:
|
||||
with open(cnfPath, mode="w", encoding="utf8") as outFile:
|
||||
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
|
||||
cnfParse.write(outFile)
|
||||
self.confChanged = False
|
||||
except Exception as e:
|
||||
@@ -831,13 +831,13 @@ class Config:
|
||||
|
||||
if os.path.isfile(cacheFile):
|
||||
try:
|
||||
with open(cacheFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
||||
theData = json.load(inFile)
|
||||
|
||||
for projPath in theData.keys():
|
||||
theEntry = theData[projPath]
|
||||
theTitle = ""
|
||||
lastTime = 0
|
||||
theEntry = theData[projPath]
|
||||
theTitle = ""
|
||||
lastTime = 0
|
||||
wordCount = 0
|
||||
if "title" in theEntry.keys():
|
||||
theTitle = theEntry["title"]
|
||||
@@ -869,7 +869,7 @@ class Config:
|
||||
cacheTemp = os.path.join(self.dataPath, nwFiles.RECENT_FILE+"~")
|
||||
|
||||
try:
|
||||
with open(cacheTemp, mode="w+", encoding="utf8") as outFile:
|
||||
with open(cacheTemp, mode="w+", encoding="utf-8") as outFile:
|
||||
json.dump(self.recentProj, outFile, indent=2)
|
||||
except Exception as e:
|
||||
self.hasError = True
|
||||
|
||||
+2
-2
@@ -82,7 +82,7 @@ class NWDoc():
|
||||
self._docMeta = {}
|
||||
if os.path.isfile(docPath):
|
||||
try:
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
with open(docPath, mode="r", encoding="utf-8") as inFile:
|
||||
|
||||
# Check the first <= 10 lines for metadata
|
||||
for i in range(10):
|
||||
@@ -136,7 +136,7 @@ class NWDoc():
|
||||
)
|
||||
|
||||
try:
|
||||
with open(docTemp, mode="w", encoding="utf8") as outFile:
|
||||
with open(docTemp, mode="w", encoding="utf-8") as outFile:
|
||||
outFile.write(docMeta)
|
||||
outFile.write(docText)
|
||||
except Exception as e:
|
||||
|
||||
+15
-15
@@ -144,13 +144,13 @@ class NWIndex():
|
||||
def loadIndex(self):
|
||||
"""Load index from last session from the project meta folder.
|
||||
"""
|
||||
theData = {}
|
||||
theData = {}
|
||||
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||
|
||||
if os.path.isfile(indexFile):
|
||||
logger.debug("Loading index file")
|
||||
try:
|
||||
with open(indexFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(indexFile, mode="r", encoding="utf-8") as inFile:
|
||||
theData = json.load(inFile)
|
||||
except Exception:
|
||||
logger.error("Failed to load index file")
|
||||
@@ -158,10 +158,10 @@ class NWIndex():
|
||||
self.indexBroken = True
|
||||
return False
|
||||
|
||||
self._tagIndex = theData.get("tagIndex", {})
|
||||
self._refIndex = theData.get("refIndex", {})
|
||||
self._tagIndex = theData.get("tagIndex", {})
|
||||
self._refIndex = theData.get("refIndex", {})
|
||||
self._novelIndex = theData.get("novelIndex", {})
|
||||
self._noteIndex = theData.get("noteIndex", {})
|
||||
self._noteIndex = theData.get("noteIndex", {})
|
||||
self._textCounts = theData.get("textCounts", {})
|
||||
|
||||
nowTime = round(time())
|
||||
@@ -181,12 +181,12 @@ class NWIndex():
|
||||
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||
|
||||
try:
|
||||
with open(indexFile, mode="w+", encoding="utf8") as outFile:
|
||||
with open(indexFile, mode="w+", encoding="utf-8") as outFile:
|
||||
json.dump({
|
||||
"tagIndex": self._tagIndex,
|
||||
"refIndex": self._refIndex,
|
||||
"tagIndex": self._tagIndex,
|
||||
"refIndex": self._refIndex,
|
||||
"novelIndex": self._novelIndex,
|
||||
"noteIndex": self._noteIndex,
|
||||
"noteIndex": self._noteIndex,
|
||||
"textCounts": self._textCounts,
|
||||
}, outFile, indent=2)
|
||||
except Exception:
|
||||
@@ -292,12 +292,12 @@ class NWIndex():
|
||||
for aTag in clearTags:
|
||||
self._tagIndex.pop(aTag)
|
||||
|
||||
nLine = 0
|
||||
nLine = 0
|
||||
nTitle = 0
|
||||
theLines = theText.splitlines()
|
||||
for aLine in theLines:
|
||||
nLine += 1
|
||||
nChar = len(aLine.strip())
|
||||
nChar = len(aLine.strip())
|
||||
if nChar == 0:
|
||||
continue
|
||||
|
||||
@@ -352,16 +352,16 @@ class NWIndex():
|
||||
"""
|
||||
if aLine.startswith("# "):
|
||||
hDepth = "H1"
|
||||
hText = aLine[2:].strip()
|
||||
hText = aLine[2:].strip()
|
||||
elif aLine.startswith("## "):
|
||||
hDepth = "H2"
|
||||
hText = aLine[3:].strip()
|
||||
hText = aLine[3:].strip()
|
||||
elif aLine.startswith("### "):
|
||||
hDepth = "H3"
|
||||
hText = aLine[4:].strip()
|
||||
hText = aLine[4:].strip()
|
||||
elif aLine.startswith("#### "):
|
||||
hDepth = "H4"
|
||||
hText = aLine[5:].strip()
|
||||
hText = aLine[5:].strip()
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
+3
-3
@@ -125,12 +125,12 @@ class OptionState():
|
||||
return False
|
||||
|
||||
stateFile = os.path.join(self.theProject.projMeta, nwFiles.OPTS_FILE)
|
||||
theState = {}
|
||||
theState = {}
|
||||
|
||||
if os.path.isfile(stateFile):
|
||||
logger.debug("Loading GUI options file")
|
||||
try:
|
||||
with open(stateFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(stateFile, mode="r", encoding="utf-8") as inFile:
|
||||
theState = json.load(inFile)
|
||||
except Exception:
|
||||
logger.error("Failed to load GUI options file")
|
||||
@@ -157,7 +157,7 @@ class OptionState():
|
||||
logger.debug("Saving GUI options file")
|
||||
|
||||
try:
|
||||
with open(stateFile, mode="w+", encoding="utf8") as outFile:
|
||||
with open(stateFile, mode="w+", encoding="utf-8") as outFile:
|
||||
json.dump(self.theState, outFile, indent=2)
|
||||
except Exception:
|
||||
logger.error("Failed to save GUI options file")
|
||||
|
||||
+17
-18
@@ -355,7 +355,7 @@ class NWProject():
|
||||
|
||||
def openProject(self, fileName, overrideLock=False):
|
||||
"""Open the project file provided, or if doesn't exist, assume
|
||||
it is a folder, and look for the file within it. If successful,
|
||||
it is a folder and look for the file within it. If successful,
|
||||
parse the XML of the file and populate the project variables and
|
||||
build the tree of project items.
|
||||
"""
|
||||
@@ -483,10 +483,11 @@ class NWProject():
|
||||
msgYes = self.theParent.askQuestion(
|
||||
self.tr("Version Conflict"),
|
||||
self.tr(
|
||||
"This project was saved by a newer version of novelWriter, version "
|
||||
"{0}. This is version {1}. If you continue to open the project, "
|
||||
"some attributes and settings may not be preserved, but the "
|
||||
"overall project should be fine. Continue opening the project?"
|
||||
"This project was saved by a newer version of "
|
||||
"novelWriter, version {0}. This is version {1}. If you "
|
||||
"continue to open the project, some attributes and "
|
||||
"settings may not be preserved, but the overall project "
|
||||
"should be fine. Continue opening the project?"
|
||||
).format(appVersion, nw.__version__)
|
||||
)
|
||||
if not msgYes:
|
||||
@@ -1219,7 +1220,7 @@ class NWProject():
|
||||
loadFile = chkFile2
|
||||
|
||||
try:
|
||||
with open(loadFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(loadFile, mode="r", encoding="utf-8") as inFile:
|
||||
self.langData = json.load(inFile)
|
||||
logger.debug("Loaded project language file: %s", os.path.basename(loadFile))
|
||||
|
||||
@@ -1242,7 +1243,7 @@ class NWProject():
|
||||
|
||||
theLines = []
|
||||
try:
|
||||
with open(lockFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(lockFile, mode="r", encoding="utf-8") as inFile:
|
||||
theData = inFile.read()
|
||||
theLines = theData.splitlines()
|
||||
if len(theLines) != 4:
|
||||
@@ -1263,7 +1264,7 @@ class NWProject():
|
||||
|
||||
lockFile = os.path.join(self.projPath, nwFiles.PROJ_LOCK)
|
||||
try:
|
||||
with open(lockFile, mode="w+", encoding="utf8") as outFile:
|
||||
with open(lockFile, mode="w+", encoding="utf-8") as outFile:
|
||||
outFile.write("%s\n" % self.mainConf.hostName)
|
||||
outFile.write("%s\n" % self.mainConf.osType)
|
||||
outFile.write("%s\n" % self.mainConf.kernelVer)
|
||||
@@ -1442,7 +1443,7 @@ class NWProject():
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(sessionFile, mode="a+", encoding="utf8") as outFile:
|
||||
with open(sessionFile, mode="a+", encoding="utf-8") as outFile:
|
||||
if not isFile:
|
||||
# It's a new file, so add a header
|
||||
if self.lastWCount > 0:
|
||||
@@ -1481,7 +1482,6 @@ class NWProject():
|
||||
logger.info("Old data folder %s found", theFolder)
|
||||
|
||||
# Move Documents to Content
|
||||
# =========================
|
||||
for dataItem in os.listdir(theData):
|
||||
theFile = os.path.join(theData, dataItem)
|
||||
if not os.path.isfile(theFile):
|
||||
@@ -1517,7 +1517,6 @@ class NWProject():
|
||||
errList.append(theErr)
|
||||
|
||||
# Remove Data Folder
|
||||
# ==================
|
||||
try:
|
||||
os.rmdir(theData)
|
||||
logger.info("Deleted folder: %s", theFolder)
|
||||
@@ -1563,13 +1562,13 @@ class NWProject():
|
||||
os.path.join(self.projCache, "nwProject.nwx.7"),
|
||||
os.path.join(self.projCache, "nwProject.nwx.8"),
|
||||
os.path.join(self.projCache, "nwProject.nwx.9"),
|
||||
os.path.join(self.projMeta, "mainOptions.json"),
|
||||
os.path.join(self.projMeta, "exportOptions.json"),
|
||||
os.path.join(self.projMeta, "outlineOptions.json"),
|
||||
os.path.join(self.projMeta, "timelineOptions.json"),
|
||||
os.path.join(self.projMeta, "docMergeOptions.json"),
|
||||
os.path.join(self.projMeta, "sessionLogOptions.json"),
|
||||
os.path.join(self.projPath, "ToC.json"),
|
||||
os.path.join(self.projMeta, "mainOptions.json"),
|
||||
os.path.join(self.projMeta, "exportOptions.json"),
|
||||
os.path.join(self.projMeta, "outlineOptions.json"),
|
||||
os.path.join(self.projMeta, "timelineOptions.json"),
|
||||
os.path.join(self.projMeta, "docMergeOptions.json"),
|
||||
os.path.join(self.projMeta, "sessionLogOptions.json"),
|
||||
os.path.join(self.projPath, "ToC.json"),
|
||||
]
|
||||
|
||||
for rmFile in rmList:
|
||||
|
||||
@@ -235,8 +235,7 @@ class FakeEnchant:
|
||||
class NWSpellSimple(NWSpellCheck):
|
||||
"""Internal spell check tool that uses standard Python packages with
|
||||
no other external dependencies. This is the fallback spell checker
|
||||
when no other is available. This method is fairly slow compared to
|
||||
other implementations.
|
||||
when no other is available. This method is slower than enchant.
|
||||
"""
|
||||
theWords = set()
|
||||
|
||||
|
||||
+12
-10
@@ -35,12 +35,14 @@ logger = logging.getLogger(__name__)
|
||||
class NWStatus():
|
||||
|
||||
def __init__(self):
|
||||
self._theLabels = []
|
||||
|
||||
self._theLabels = []
|
||||
self._theColours = []
|
||||
self._theCounts = []
|
||||
self._theMap = {}
|
||||
self._theLength = 0
|
||||
self._theIndex = 0
|
||||
self._theCounts = []
|
||||
self._theMap = {}
|
||||
self._theLength = 0
|
||||
self._theIndex = 0
|
||||
|
||||
return
|
||||
|
||||
def addEntry(self, theLabel, theColours):
|
||||
@@ -87,12 +89,12 @@ class NWStatus():
|
||||
replaceMap = {}
|
||||
|
||||
if newList is not None:
|
||||
self._theLabels = []
|
||||
self._theLabels = []
|
||||
self._theColours = []
|
||||
self._theCounts = []
|
||||
self._theMap = {}
|
||||
self._theLength = 0
|
||||
self._theIndex = 0
|
||||
self._theCounts = []
|
||||
self._theMap = {}
|
||||
self._theLength = 0
|
||||
self._theIndex = 0
|
||||
|
||||
for nName, nR, nG, nB, oName in newList:
|
||||
self.addEntry(nName, (nR, nG, nB))
|
||||
|
||||
+4
-4
@@ -40,9 +40,9 @@ class ToHtml(Tokenizer):
|
||||
def __init__(self, theProject):
|
||||
Tokenizer.__init__(self, theProject)
|
||||
|
||||
self.genMode = self.M_EXPORT
|
||||
self.genMode = self.M_EXPORT
|
||||
self.cssStyles = True
|
||||
self.fullHTML = []
|
||||
self.fullHTML = []
|
||||
|
||||
# Internals
|
||||
self._trMap = {}
|
||||
@@ -59,7 +59,7 @@ class ToHtml(Tokenizer):
|
||||
need to make a few changes to formatting, which is managed by
|
||||
these flags.
|
||||
"""
|
||||
self.genMode = self.M_PREVIEW
|
||||
self.genMode = self.M_PREVIEW
|
||||
self.doKeywords = True
|
||||
self.doComments = doComments
|
||||
self.doSynopsis = doSynopsis
|
||||
@@ -268,7 +268,7 @@ class ToHtml(Tokenizer):
|
||||
def saveHTML5(self, savePath):
|
||||
"""Save the data to an .html file.
|
||||
"""
|
||||
with open(savePath, mode="w", encoding="utf8") as outFile:
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
theStyle = self.getStyleSheet()
|
||||
theStyle.append("article {width: 800px; margin: 40px auto;}")
|
||||
bodyText = "".join(self.fullHTML)
|
||||
|
||||
+19
-19
@@ -24,8 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import re
|
||||
import logging
|
||||
|
||||
from operator import itemgetter
|
||||
from functools import partial
|
||||
@@ -43,12 +43,12 @@ logger = logging.getLogger(__name__)
|
||||
class Tokenizer():
|
||||
|
||||
# In-Text Format
|
||||
FMT_B_B = 1 # Begin bold
|
||||
FMT_B_E = 2 # End bold
|
||||
FMT_I_B = 3 # Begin italics
|
||||
FMT_I_E = 4 # End italics
|
||||
FMT_D_B = 5 # Begin strikeout
|
||||
FMT_D_E = 6 # End strikeout
|
||||
FMT_B_B = 1 # Begin bold
|
||||
FMT_B_E = 2 # End bold
|
||||
FMT_I_B = 3 # Begin italics
|
||||
FMT_I_E = 4 # End italics
|
||||
FMT_D_B = 5 # Begin strikeout
|
||||
FMT_D_E = 6 # End strikeout
|
||||
|
||||
# Block Type
|
||||
T_EMPTY = 1 # Empty line (new paragraph)
|
||||
@@ -86,11 +86,11 @@ class Tokenizer():
|
||||
self.mainConf = nw.CONFIG
|
||||
|
||||
# Data Variables
|
||||
self.theText = "" # The raw text to be tokenized
|
||||
self.theHandle = None # The handle associated with the text
|
||||
self.theItem = None # The NWItem associated with the handle
|
||||
self.theTokens = [] # The list of the processed tokens
|
||||
self.theResult = "" # The result of the last document
|
||||
self.theText = "" # The raw text to be tokenized
|
||||
self.theHandle = None # The handle associated with the text
|
||||
self.theItem = None # The NWItem associated with the handle
|
||||
self.theTokens = [] # The list of the processed tokens
|
||||
self.theResult = "" # The result of the last document
|
||||
|
||||
self.keepMarkdown = False # Whether to keep the markdown text
|
||||
self.theMarkdown = [] # The result novelWriter markdown of all documents
|
||||
@@ -117,11 +117,11 @@ class Tokenizer():
|
||||
self.marginMeta = (0.000, 0.584)
|
||||
|
||||
# Title Formats
|
||||
self.fmtTitle = "%title%" # Formatting for titles
|
||||
self.fmtChapter = "%title%" # Formatting for numbered chapters
|
||||
self.fmtUnNum = "%title%" # Formatting for unnumbered chapters
|
||||
self.fmtScene = "%title%" # Formatting for scenes
|
||||
self.fmtSection = "%title%" # Formatting for sections
|
||||
self.fmtTitle = "%title%" # Formatting for titles
|
||||
self.fmtChapter = "%title%" # Formatting for numbered chapters
|
||||
self.fmtUnNum = "%title%" # Formatting for unnumbered chapters
|
||||
self.fmtScene = "%title%" # Formatting for scenes
|
||||
self.fmtSection = "%title%" # Formatting for sections
|
||||
|
||||
self.hideScene = False # Do not include scene headers
|
||||
self.hideSection = False # Do not include section headers
|
||||
@@ -320,7 +320,7 @@ class Tokenizer():
|
||||
return True
|
||||
|
||||
def doPreProcessing(self):
|
||||
"""Reun trough the various replace doctionaries.
|
||||
"""Run trough the various replace doctionaries.
|
||||
"""
|
||||
# Process the user's auto-replace dictionary
|
||||
if len(self.theProject.autoReplace) > 0:
|
||||
@@ -701,7 +701,7 @@ class Tokenizer():
|
||||
def saveRawMarkdown(self, savePath):
|
||||
"""Save the data to a plain text file.
|
||||
"""
|
||||
with open(savePath, mode="w", encoding="utf8") as outFile:
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
for nwdPage in self.theMarkdown:
|
||||
outFile.write(nwdPage)
|
||||
return
|
||||
|
||||
+14
-8
@@ -72,16 +72,22 @@ class ToMarkdown(Tokenizer):
|
||||
if self.genMode == self.M_STD:
|
||||
# Standard
|
||||
mdTags = {
|
||||
self.FMT_B_B: "**", self.FMT_B_E: "**",
|
||||
self.FMT_I_B: "_", self.FMT_I_E: "_",
|
||||
self.FMT_D_B: "", self.FMT_D_E: "",
|
||||
self.FMT_B_B: "**",
|
||||
self.FMT_B_E: "**",
|
||||
self.FMT_I_B: "_",
|
||||
self.FMT_I_E: "_",
|
||||
self.FMT_D_B: "",
|
||||
self.FMT_D_E: "",
|
||||
}
|
||||
else:
|
||||
# GitHub
|
||||
mdTags = {
|
||||
self.FMT_B_B: "**", self.FMT_B_E: "**",
|
||||
self.FMT_I_B: "_", self.FMT_I_E: "_",
|
||||
self.FMT_D_B: "~~", self.FMT_D_E: "~~",
|
||||
self.FMT_B_B: "**",
|
||||
self.FMT_B_E: "**",
|
||||
self.FMT_I_B: "_",
|
||||
self.FMT_I_E: "_",
|
||||
self.FMT_D_B: "~~",
|
||||
self.FMT_D_E: "~~",
|
||||
}
|
||||
|
||||
self.theResult = ""
|
||||
@@ -149,7 +155,7 @@ class ToMarkdown(Tokenizer):
|
||||
def saveMarkdown(self, savePath):
|
||||
"""Save the data to a plain text file.
|
||||
"""
|
||||
with open(savePath, mode="w", encoding="utf8") as outFile:
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
theText = "".join(self.fullMD)
|
||||
outFile.write(theText)
|
||||
|
||||
@@ -173,7 +179,7 @@ class ToMarkdown(Tokenizer):
|
||||
def _formatKeywords(self, tText, tStyle):
|
||||
"""Apply Markdown formatting to keywords.
|
||||
"""
|
||||
isValid, theBits, thePos = self.theParent.theIndex.scanThis("@"+tText)
|
||||
isValid, theBits, _ = self.theParent.theIndex.scanThis("@"+tText)
|
||||
if not isValid or not theBits:
|
||||
return ""
|
||||
|
||||
|
||||
+3
-3
@@ -24,11 +24,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import os
|
||||
import logging
|
||||
|
||||
from lxml import etree
|
||||
from time import time
|
||||
from lxml import etree
|
||||
from hashlib import sha256
|
||||
|
||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
||||
@@ -193,7 +193,7 @@ class NWTree():
|
||||
try:
|
||||
# Dump the text
|
||||
tocText = os.path.join(self.theProject.projPath, nwFiles.TOC_TXT)
|
||||
with open(tocText, mode="w", encoding="utf8") as outFile:
|
||||
with open(tocText, mode="w", encoding="utf-8") as outFile:
|
||||
outFile.write("\n")
|
||||
outFile.write("Table of Contents\n")
|
||||
outFile.write("=================\n")
|
||||
|
||||
+4
-4
@@ -24,13 +24,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import os
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QCursor
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget,
|
||||
QTextBrowser, QLabel
|
||||
@@ -229,7 +229,7 @@ class GuiAbout(QDialog):
|
||||
"""
|
||||
docPath = os.path.join(self.mainConf.assetPath, "text", "release_notes.htm")
|
||||
if os.path.isfile(docPath):
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
with open(docPath, mode="r", encoding="utf-8") as inFile:
|
||||
helpText = inFile.read()
|
||||
self.pageNotes.setHtml(helpText)
|
||||
else:
|
||||
@@ -241,7 +241,7 @@ class GuiAbout(QDialog):
|
||||
"""
|
||||
docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm")
|
||||
if os.path.isfile(docPath):
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
with open(docPath, mode="r", encoding="utf-8") as inFile:
|
||||
helpText = inFile.read()
|
||||
self.pageLicense.setHtml(helpText)
|
||||
else:
|
||||
|
||||
@@ -137,7 +137,7 @@ class GuiItemEditor(QDialog):
|
||||
# Assemble
|
||||
##
|
||||
|
||||
nameLabel = QLabel(self.tr("Label"))
|
||||
nameLabel = QLabel(self.tr("Label"))
|
||||
statusLabel = QLabel(self.tr("Status"))
|
||||
layoutLabel = QLabel(self.tr("Layout"))
|
||||
|
||||
@@ -179,7 +179,7 @@ class GuiItemEditor(QDialog):
|
||||
"""
|
||||
logger.verbose("ItemEditor save button clicked")
|
||||
|
||||
itemName = self.editName.text()
|
||||
itemName = self.editName.text()
|
||||
itemStatus = self.editStatus.currentData()
|
||||
itemLayout = self.editLayout.currentData()
|
||||
isExported = self.editExport.isChecked()
|
||||
|
||||
@@ -24,11 +24,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import os
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt, QLocale
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtCore import Qt, QLocale
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
|
||||
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
|
||||
@@ -127,7 +127,7 @@ class GuiPreferences(PagedDialog):
|
||||
def _saveWindowSize(self):
|
||||
"""Save the dialog window size.
|
||||
"""
|
||||
winWidth = self.mainConf.rpxInt(self.width())
|
||||
winWidth = self.mainConf.rpxInt(self.width())
|
||||
winHeight = self.mainConf.rpxInt(self.height())
|
||||
self.mainConf.setPreferencesSize(winWidth, winHeight)
|
||||
return
|
||||
@@ -272,12 +272,12 @@ class GuiPreferencesGeneral(QWidget):
|
||||
def saveValues(self):
|
||||
"""Save the values set for this tab.
|
||||
"""
|
||||
guiLang = self.guiLang.currentData()
|
||||
guiTheme = self.guiTheme.currentData()
|
||||
guiIcons = self.guiIcons.currentData()
|
||||
guiDark = self.guiDark.isChecked()
|
||||
guiFont = self.guiFont.text()
|
||||
guiFontSize = self.guiFontSize.value()
|
||||
guiLang = self.guiLang.currentData()
|
||||
guiTheme = self.guiTheme.currentData()
|
||||
guiIcons = self.guiIcons.currentData()
|
||||
guiDark = self.guiDark.isChecked()
|
||||
guiFont = self.guiFont.text()
|
||||
guiFontSize = self.guiFontSize.value()
|
||||
|
||||
# Check if restart is needed
|
||||
needsRestart = False
|
||||
|
||||
@@ -24,13 +24,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import os
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QTreeWidget,
|
||||
QAbstractItemView, QTreeWidgetItem, QDialogButtonBox, QLabel, QShortcut,
|
||||
@@ -105,8 +105,8 @@ class GuiProjectLoad(QDialog):
|
||||
treeHead.setTextAlignment(self.C_TIME, Qt.AlignRight)
|
||||
|
||||
self.lblRecent = QLabel("<b>%s</b>" % self.tr("Recently Opened Projects"))
|
||||
self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
|
||||
self.selPath = QLineEdit("")
|
||||
self.lblPath = QLabel("<b>%s</b>" % self.tr("Path"))
|
||||
self.selPath = QLineEdit("")
|
||||
self.selPath.setReadOnly(True)
|
||||
|
||||
self.browseButton = QPushButton("...")
|
||||
|
||||
@@ -26,8 +26,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import nw
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt, QLocale
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush
|
||||
from PyQt5.QtCore import Qt, QLocale
|
||||
from PyQt5.QtWidgets import (
|
||||
QHBoxLayout, QVBoxLayout, QLineEdit, QPlainTextEdit, QLabel, QWidget,
|
||||
QDialogButtonBox, QPushButton, QColorDialog, QTreeWidget, QTreeWidgetItem,
|
||||
@@ -393,7 +393,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
"""
|
||||
selItem = self._getSelectedItem()
|
||||
if selItem is not None:
|
||||
iRow = self.listBox.indexOfTopLevelItem(selItem)
|
||||
iRow = self.listBox.indexOfTopLevelItem(selItem)
|
||||
selIdx = selItem.data(self.COL_LABEL, Qt.UserRole)
|
||||
if self.colCounts[selIdx] == 0:
|
||||
self.listBox.takeTopLevelItem(iRow)
|
||||
@@ -626,8 +626,8 @@ class GuiProjectEditReplace(QWidget):
|
||||
if selItem is None:
|
||||
return False
|
||||
|
||||
newKey = self.editKey.text()
|
||||
newVal = self.editValue.text()
|
||||
newKey = self.editKey.text()
|
||||
newVal = self.editValue.text()
|
||||
saveKey = self._stripNotAllowed(newKey)
|
||||
|
||||
if len(saveKey) > 0 and len(newVal) > 0:
|
||||
|
||||
@@ -24,8 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import os
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
@@ -155,7 +155,7 @@ class GuiWordList(QDialog):
|
||||
tmpFile = dctFile + "~"
|
||||
|
||||
try:
|
||||
with open(tmpFile, mode="w", encoding="utf8") as outFile:
|
||||
with open(tmpFile, mode="w", encoding="utf-8") as outFile:
|
||||
for i in range(self.listBox.count()):
|
||||
outFile.write(self.listBox.item(i).text() + "\n")
|
||||
|
||||
@@ -193,7 +193,7 @@ class GuiWordList(QDialog):
|
||||
logger.debug("No project dictionary file found")
|
||||
return False
|
||||
|
||||
with open(wordList, mode="r", encoding="utf8") as inFile:
|
||||
with open(wordList, mode="r", encoding="utf-8") as inFile:
|
||||
for inLine in inFile:
|
||||
theWord = inLine.strip()
|
||||
if len(theWord) == 0:
|
||||
|
||||
+5
-5
@@ -104,11 +104,11 @@ class nwDocAction(Enum):
|
||||
|
||||
class nwDocInsert(Enum):
|
||||
|
||||
NO_INSERT = 0
|
||||
QUOTE_LS = 1
|
||||
QUOTE_RS = 2
|
||||
QUOTE_LD = 3
|
||||
QUOTE_RD = 4
|
||||
NO_INSERT = 0
|
||||
QUOTE_LS = 1
|
||||
QUOTE_RS = 2
|
||||
QUOTE_LD = 3
|
||||
QUOTE_RD = 4
|
||||
|
||||
# END Enum nwDocInsert
|
||||
|
||||
|
||||
+15
-12
@@ -311,13 +311,13 @@ class QSwitch(QAbstractButton):
|
||||
if self.isChecked():
|
||||
trackBrush = qPalette.highlight()
|
||||
thumbBrush = qPalette.highlightedText()
|
||||
textColor = qPalette.highlight().color()
|
||||
thumbText = nwUnicode.U_CHECK
|
||||
textColor = qPalette.highlight().color()
|
||||
thumbText = nwUnicode.U_CHECK
|
||||
else:
|
||||
trackBrush = qPalette.dark()
|
||||
thumbBrush = qPalette.light()
|
||||
textColor = qPalette.dark().color()
|
||||
thumbText = nwUnicode.U_CROSS
|
||||
textColor = qPalette.dark().color()
|
||||
thumbText = nwUnicode.U_CROSS
|
||||
|
||||
if self.isEnabled():
|
||||
trackOpacity = 1.0
|
||||
@@ -325,7 +325,7 @@ class QSwitch(QAbstractButton):
|
||||
trackOpacity = 0.6
|
||||
trackBrush = qPalette.shadow()
|
||||
thumbBrush = qPalette.mid()
|
||||
textColor = qPalette.shadow().color()
|
||||
textColor = qPalette.shadow().color()
|
||||
|
||||
qPaint.setBrush(trackBrush)
|
||||
qPaint.setOpacity(trackOpacity)
|
||||
@@ -379,18 +379,18 @@ class PagedDialog(QDialog):
|
||||
def __init__(self, theParent=None):
|
||||
QDialog.__init__(self, parent=theParent)
|
||||
|
||||
self._outerBox = QVBoxLayout()
|
||||
self._buttonBox = QHBoxLayout()
|
||||
self._tabBox = QTabWidget()
|
||||
|
||||
self._tabBar = VerticalTabBar(self)
|
||||
self._tabBox.setTabBar(self._tabBar)
|
||||
self._tabBox.setTabPosition(QTabWidget.West)
|
||||
self._tabBar.setExpanding(False)
|
||||
|
||||
self._tabBox = QTabWidget()
|
||||
self._tabBox.setTabBar(self._tabBar)
|
||||
self._tabBox.setTabPosition(QTabWidget.West)
|
||||
|
||||
self._buttonBox = QHBoxLayout()
|
||||
|
||||
self._outerBox = QVBoxLayout()
|
||||
self._outerBox.addWidget(self._tabBox)
|
||||
self._outerBox.addLayout(self._buttonBox)
|
||||
self.setLayout(self._outerBox)
|
||||
|
||||
# Default Margins
|
||||
qM = self._outerBox.contentsMargins()
|
||||
@@ -399,11 +399,14 @@ class PagedDialog(QDialog):
|
||||
mT = qM.top()
|
||||
mB = qM.bottom()
|
||||
|
||||
# Set Margins
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self._outerBox.setContentsMargins(0, 0, 0, mB)
|
||||
self._buttonBox.setContentsMargins(mL, 0, mR, 0)
|
||||
self._outerBox.setSpacing(mT)
|
||||
|
||||
self.setLayout(self._outerBox)
|
||||
|
||||
return
|
||||
|
||||
def addTab(self, tabWidget, tabLabel):
|
||||
|
||||
+35
-33
@@ -1090,7 +1090,7 @@ class GuiDocEditor(QTextEdit):
|
||||
# Spell Checking
|
||||
# ==============
|
||||
|
||||
posCursor = self.cursorForPosition(thePos)
|
||||
posCursor = self.cursorForPosition(thePos)
|
||||
spellCheck = self._spellCheck
|
||||
|
||||
if posCursor.block().text().startswith("@"):
|
||||
@@ -1272,7 +1272,7 @@ class GuiDocEditor(QTextEdit):
|
||||
findOpt |= QTextDocument.FindWholeWords
|
||||
|
||||
searchFor = self.docSearch.getSearchObject()
|
||||
wasFound = self.find(searchFor, findOpt)
|
||||
wasFound = self.find(searchFor, findOpt)
|
||||
if not wasFound:
|
||||
if self.docSearch.doNextFile and not goBack:
|
||||
self.theParent.openNextDocument(
|
||||
@@ -1330,7 +1330,7 @@ class GuiDocEditor(QTextEdit):
|
||||
return
|
||||
|
||||
searchFor = self.docSearch.getSearchText()
|
||||
replWith = self.docSearch.getReplaceText()
|
||||
replWith = self.docSearch.getReplaceText()
|
||||
|
||||
if self.docSearch.doMatchCap:
|
||||
replWith = transferCase(theCursor.selectedText(), replWith)
|
||||
@@ -1376,7 +1376,7 @@ class GuiDocEditor(QTextEdit):
|
||||
theCursor = self.textCursor()
|
||||
|
||||
theBlock = theCursor.block()
|
||||
theText = theBlock.text()
|
||||
theText = theBlock.text()
|
||||
|
||||
if len(theText) == 0:
|
||||
return False
|
||||
@@ -1413,16 +1413,16 @@ class GuiDocEditor(QTextEdit):
|
||||
if not theBlock.isValid():
|
||||
return
|
||||
|
||||
theText = theBlock.text()
|
||||
theText = theBlock.text()
|
||||
theCursor = self.textCursor()
|
||||
thePos = theCursor.positionInBlock()
|
||||
theLen = len(theText)
|
||||
thePos = theCursor.positionInBlock()
|
||||
theLen = len(theText)
|
||||
|
||||
if theLen < 1 or thePos-1 > theLen:
|
||||
return
|
||||
|
||||
theOne = theText[thePos-1:thePos]
|
||||
theTwo = theText[thePos-2:thePos]
|
||||
theOne = theText[thePos-1:thePos]
|
||||
theTwo = theText[thePos-2:thePos]
|
||||
theThree = theText[thePos-3:thePos]
|
||||
|
||||
if not theOne: # Makes Neo sad
|
||||
@@ -2028,7 +2028,7 @@ class GuiDocEditSearch(QFrame):
|
||||
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||
self.searchOpt.setIconSize(QSize(tPx, tPx))
|
||||
self.searchOpt.setContentsMargins(0, 0, 0, 0)
|
||||
self.searchOpt.setStyleSheet(r"QToolBar {padding: 0;}")
|
||||
self.searchOpt.setStyleSheet("QToolBar {padding: 0;}")
|
||||
|
||||
self.searchLabel = QLabel(self.tr("Search"))
|
||||
self.searchLabel.setFont(boxFont)
|
||||
@@ -2100,7 +2100,7 @@ class GuiDocEditSearch(QFrame):
|
||||
self.showReplace.setArrowType(Qt.RightArrow)
|
||||
self.showReplace.setCheckable(True)
|
||||
self.showReplace.setToolTip(self.tr("Show/hide the replace text box"))
|
||||
self.showReplace.setStyleSheet(r"QToolButton {border: none; background: transparent;}")
|
||||
self.showReplace.setStyleSheet("QToolButton {border: none; background: transparent;}")
|
||||
self.showReplace.toggled.connect(self._doToggleReplace)
|
||||
|
||||
self.searchButton = QPushButton(self.theTheme.getIcon("search"), "")
|
||||
@@ -2139,9 +2139,9 @@ class GuiDocEditSearch(QFrame):
|
||||
# Construct Box Colours
|
||||
qPalette = self.searchBox.palette()
|
||||
baseCol = qPalette.base().color()
|
||||
rCol = baseCol.redF() + 0.1
|
||||
rCol = baseCol.redF() + 0.1
|
||||
gCol = baseCol.greenF() - 0.1
|
||||
bCol = baseCol.blueF() - 0.1
|
||||
bCol = baseCol.blueF() - 0.1
|
||||
|
||||
mCol = max(rCol, gCol, bCol, 1.0)
|
||||
errCol = QColor()
|
||||
@@ -2161,10 +2161,10 @@ class GuiDocEditSearch(QFrame):
|
||||
def closeSearch(self):
|
||||
"""Close the search box.
|
||||
"""
|
||||
self.mainConf.searchCase = self.isCaseSense
|
||||
self.mainConf.searchWord = self.isWholeWord
|
||||
self.mainConf.searchRegEx = self.isRegEx
|
||||
self.mainConf.searchLoop = self.doLoop
|
||||
self.mainConf.searchCase = self.isCaseSense
|
||||
self.mainConf.searchWord = self.isWholeWord
|
||||
self.mainConf.searchRegEx = self.isRegEx
|
||||
self.mainConf.searchLoop = self.doLoop
|
||||
self.mainConf.searchNextFile = self.doNextFile
|
||||
self.mainConf.searchMatchCap = self.doMatchCap
|
||||
|
||||
@@ -2367,7 +2367,8 @@ class GuiDocEditHeader(QWidget):
|
||||
self.theParent = docEditor.theParent
|
||||
self.theProject = docEditor.theProject
|
||||
self.theTheme = docEditor.theTheme
|
||||
self._docHandle = None
|
||||
|
||||
self._docHandle = None
|
||||
|
||||
fPx = int(0.9*self.theTheme.fontPixelSize)
|
||||
hSp = self.mainConf.pxInt(6)
|
||||
@@ -2591,8 +2592,9 @@ class GuiDocEditFooter(QWidget):
|
||||
self.theProject = docEditor.theProject
|
||||
self.theTheme = docEditor.theTheme
|
||||
self.optState = docEditor.theProject.optState
|
||||
self._docHandle = None
|
||||
self.theItem = None
|
||||
|
||||
self._theItem = None
|
||||
self._docHandle = None
|
||||
|
||||
self.sPx = int(round(0.9*self.theTheme.baseIconSize))
|
||||
fPx = int(0.9*self.theTheme.fontPixelSize)
|
||||
@@ -2708,9 +2710,9 @@ class GuiDocEditFooter(QWidget):
|
||||
self._docHandle = tHandle
|
||||
if self._docHandle is None:
|
||||
logger.verbose("No handle set, so clearing the editor footer")
|
||||
self.theItem = None
|
||||
self._theItem = None
|
||||
else:
|
||||
self.theItem = self.theProject.projTree[self._docHandle]
|
||||
self._theItem = self.theProject.projTree[self._docHandle]
|
||||
|
||||
self.updateInfo()
|
||||
self.updateCounts()
|
||||
@@ -2720,12 +2722,12 @@ class GuiDocEditFooter(QWidget):
|
||||
def updateInfo(self):
|
||||
"""Update the content of text labels.
|
||||
"""
|
||||
if self.theItem is None:
|
||||
if self._theItem is None:
|
||||
sIcon = QPixmap()
|
||||
sText = ""
|
||||
else:
|
||||
iStatus = self.theItem.itemStatus
|
||||
if self.theItem.itemClass == nwItemClass.NOVEL:
|
||||
iStatus = self._theItem.itemStatus
|
||||
if self._theItem.itemClass == nwItemClass.NOVEL:
|
||||
iStatus = self.theProject.statusItems.checkEntry(iStatus)
|
||||
theIcon = self.theParent.statusIcons[iStatus]
|
||||
else:
|
||||
@@ -2733,9 +2735,9 @@ class GuiDocEditFooter(QWidget):
|
||||
theIcon = self.theParent.importIcons[iStatus]
|
||||
|
||||
sIcon = theIcon.pixmap(self.sPx, self.sPx)
|
||||
sClass = trConst(nwLabels.CLASS_NAME[self.theItem.itemClass])
|
||||
sLayout = trConst(nwLabels.LAYOUT_NAME[self.theItem.itemLayout])
|
||||
sText = f"{self.theItem.itemStatus} / {sClass} / {sLayout}"
|
||||
sClass = trConst(nwLabels.CLASS_NAME[self._theItem.itemClass])
|
||||
sLayout = trConst(nwLabels.LAYOUT_NAME[self._theItem.itemLayout])
|
||||
sText = f"{self._theItem.itemStatus} / {sClass} / {sLayout}"
|
||||
|
||||
self.statusIcon.setPixmap(sIcon)
|
||||
self.statusText.setText(sText)
|
||||
@@ -2745,7 +2747,7 @@ class GuiDocEditFooter(QWidget):
|
||||
def updateLineCount(self):
|
||||
"""Update the word count.
|
||||
"""
|
||||
if self.theItem is None:
|
||||
if self._theItem is None:
|
||||
iLine = 0
|
||||
iDist = 0
|
||||
else:
|
||||
@@ -2762,12 +2764,12 @@ class GuiDocEditFooter(QWidget):
|
||||
def updateCounts(self):
|
||||
"""Update the word count.
|
||||
"""
|
||||
if self.theItem is None:
|
||||
if self._theItem is None:
|
||||
wCount = 0
|
||||
wDiff = 0
|
||||
wDiff = 0
|
||||
else:
|
||||
wCount = self.theItem.wordCount
|
||||
wDiff = wCount - self.theItem.initCount
|
||||
wCount = self._theItem.wordCount
|
||||
wDiff = wCount - self._theItem.initCount
|
||||
|
||||
self.wordsText.setText(
|
||||
self.tr("Words: {0} ({1})").format(f"{wCount:n}", f"{wDiff:+n}")
|
||||
|
||||
+2
-6
@@ -60,7 +60,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
self.theProject = theParent.theProject
|
||||
|
||||
# Internal Variables
|
||||
self._docHandle = None
|
||||
self._docHandle = None
|
||||
self._qDocument = self.document()
|
||||
|
||||
# Settings
|
||||
@@ -312,9 +312,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
return
|
||||
|
||||
def updateDocMargins(self):
|
||||
"""Automatically adjust the margins so the text is centred if
|
||||
Config.textFixedW is enabled or we're in Focus Mode. Otherwise,
|
||||
just ensure the margins are set correctly.
|
||||
"""Automatically adjust the margins so the text is centred.
|
||||
"""
|
||||
vBar = self.verticalScrollBar()
|
||||
sW = vBar.width() if vBar.isVisible() else 0
|
||||
@@ -1152,7 +1150,6 @@ class GuiDocViewDetails(QScrollArea):
|
||||
self.theParent = theParent
|
||||
self.theProject = theParent.theProject
|
||||
self.theTheme = theParent.theTheme
|
||||
self.currHandle = None
|
||||
|
||||
self.refList = QLabel("")
|
||||
self.refList.setWordWrap(True)
|
||||
@@ -1185,7 +1182,6 @@ class GuiDocViewDetails(QScrollArea):
|
||||
"""Update the current list of document references from the
|
||||
project index.
|
||||
"""
|
||||
self.currHandle = tHandle
|
||||
if self.theParent.docViewer.stickyRef:
|
||||
return
|
||||
|
||||
|
||||
+16
-18
@@ -47,7 +47,7 @@ class GuiMainMenu(QMenuBar):
|
||||
self.theProject = theParent.theProject
|
||||
|
||||
# Internals
|
||||
self.assistProc = None
|
||||
self._assistProc = None
|
||||
|
||||
# Build Menu
|
||||
self._buildProjectMenu()
|
||||
@@ -92,19 +92,19 @@ class GuiMainMenu(QMenuBar):
|
||||
def closeHelp(self):
|
||||
"""Close the process used for the Qt Assistant, if it is open.
|
||||
"""
|
||||
if self.assistProc is None:
|
||||
if self._assistProc is None:
|
||||
return
|
||||
|
||||
if self.assistProc.state() == QProcess.Starting:
|
||||
if self.assistProc.waitForStarted(10000):
|
||||
self.assistProc.terminate()
|
||||
if self._assistProc.state() == QProcess.Starting:
|
||||
if self._assistProc.waitForStarted(10000):
|
||||
self._assistProc.terminate()
|
||||
else:
|
||||
self.assistProc.kill()
|
||||
self._assistProc.kill()
|
||||
|
||||
elif self.assistProc.state() == QProcess.Running:
|
||||
self.assistProc.terminate()
|
||||
if not self.assistProc.waitForFinished(10000):
|
||||
self.assistProc.kill()
|
||||
elif self._assistProc.state() == QProcess.Running:
|
||||
self._assistProc.terminate()
|
||||
if not self._assistProc.waitForFinished(10000):
|
||||
self._assistProc.kill()
|
||||
|
||||
return
|
||||
|
||||
@@ -134,7 +134,7 @@ class GuiMainMenu(QMenuBar):
|
||||
# Slots
|
||||
##
|
||||
|
||||
def _toggleSpellCheck(self, isChecked=False):
|
||||
def _toggleSpellCheck(self):
|
||||
"""Toggle spell checking. The active status of the spell check
|
||||
flag is handled by the document editor class, so we make no
|
||||
decision, just pass a None to the function and let it decide.
|
||||
@@ -155,10 +155,10 @@ class GuiMainMenu(QMenuBar):
|
||||
self._openWebsite(nw.__docurl__)
|
||||
return False
|
||||
|
||||
self.assistProc = QProcess(self)
|
||||
self.assistProc.start("assistant", ["-collectionFile", self.mainConf.helpPath])
|
||||
self._assistProc = QProcess(self)
|
||||
self._assistProc.start("assistant", ["-collectionFile", self.mainConf.helpPath])
|
||||
|
||||
if not self.assistProc.waitForStarted(10000):
|
||||
if not self._assistProc.waitForStarted(10000):
|
||||
self._openWebsite(nw.__docurl__)
|
||||
return False
|
||||
|
||||
@@ -239,11 +239,9 @@ class GuiMainMenu(QMenuBar):
|
||||
self.rootItems[nwItemClass.ENTITY] = QAction(self.tr("Entity Root"), self.rootMenu)
|
||||
self.rootItems[nwItemClass.CUSTOM] = QAction(self.tr("Custom Root"), self.rootMenu)
|
||||
self.rootItems[nwItemClass.ARCHIVE] = QAction(self.tr("Outtakes Root"), self.rootMenu)
|
||||
nCount = 0
|
||||
for itemClass in self.rootItems.keys():
|
||||
nCount += 1 # This forces the lambdas to be unique
|
||||
for n, itemClass in enumerate(self.rootItems.keys()):
|
||||
self.rootItems[itemClass].triggered.connect(
|
||||
lambda nCount, itemClass=itemClass: self._newTreeItem(nwItemType.ROOT, itemClass)
|
||||
lambda n, itemClass=itemClass: self._newTreeItem(nwItemType.ROOT, itemClass)
|
||||
)
|
||||
self.rootMenu.addAction(self.rootItems[itemClass])
|
||||
|
||||
|
||||
+5
-5
@@ -247,9 +247,9 @@ class GuiNovelTree(QTreeWidget):
|
||||
"""
|
||||
self.clearTree()
|
||||
|
||||
currTitle = None
|
||||
currTitle = None
|
||||
currChapter = None
|
||||
currScene = None
|
||||
currScene = None
|
||||
|
||||
for tKey, tHandle, sTitle, novIdx in self.theIndex.novelStructure(skipExcluded=True):
|
||||
|
||||
@@ -259,9 +259,9 @@ class GuiNovelTree(QTreeWidget):
|
||||
tLevel = novIdx["level"]
|
||||
if tLevel == "H1":
|
||||
self.addTopLevelItem(tItem)
|
||||
currTitle = tItem
|
||||
currTitle = tItem
|
||||
currChapter = None
|
||||
currScene = None
|
||||
currScene = None
|
||||
|
||||
elif tLevel == "H2":
|
||||
if currTitle is None:
|
||||
@@ -269,7 +269,7 @@ class GuiNovelTree(QTreeWidget):
|
||||
else:
|
||||
currTitle.addChild(tItem)
|
||||
currChapter = tItem
|
||||
currScene = None
|
||||
currScene = None
|
||||
|
||||
elif tLevel == "H3":
|
||||
if currChapter is None:
|
||||
|
||||
+88
-86
@@ -28,7 +28,7 @@ import logging
|
||||
|
||||
from time import time
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtCore import Qt, QSize, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView
|
||||
)
|
||||
@@ -94,9 +94,6 @@ class GuiOutline(QTreeWidget):
|
||||
self.optState = theParent.theProject.optState
|
||||
self.headerMenu = GuiOutlineHeaderMenu(self)
|
||||
|
||||
self.firstView = True
|
||||
self.lastBuild = 0
|
||||
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.setExpandsOnDoubleClick(False)
|
||||
@@ -113,16 +110,18 @@ class GuiOutline(QTreeWidget):
|
||||
self.treeHead.customContextMenuRequested.connect(self._headerRightClick)
|
||||
self.treeHead.sectionMoved.connect(self._columnMoved)
|
||||
|
||||
self.treeMap = {}
|
||||
self.treeOrder = []
|
||||
self.colWidth = {}
|
||||
self.colHidden = {}
|
||||
self.colIndex = {}
|
||||
self.treeNCols = 0
|
||||
# Internals
|
||||
self._treeOrder = []
|
||||
self._colWidth = {}
|
||||
self._colHidden = {}
|
||||
self._colIdx = {}
|
||||
self._treeNCols = 0
|
||||
self._firstView = True
|
||||
self._lastBuild = 0
|
||||
|
||||
self.initOutline()
|
||||
self.clearOutline()
|
||||
self.headerMenu.setHiddenState(self.colHidden)
|
||||
self.headerMenu.setHiddenState(self._colHidden)
|
||||
|
||||
logger.debug("GuiOutline initialisation complete")
|
||||
|
||||
@@ -152,18 +151,18 @@ class GuiOutline(QTreeWidget):
|
||||
self.setColumnCount(1)
|
||||
self.setHeaderLabel(trConst(nwLabels.OUTLINE_COLS[nwOutline.TITLE]))
|
||||
|
||||
self.treeOrder = []
|
||||
self.colWidth = {}
|
||||
self.colHidden = {}
|
||||
self.colIndex = {}
|
||||
self.treeNCols = 0
|
||||
self._treeOrder = []
|
||||
self._colWidth = {}
|
||||
self._colHidden = {}
|
||||
self._colIdx = {}
|
||||
self._treeNCols = 0
|
||||
|
||||
for i, hItem in enumerate(nwOutline):
|
||||
self.treeOrder.append(hItem)
|
||||
self.colWidth[hItem] = self.DEF_WIDTH[hItem]
|
||||
self.colHidden[hItem] = self.DEF_HIDDEN[hItem]
|
||||
for hItem in nwOutline:
|
||||
self._treeOrder.append(hItem)
|
||||
self._colWidth[hItem] = self.DEF_WIDTH[hItem]
|
||||
self._colHidden[hItem] = self.DEF_HIDDEN[hItem]
|
||||
|
||||
self.treeNCols = len(self.treeOrder)
|
||||
self._treeNCols = len(self._treeOrder)
|
||||
|
||||
return
|
||||
|
||||
@@ -173,15 +172,15 @@ class GuiOutline(QTreeWidget):
|
||||
tree.
|
||||
"""
|
||||
# If it's the first time, we always build
|
||||
if self.firstView or self.firstView and overRide:
|
||||
if self._firstView or self._firstView and overRide:
|
||||
self._loadHeaderState()
|
||||
self._populateTree()
|
||||
self.firstView = False
|
||||
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.theIndex.novelChangedSince(self.lastBuild)
|
||||
indexChanged = self.theIndex.novelChangedSince(self._lastBuild)
|
||||
doBuild = (novelChanged or indexChanged) and self.theProject.autoOutline
|
||||
if doBuild or overRide:
|
||||
logger.debug("Rebuilding Project Outline")
|
||||
@@ -194,21 +193,22 @@ class GuiOutline(QTreeWidget):
|
||||
"""
|
||||
self._saveHeaderState()
|
||||
self.clearOutline()
|
||||
self.firstView = True
|
||||
self._firstView = True
|
||||
return
|
||||
|
||||
##
|
||||
# Slots
|
||||
##
|
||||
|
||||
@pyqtSlot("QTreeWidgetItem*", int)
|
||||
def _treeDoubleClick(self, tItem, tCol):
|
||||
"""Extract the handle and line number of the title double-
|
||||
clicked, and send it to the main gui class for opening in the
|
||||
document editor.
|
||||
"""
|
||||
tHandle = tItem.data(self.colIndex[nwOutline.TITLE], Qt.UserRole)
|
||||
tHandle = tItem.data(self._colIdx[nwOutline.TITLE], Qt.UserRole)
|
||||
try:
|
||||
tLine = int(tItem.text(self.colIndex[nwOutline.LINE]))
|
||||
tLine = int(tItem.text(self._colIdx[nwOutline.LINE]))
|
||||
except Exception:
|
||||
tLine = 1
|
||||
|
||||
@@ -217,30 +217,33 @@ class GuiOutline(QTreeWidget):
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _itemSelected(self):
|
||||
"""Extract the handle and line number of the currently selected
|
||||
title, and send it to the details panel.
|
||||
"""
|
||||
selItems = self.selectedItems()
|
||||
if selItems:
|
||||
tHandle = selItems[0].data(self.colIndex[nwOutline.TITLE], Qt.UserRole)
|
||||
sTitle = selItems[0].data(self.colIndex[nwOutline.LINE], Qt.UserRole)
|
||||
tHandle = selItems[0].data(self._colIdx[nwOutline.TITLE], Qt.UserRole)
|
||||
sTitle = selItems[0].data(self._colIdx[nwOutline.LINE], Qt.UserRole)
|
||||
self.theParent.projMeta.showItem(tHandle, sTitle)
|
||||
self.theParent.treeView.setSelectedHandle(tHandle)
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot("QPoint")
|
||||
def _headerRightClick(self, clickPos):
|
||||
"""Show the header column menu.
|
||||
"""
|
||||
self.headerMenu.exec_(self.mapToGlobal(clickPos))
|
||||
return
|
||||
|
||||
@pyqtSlot(int, int, int)
|
||||
def _columnMoved(self, logIdx, oldVisualIdx, newVisualIdx):
|
||||
"""Make sure the order array is up to date with the actual order
|
||||
of the columns.
|
||||
"""
|
||||
self.treeOrder.insert(newVisualIdx, self.treeOrder.pop(oldVisualIdx))
|
||||
self._treeOrder.insert(newVisualIdx, self._treeOrder.pop(oldVisualIdx))
|
||||
self._saveHeaderState()
|
||||
return
|
||||
|
||||
@@ -249,8 +252,8 @@ class GuiOutline(QTreeWidget):
|
||||
header context menu.
|
||||
"""
|
||||
logger.verbose("User toggled Outline column '%s'", theItem.name)
|
||||
if theItem in self.colIndex:
|
||||
self.setColumnHidden(self.colIndex[theItem], not isChecked)
|
||||
if theItem in self._colIdx:
|
||||
self.setColumnHidden(self._colIdx[theItem], not isChecked)
|
||||
self._saveHeaderState()
|
||||
|
||||
return
|
||||
@@ -281,29 +284,29 @@ class GuiOutline(QTreeWidget):
|
||||
|
||||
# Check that we now have a complete list, and only if so, save
|
||||
# the order loaded from file. Otherwise, we keep the default.
|
||||
if len(treeOrder) == self.treeNCols:
|
||||
self.treeOrder = treeOrder
|
||||
if len(treeOrder) == self._treeNCols:
|
||||
self._treeOrder = treeOrder
|
||||
else:
|
||||
logger.error("Failed to extract outline column order from previous session")
|
||||
logger.error("Column count doesn't match %d != %d", len(treeOrder), self.treeNCols)
|
||||
logger.error("Column count doesn't match %d != %d", len(treeOrder), self._treeNCols)
|
||||
|
||||
# We load whatever column widths and hidden states we find in
|
||||
# the file, and leave the rest in their default state.
|
||||
tmpWidth = self.optState.getValue("GuiOutline", "columnWidth", {})
|
||||
for hName in tmpWidth:
|
||||
try:
|
||||
self.colWidth[nwOutline[hName]] = self.mainConf.pxInt(tmpWidth[hName])
|
||||
self._colWidth[nwOutline[hName]] = self.mainConf.pxInt(tmpWidth[hName])
|
||||
except Exception:
|
||||
logger.warning("Ignored unknown outline column '%s'", str(hName))
|
||||
|
||||
tmpHidden = self.optState.getValue("GuiOutline", "columnHidden", {})
|
||||
for hName in tmpHidden:
|
||||
try:
|
||||
self.colHidden[nwOutline[hName]] = tmpHidden[hName]
|
||||
self._colHidden[nwOutline[hName]] = tmpHidden[hName]
|
||||
except Exception:
|
||||
logger.warning("Ignored unknown outline column '%s'", str(hName))
|
||||
|
||||
self.headerMenu.setHiddenState(self.colHidden)
|
||||
self.headerMenu.setHiddenState(self._colHidden)
|
||||
|
||||
return
|
||||
|
||||
@@ -314,19 +317,19 @@ class GuiOutline(QTreeWidget):
|
||||
the last known width in case they're unhidden again.
|
||||
"""
|
||||
# If we haven't built the tree, there is nothing to save.
|
||||
if self.lastBuild == 0:
|
||||
if self._lastBuild == 0:
|
||||
return
|
||||
|
||||
treeOrder = []
|
||||
colWidth = {}
|
||||
colWidth = {}
|
||||
colHidden = {}
|
||||
|
||||
for hItem in nwOutline:
|
||||
colWidth[hItem.name] = self.mainConf.rpxInt(self.colWidth[hItem])
|
||||
colHidden[hItem.name] = self.colHidden[hItem]
|
||||
colWidth[hItem.name] = self.mainConf.rpxInt(self._colWidth[hItem])
|
||||
colHidden[hItem.name] = self._colHidden[hItem]
|
||||
|
||||
for iCol in range(self.columnCount()):
|
||||
hName = self.treeOrder[iCol].name
|
||||
hName = self._treeOrder[iCol].name
|
||||
treeOrder.append(hName)
|
||||
|
||||
iLog = self.treeHead.logicalIndex(iCol)
|
||||
@@ -353,41 +356,40 @@ class GuiOutline(QTreeWidget):
|
||||
"""
|
||||
self.clear()
|
||||
|
||||
if self.firstView:
|
||||
if self._firstView:
|
||||
theLabels = []
|
||||
for i, hItem in enumerate(self.treeOrder):
|
||||
for i, hItem in enumerate(self._treeOrder):
|
||||
theLabels.append(trConst(nwLabels.OUTLINE_COLS[hItem]))
|
||||
self.colIndex[hItem] = i
|
||||
self._colIdx[hItem] = i
|
||||
|
||||
self.setHeaderLabels(theLabels)
|
||||
for hItem in self.treeOrder:
|
||||
self.setColumnWidth(self.colIndex[hItem], self.colWidth[hItem])
|
||||
self.setColumnHidden(self.colIndex[hItem], self.colHidden[hItem])
|
||||
for hItem in self._treeOrder:
|
||||
self.setColumnWidth(self._colIdx[hItem], self._colWidth[hItem])
|
||||
self.setColumnHidden(self._colIdx[hItem], self._colHidden[hItem])
|
||||
|
||||
# Make sure title column is always visible,
|
||||
# and handle column always hidden
|
||||
self.setColumnHidden(self.colIndex[nwOutline.TITLE], False)
|
||||
self.setColumnHidden(self._colIdx[nwOutline.TITLE], False)
|
||||
|
||||
headItem = self.headerItem()
|
||||
headItem.setTextAlignment(self.colIndex[nwOutline.CCOUNT], Qt.AlignRight)
|
||||
headItem.setTextAlignment(self.colIndex[nwOutline.WCOUNT], Qt.AlignRight)
|
||||
headItem.setTextAlignment(self.colIndex[nwOutline.PCOUNT], Qt.AlignRight)
|
||||
headItem.setTextAlignment(self._colIdx[nwOutline.CCOUNT], Qt.AlignRight)
|
||||
headItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight)
|
||||
headItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight)
|
||||
|
||||
currTitle = None
|
||||
currTitle = None
|
||||
currChapter = None
|
||||
currScene = None
|
||||
currScene = None
|
||||
|
||||
for tKey, tHandle, sTitle, novIdx in self.theIndex.novelStructure(skipExcluded=True):
|
||||
|
||||
tItem = self._createTreeItem(tHandle, sTitle, novIdx)
|
||||
self.treeMap[tKey] = tItem
|
||||
|
||||
tLevel = novIdx["level"]
|
||||
if tLevel == "H1":
|
||||
self.addTopLevelItem(tItem)
|
||||
currTitle = tItem
|
||||
currTitle = tItem
|
||||
currChapter = None
|
||||
currScene = None
|
||||
currScene = None
|
||||
|
||||
elif tLevel == "H2":
|
||||
if currTitle is None:
|
||||
@@ -395,7 +397,7 @@ class GuiOutline(QTreeWidget):
|
||||
else:
|
||||
currTitle.addChild(tItem)
|
||||
currChapter = tItem
|
||||
currScene = None
|
||||
currScene = None
|
||||
|
||||
elif tLevel == "H3":
|
||||
if currChapter is None:
|
||||
@@ -421,47 +423,47 @@ class GuiOutline(QTreeWidget):
|
||||
|
||||
tItem.setExpanded(True)
|
||||
|
||||
self.lastBuild = time()
|
||||
self._lastBuild = time()
|
||||
|
||||
return
|
||||
|
||||
def _createTreeItem(self, tHandle, sTitle, novIdx):
|
||||
"""Populate a tree item with all the column values.
|
||||
"""
|
||||
nwItem = self.theProject.projTree[tHandle]
|
||||
nwItem = self.theProject.projTree[tHandle]
|
||||
newItem = QTreeWidgetItem()
|
||||
hIcon = "doc_%s" % novIdx["level"].lower()
|
||||
hIcon = "doc_%s" % novIdx["level"].lower()
|
||||
|
||||
cC = int(novIdx["cCount"])
|
||||
wC = int(novIdx["wCount"])
|
||||
pC = int(novIdx["pCount"])
|
||||
|
||||
newItem.setText(self.colIndex[nwOutline.TITLE], novIdx["title"])
|
||||
newItem.setData(self.colIndex[nwOutline.TITLE], Qt.UserRole, tHandle)
|
||||
newItem.setIcon(self.colIndex[nwOutline.TITLE], self.theTheme.getIcon(hIcon))
|
||||
newItem.setText(self.colIndex[nwOutline.LEVEL], novIdx["level"])
|
||||
newItem.setText(self.colIndex[nwOutline.LABEL], nwItem.itemName)
|
||||
newItem.setIcon(self.colIndex[nwOutline.LABEL], self.theTheme.getIcon("proj_document"))
|
||||
newItem.setText(self.colIndex[nwOutline.LINE], sTitle[1:].lstrip("0"))
|
||||
newItem.setData(self.colIndex[nwOutline.LINE], Qt.UserRole, sTitle)
|
||||
newItem.setText(self.colIndex[nwOutline.SYNOP], novIdx["synopsis"])
|
||||
newItem.setText(self.colIndex[nwOutline.CCOUNT], f"{cC:n}")
|
||||
newItem.setText(self.colIndex[nwOutline.WCOUNT], f"{wC:n}")
|
||||
newItem.setText(self.colIndex[nwOutline.PCOUNT], f"{pC:n}")
|
||||
newItem.setTextAlignment(self.colIndex[nwOutline.CCOUNT], Qt.AlignRight)
|
||||
newItem.setTextAlignment(self.colIndex[nwOutline.WCOUNT], Qt.AlignRight)
|
||||
newItem.setTextAlignment(self.colIndex[nwOutline.PCOUNT], Qt.AlignRight)
|
||||
newItem.setText(self._colIdx[nwOutline.TITLE], novIdx["title"])
|
||||
newItem.setData(self._colIdx[nwOutline.TITLE], Qt.UserRole, tHandle)
|
||||
newItem.setIcon(self._colIdx[nwOutline.TITLE], self.theTheme.getIcon(hIcon))
|
||||
newItem.setText(self._colIdx[nwOutline.LEVEL], novIdx["level"])
|
||||
newItem.setText(self._colIdx[nwOutline.LABEL], nwItem.itemName)
|
||||
newItem.setIcon(self._colIdx[nwOutline.LABEL], self.theTheme.getIcon("proj_document"))
|
||||
newItem.setText(self._colIdx[nwOutline.LINE], sTitle[1:].lstrip("0"))
|
||||
newItem.setData(self._colIdx[nwOutline.LINE], Qt.UserRole, sTitle)
|
||||
newItem.setText(self._colIdx[nwOutline.SYNOP], novIdx["synopsis"])
|
||||
newItem.setText(self._colIdx[nwOutline.CCOUNT], f"{cC:n}")
|
||||
newItem.setText(self._colIdx[nwOutline.WCOUNT], f"{wC:n}")
|
||||
newItem.setText(self._colIdx[nwOutline.PCOUNT], f"{pC:n}")
|
||||
newItem.setTextAlignment(self._colIdx[nwOutline.CCOUNT], Qt.AlignRight)
|
||||
newItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight)
|
||||
newItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight)
|
||||
|
||||
theRefs = self.theIndex.getReferences(tHandle, sTitle)
|
||||
newItem.setText(self.colIndex[nwOutline.POV], ", ".join(theRefs[nwKeyWords.POV_KEY]))
|
||||
newItem.setText(self.colIndex[nwOutline.FOCUS], ", ".join(theRefs[nwKeyWords.FOCUS_KEY]))
|
||||
newItem.setText(self.colIndex[nwOutline.CHAR], ", ".join(theRefs[nwKeyWords.CHAR_KEY]))
|
||||
newItem.setText(self.colIndex[nwOutline.PLOT], ", ".join(theRefs[nwKeyWords.PLOT_KEY]))
|
||||
newItem.setText(self.colIndex[nwOutline.TIME], ", ".join(theRefs[nwKeyWords.TIME_KEY]))
|
||||
newItem.setText(self.colIndex[nwOutline.WORLD], ", ".join(theRefs[nwKeyWords.WORLD_KEY]))
|
||||
newItem.setText(self.colIndex[nwOutline.OBJECT], ", ".join(theRefs[nwKeyWords.OBJECT_KEY]))
|
||||
newItem.setText(self.colIndex[nwOutline.ENTITY], ", ".join(theRefs[nwKeyWords.ENTITY_KEY]))
|
||||
newItem.setText(self.colIndex[nwOutline.CUSTOM], ", ".join(theRefs[nwKeyWords.CUSTOM_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.POV], ", ".join(theRefs[nwKeyWords.POV_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.FOCUS], ", ".join(theRefs[nwKeyWords.FOCUS_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.CHAR], ", ".join(theRefs[nwKeyWords.CHAR_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.PLOT], ", ".join(theRefs[nwKeyWords.PLOT_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.TIME], ", ".join(theRefs[nwKeyWords.TIME_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.WORLD], ", ".join(theRefs[nwKeyWords.WORLD_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.OBJECT], ", ".join(theRefs[nwKeyWords.OBJECT_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.ENTITY], ", ".join(theRefs[nwKeyWords.ENTITY_KEY]))
|
||||
newItem.setText(self._colIdx[nwOutline.CUSTOM], ", ".join(theRefs[nwKeyWords.CUSTOM_KEY]))
|
||||
|
||||
return newItem
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ class GuiOutlineDetails(QScrollArea):
|
||||
|
||||
# Selected Item Tags
|
||||
self.tagsGroup = QGroupBox(self.tr("Reference Tags"), self)
|
||||
self.tagsForm = QGridLayout()
|
||||
self.tagsForm = QGridLayout()
|
||||
self.tagsGroup.setLayout(self.tagsForm)
|
||||
|
||||
self.tagsForm.addWidget(self.povKeyLabel, 0, 0, 1, 1, Qt.AlignTop | Qt.AlignLeft)
|
||||
@@ -279,8 +279,8 @@ class GuiOutlineDetails(QScrollArea):
|
||||
"""Update the content of the tree with the given handle and line
|
||||
number pointing to a header.
|
||||
"""
|
||||
nwItem = self.theProject.projTree[tHandle]
|
||||
novIdx = self.theIndex.getNovelData(tHandle, sTitle)
|
||||
nwItem = self.theProject.projTree[tHandle]
|
||||
novIdx = self.theIndex.getNovelData(tHandle, sTitle)
|
||||
theRefs = self.theIndex.getReferences(tHandle, sTitle)
|
||||
if nwItem is None or novIdx is None:
|
||||
return False
|
||||
|
||||
@@ -24,8 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import math
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QFont
|
||||
@@ -66,10 +66,10 @@ class GuiProjectDetails(PagedDialog):
|
||||
self.mainConf.pxInt(self.optState.getInt("GuiProjectDetails", "winHeight", wH))
|
||||
)
|
||||
|
||||
self.tabMain = GuiProjectDetailsMain(self.theParent, self.theProject)
|
||||
self.tabMain = GuiProjectDetailsMain(self.theParent, self.theProject)
|
||||
self.tabContents = GuiProjectDetailsContents(self.theParent, self.theProject)
|
||||
|
||||
self.addTab(self.tabMain, self.tr("Overview"))
|
||||
self.addTab(self.tabMain, self.tr("Overview"))
|
||||
self.addTab(self.tabContents, self.tr("Contents"))
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
|
||||
@@ -413,8 +413,8 @@ class GuiProjectDetailsContents(QWidget):
|
||||
"""Set the content of the chapter/page tree.
|
||||
"""
|
||||
dblPages = self.dblValue.isChecked()
|
||||
wpPage = self.wpValue.value()
|
||||
fstPage = self.poValue.value() - 1
|
||||
wpPage = self.wpValue.value()
|
||||
fstPage = self.poValue.value() - 1
|
||||
|
||||
pTotal = 0
|
||||
tPages = 1
|
||||
|
||||
+2
-2
@@ -132,7 +132,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
self.initTree()
|
||||
|
||||
# Internal Function Mapping
|
||||
self.makeAlert = self.theParent.makeAlert
|
||||
self.makeAlert = self.theParent.makeAlert
|
||||
self.askQuestion = self.theParent.askQuestion
|
||||
|
||||
logger.debug("GuiProjectTree initialisation complete")
|
||||
@@ -1167,7 +1167,7 @@ class GuiProjectTreeMenu(QMenu):
|
||||
|
||||
inTrash = theItem.itemParent == trashHandle and trashHandle is not None
|
||||
isTrash = theItem.itemHandle == trashHandle and trashHandle is not None
|
||||
isFile = theItem.itemType == nwItemType.FILE
|
||||
isFile = theItem.itemType == nwItemType.FILE
|
||||
|
||||
allowNew = not (isTrash or inTrash)
|
||||
|
||||
|
||||
+4
-4
@@ -51,8 +51,8 @@ class GuiMainStatus(QStatusBar):
|
||||
self.refTime = None
|
||||
self.userIdle = False
|
||||
|
||||
colNone = QColor(*self.theTheme.statNone)
|
||||
colTrue = QColor(*self.theTheme.statUnsaved)
|
||||
colNone = QColor(*self.theTheme.statNone)
|
||||
colTrue = QColor(*self.theTheme.statUnsaved)
|
||||
colFalse = QColor(*self.theTheme.statSaved)
|
||||
|
||||
iPx = self.theTheme.baseIconSize
|
||||
@@ -243,8 +243,8 @@ class StatusLED(QAbstractButton):
|
||||
|
||||
self._colNone = colNone
|
||||
self._colGood = colGood
|
||||
self._colBad = colBad
|
||||
self._theCol = colNone
|
||||
self._colBad = colBad
|
||||
self._theCol = colNone
|
||||
|
||||
self.setFixedWidth(sW)
|
||||
self.setFixedHeight(sH)
|
||||
|
||||
+9
-9
@@ -25,9 +25,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import os
|
||||
import logging
|
||||
import configparser
|
||||
import os
|
||||
|
||||
from math import ceil
|
||||
from functools import partial
|
||||
@@ -272,7 +272,7 @@ class GuiTheme:
|
||||
cssData = ""
|
||||
try:
|
||||
if os.path.isfile(self.cssFile):
|
||||
with open(self.cssFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(self.cssFile, mode="r", encoding="utf-8") as inFile:
|
||||
cssData = inFile.read()
|
||||
except Exception:
|
||||
logger.error("Could not load theme css file")
|
||||
@@ -282,7 +282,7 @@ class GuiTheme:
|
||||
# Config File
|
||||
confParser = configparser.ConfigParser()
|
||||
try:
|
||||
with open(self.confFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(self.confFile, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
except Exception:
|
||||
logger.error("Could not load theme settings from: %s", self.confFile)
|
||||
@@ -340,7 +340,7 @@ class GuiTheme:
|
||||
|
||||
confParser = configparser.ConfigParser()
|
||||
try:
|
||||
with open(self.syntaxFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(self.syntaxFile, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
except Exception:
|
||||
logger.error("Could not load syntax colours from: %s", self.syntaxFile)
|
||||
@@ -395,7 +395,7 @@ class GuiTheme:
|
||||
)
|
||||
logger.verbose("Checking theme config for '%s'", themeDir)
|
||||
try:
|
||||
with open(themeConf, mode="r", encoding="utf8") as inFile:
|
||||
with open(themeConf, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
except Exception as e:
|
||||
self.makeAlert([
|
||||
@@ -421,14 +421,14 @@ class GuiTheme:
|
||||
return self.syntaxList
|
||||
|
||||
confParser = configparser.ConfigParser()
|
||||
syntaxDir = os.path.join(self.mainConf.themeRoot, self.syntaxPath)
|
||||
syntaxDir = os.path.join(self.mainConf.themeRoot, self.syntaxPath)
|
||||
for syntaxFile in os.listdir(syntaxDir):
|
||||
syntaxPath = os.path.join(syntaxDir, syntaxFile)
|
||||
if not os.path.isfile(syntaxPath):
|
||||
continue
|
||||
logger.verbose("Checking theme syntax for '%s'", syntaxFile)
|
||||
try:
|
||||
with open(syntaxPath, mode="r", encoding="utf8") as inFile:
|
||||
with open(syntaxPath, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
except Exception as e:
|
||||
self.makeAlert([
|
||||
@@ -646,7 +646,7 @@ class GuiIcons:
|
||||
# Config File
|
||||
confParser = configparser.ConfigParser()
|
||||
try:
|
||||
with open(self.confFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(self.confFile, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
except Exception:
|
||||
logger.error("Could not load icon theme settings from: %s", self.confFile)
|
||||
@@ -744,7 +744,7 @@ class GuiIcons:
|
||||
themeConf = os.path.join(themePath, self.confName)
|
||||
logger.verbose("Checking icon theme config for '%s'", themeDir)
|
||||
try:
|
||||
with open(themeConf, mode="r", encoding="utf8") as inFile:
|
||||
with open(themeConf, mode="r", encoding="utf-8") as inFile:
|
||||
confParser.read_file(inFile)
|
||||
except Exception as e:
|
||||
self.makeAlert([
|
||||
|
||||
+26
-27
@@ -24,11 +24,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import os
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
from time import time
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer, QSize, QThreadPool, pyqtSlot
|
||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QKeySequence, QCursor
|
||||
@@ -71,15 +71,12 @@ class GuiMain(QMainWindow):
|
||||
logger.info("OS: %s", self.mainConf.osType)
|
||||
logger.info("Kernel: %s", self.mainConf.kernelVer)
|
||||
logger.info("Host: %s", self.mainConf.hostName)
|
||||
logger.info(
|
||||
"Qt5 Version: %s (%d)", self.mainConf.verQtString, self.mainConf.verQtValue
|
||||
)
|
||||
logger.info(
|
||||
"PyQt5 Version: %s (%d)", self.mainConf.verPyQtString, self.mainConf.verPyQtValue
|
||||
)
|
||||
logger.info(
|
||||
"Python Version: %s (0x%x)", self.mainConf.verPyString, self.mainConf.verPyHexVal
|
||||
)
|
||||
logger.info("Qt5 Version: %s (%d)",
|
||||
self.mainConf.verQtString, self.mainConf.verQtValue)
|
||||
logger.info("PyQt5 Version: %s (%d)",
|
||||
self.mainConf.verPyQtString, self.mainConf.verPyQtValue)
|
||||
logger.info("Python Version: %s (0x%x)",
|
||||
self.mainConf.verPyString, self.mainConf.verPyHexVal)
|
||||
logger.info("GUI Language: %s", self.mainConf.guiLang)
|
||||
|
||||
# Core Classes
|
||||
@@ -137,7 +134,7 @@ class GuiMain(QMainWindow):
|
||||
# Project Tree Tabs
|
||||
self.projTabs = QTabWidget()
|
||||
self.projTabs.setTabPosition(QTabWidget.South)
|
||||
self.projTabs.setStyleSheet(r"QTabWidget::pane {border: 0;};")
|
||||
self.projTabs.setStyleSheet("QTabWidget::pane {border: 0;};")
|
||||
self.projTabs.addTab(self.treeView, self.tr("Project"))
|
||||
self.projTabs.addTab(self.novelView, self.tr("Novel"))
|
||||
self.projTabs.currentChanged.connect(self._projTabsChanged)
|
||||
@@ -152,7 +149,7 @@ class GuiMain(QMainWindow):
|
||||
self.treeButtons.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||
self.treeButtons.setIconSize(QSize(btnSize, btnSize))
|
||||
self.treeButtons.setContentsMargins(0, 0, 0, 0)
|
||||
self.treeButtons.setStyleSheet(r"QToolBar {padding: 0;}")
|
||||
self.treeButtons.setStyleSheet("QToolBar {padding: 0;}")
|
||||
self.projTabs.setCornerWidget(self.treeButtons, Qt.BottomRightCorner)
|
||||
|
||||
self.projDetailsBtn = QAction(self.tr("Project Details"))
|
||||
@@ -199,7 +196,7 @@ class GuiMain(QMainWindow):
|
||||
# Main Tabs : Editor / Outline
|
||||
self.mainTabs = QTabWidget()
|
||||
self.mainTabs.setTabPosition(QTabWidget.East)
|
||||
self.mainTabs.setStyleSheet(r"QTabWidget::pane {border: 0;}")
|
||||
self.mainTabs.setStyleSheet("QTabWidget::pane {border: 0;}")
|
||||
self.mainTabs.addTab(self.splitDocs, self.tr("Editor"))
|
||||
self.mainTabs.addTab(self.splitOutline, self.tr("Outline"))
|
||||
self.mainTabs.currentChanged.connect(self._mainTabChanged)
|
||||
@@ -415,7 +412,7 @@ class GuiMain(QMainWindow):
|
||||
self.saveDocument()
|
||||
|
||||
if self.theProject.projAltered:
|
||||
saveOK = self.saveProject()
|
||||
saveOK = self.saveProject()
|
||||
doBackup = False
|
||||
if self.theProject.doBackup and self.mainConf.backupOnClose:
|
||||
doBackup = True
|
||||
@@ -438,7 +435,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
self.theProject.closeProject(self.idleTime)
|
||||
self.idleRefTime = time()
|
||||
self.idleTime = 0.0
|
||||
self.idleTime = 0.0
|
||||
|
||||
self.theIndex.clearIndex()
|
||||
self.clearGUI()
|
||||
@@ -490,14 +487,16 @@ class GuiMain(QMainWindow):
|
||||
self, self.tr("Project Locked"),
|
||||
"%s<br><br>%s<br>%s" % (
|
||||
self.tr(
|
||||
"The project is already open by another instance of novelWriter, and "
|
||||
"is therefore locked. Override lock and continue anyway?"
|
||||
"The project is already open by another instance of "
|
||||
"novelWriter, and is therefore locked. Override lock "
|
||||
"and continue anyway?"
|
||||
),
|
||||
self.tr(
|
||||
"Note: If the program or the computer previously crashed, the lock "
|
||||
"can safely be overridden. If, however, another instance of "
|
||||
"novelWriter has the project open, overriding the lock may corrupt "
|
||||
"the project, and is not recommended."
|
||||
"Note: If the program or the computer previously "
|
||||
"crashed, the lock can safely be overridden. If, "
|
||||
"however, another instance of novelWriter has the "
|
||||
"project open, overriding the lock may corrupt the "
|
||||
"project, and is not recommended."
|
||||
),
|
||||
lockDetails
|
||||
),
|
||||
@@ -510,9 +509,9 @@ class GuiMain(QMainWindow):
|
||||
return False
|
||||
|
||||
# Project is loaded
|
||||
self.hasProject = True
|
||||
self.hasProject = True
|
||||
self.idleRefTime = time()
|
||||
self.idleTime = 0.0
|
||||
self.idleTime = 0.0
|
||||
|
||||
# Load the tag index
|
||||
self.theIndex.loadIndex()
|
||||
@@ -721,7 +720,7 @@ class GuiMain(QMainWindow):
|
||||
|
||||
theText = None
|
||||
try:
|
||||
with open(loadFile, mode="rt", encoding="utf8") as inFile:
|
||||
with open(loadFile, mode="rt", encoding="utf-8") as inFile:
|
||||
theText = inFile.read()
|
||||
self.mainConf.setLastPath(loadFile)
|
||||
except Exception as e:
|
||||
@@ -740,8 +739,8 @@ class GuiMain(QMainWindow):
|
||||
msgYes = self.askQuestion(
|
||||
self.tr("Import Document"),
|
||||
self.tr(
|
||||
"Importing the file will overwrite the current content of the document. "
|
||||
"Do you want to proceed?"
|
||||
"Importing the file will overwrite the current content of "
|
||||
"the document. Do you want to proceed?"
|
||||
)
|
||||
)
|
||||
if not msgYes:
|
||||
|
||||
+23
-27
@@ -24,24 +24,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
|
||||
from PyQt5.QtGui import (
|
||||
QPalette, QColor, QFont, QCursor, QFontInfo
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel,
|
||||
QLineEdit, QGroupBox, QGridLayout, QProgressBar, QMenu, QAction,
|
||||
QFileDialog, QFontDialog, QSpinBox, QScrollArea, QSplitter, QWidget,
|
||||
QSizePolicy, QDoubleSpinBox, QComboBox
|
||||
)
|
||||
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
|
||||
|
||||
from nw.core import ToHtml, ToOdt, ToMarkdown
|
||||
from nw.enum import nwAlert, nwItemType, nwItemLayout, nwItemClass
|
||||
@@ -55,14 +55,12 @@ logger = logging.getLogger(__name__)
|
||||
class GuiBuildNovel(QDialog):
|
||||
|
||||
FMT_PDF = 1 # Print to PDF
|
||||
|
||||
FMT_ODT = 2 # Open Document file
|
||||
FMT_FODT = 3 # Flat Open Document file
|
||||
FMT_HTM = 4 # HTML5
|
||||
FMT_NWD = 5 # nW Markdown
|
||||
FMT_MD = 6 # Standard Markdown
|
||||
FMT_GH = 7 # GitHub Markdown
|
||||
|
||||
FMT_JSON_H = 8 # HTML5 wrapped in JSON
|
||||
FMT_JSON_M = 9 # nW Markdown wrapped in JSON
|
||||
|
||||
@@ -560,8 +558,8 @@ class GuiBuildNovel(QDialog):
|
||||
"""Load the previously generated document from cache.
|
||||
"""
|
||||
if self._loadCache():
|
||||
textFont = self.textFont.text()
|
||||
textSize = self.textSize.value()
|
||||
textFont = self.textFont.text()
|
||||
textSize = self.textSize.value()
|
||||
justifyText = self.justifyText.isChecked()
|
||||
self.docView.setTextFont(textFont, textSize)
|
||||
self.docView.setJustify(justifyText)
|
||||
@@ -596,14 +594,14 @@ class GuiBuildNovel(QDialog):
|
||||
"""
|
||||
# Get Settings
|
||||
justifyText = self.justifyText.isChecked()
|
||||
noStyling = self.noStyling.isChecked()
|
||||
textFont = self.textFont.text()
|
||||
textSize = self.textSize.value()
|
||||
noStyling = self.noStyling.isChecked()
|
||||
textFont = self.textFont.text()
|
||||
textSize = self.textSize.value()
|
||||
replaceTabs = self.replaceTabs.isChecked()
|
||||
|
||||
self.htmlText = []
|
||||
self.htmlText = []
|
||||
self.htmlStyle = []
|
||||
self.htmlSize = 0
|
||||
self.htmlSize = 0
|
||||
|
||||
# Build Preview
|
||||
# =============
|
||||
@@ -672,7 +670,7 @@ class GuiBuildNovel(QDialog):
|
||||
textFixed = fontInfo.fixedPitch()
|
||||
|
||||
isHtml = isinstance(bldObj, ToHtml)
|
||||
isOdt = isinstance(bldObj, ToOdt)
|
||||
isOdt = isinstance(bldObj, ToOdt)
|
||||
|
||||
bldObj.setTitleFormat(fmtTitle)
|
||||
bldObj.setChapterFormat(fmtChapter)
|
||||
@@ -706,7 +704,7 @@ class GuiBuildNovel(QDialog):
|
||||
|
||||
for nItt, tItem in enumerate(self.theProject.projTree):
|
||||
|
||||
noteRoot = noteFiles
|
||||
noteRoot = noteFiles
|
||||
noteRoot &= tItem.itemType == nwItemType.ROOT
|
||||
noteRoot &= tItem.itemClass != nwItemClass.NOVEL
|
||||
noteRoot &= tItem.itemClass != nwItemClass.ARCHIVE
|
||||
@@ -848,12 +846,12 @@ class GuiBuildNovel(QDialog):
|
||||
# ==================
|
||||
|
||||
cleanName = makeFileNameSafe(self.theProject.projName)
|
||||
fileName = "%s.%s" % (cleanName, fileExt)
|
||||
saveDir = self.mainConf.lastPath
|
||||
fileName = "%s.%s" % (cleanName, fileExt)
|
||||
saveDir = self.mainConf.lastPath
|
||||
if not os.path.isdir(saveDir):
|
||||
saveDir = os.path.expanduser("~")
|
||||
|
||||
savePath = os.path.join(saveDir, fileName)
|
||||
savePath = os.path.join(saveDir, fileName)
|
||||
savePath, _ = QFileDialog.getSaveFileName(
|
||||
self, self.tr("Save Document As"), savePath
|
||||
)
|
||||
@@ -967,7 +965,7 @@ class GuiBuildNovel(QDialog):
|
||||
}
|
||||
|
||||
try:
|
||||
with open(savePath, mode="w", encoding="utf8") as outFile:
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
outFile.write(json.dumps(jsonData, indent=2))
|
||||
wSuccess = True
|
||||
except Exception as e:
|
||||
@@ -1045,10 +1043,9 @@ class GuiBuildNovel(QDialog):
|
||||
buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
|
||||
dataCount = 0
|
||||
if os.path.isfile(buildCache):
|
||||
|
||||
logger.debug("Loading build cache")
|
||||
try:
|
||||
with open(buildCache, mode="r", encoding="utf8") as inFile:
|
||||
with open(buildCache, mode="r", encoding="utf-8") as inFile:
|
||||
theJson = inFile.read()
|
||||
theData = json.loads(theJson)
|
||||
except Exception:
|
||||
@@ -1071,10 +1068,9 @@ class GuiBuildNovel(QDialog):
|
||||
"""Save the current data to cache.
|
||||
"""
|
||||
buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
|
||||
|
||||
logger.debug("Saving build cache")
|
||||
try:
|
||||
with open(buildCache, mode="w+", encoding="utf8") as outFile:
|
||||
with open(buildCache, mode="w+", encoding="utf-8") as outFile:
|
||||
outFile.write(json.dumps({
|
||||
"buildTime": self.buildTime,
|
||||
"htmlStyle": self.htmlStyle,
|
||||
@@ -1311,10 +1307,10 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
"""Set the stylesheet for the preview document.
|
||||
"""
|
||||
if not theStyles:
|
||||
theStyles.append(r"h1, h2 {color: rgb(66, 113, 174);}")
|
||||
theStyles.append(r"h3, h4 {color: rgb(50, 50, 50);}")
|
||||
theStyles.append(r"a {color: rgb(66, 113, 174);}")
|
||||
theStyles.append(r".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||
theStyles.append("h1, h2 {color: rgb(66, 113, 174);}")
|
||||
theStyles.append("h3, h4 {color: rgb(50, 50, 50);}")
|
||||
theStyles.append("a {color: rgb(66, 113, 174);}")
|
||||
theStyles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||
|
||||
self.qDocument.setDefaultStyleSheet("\n".join(theStyles))
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import os
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
|
||||
@@ -24,14 +24,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import nw
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QPixmap, QCursor
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
qApp, QDialog, QTreeWidget, QTreeWidgetItem, QDialogButtonBox, QGridLayout,
|
||||
QLabel, QGroupBox, QMenu, QAction, QFileDialog, QSpinBox, QHBoxLayout
|
||||
@@ -132,7 +132,7 @@ class GuiWritingStats(QDialog):
|
||||
self.barImage.fill(self.palette().highlight().color())
|
||||
|
||||
# Session Info
|
||||
self.infoBox = QGroupBox(self.tr("Sum Totals"), self)
|
||||
self.infoBox = QGroupBox(self.tr("Sum Totals"), self)
|
||||
self.infoForm = QGridLayout(self)
|
||||
self.infoBox.setLayout(self.infoForm)
|
||||
|
||||
@@ -186,7 +186,7 @@ class GuiWritingStats(QDialog):
|
||||
# Filter Options
|
||||
sPx = self.theTheme.baseIconSize
|
||||
|
||||
self.filterBox = QGroupBox(self.tr("Filters"), self)
|
||||
self.filterBox = QGroupBox(self.tr("Filters"), self)
|
||||
self.filterForm = QGridLayout(self)
|
||||
self.filterBox.setLayout(self.filterForm)
|
||||
|
||||
@@ -382,7 +382,7 @@ class GuiWritingStats(QDialog):
|
||||
errMsg = ""
|
||||
|
||||
try:
|
||||
with open(savePath, mode="w", encoding="utf8") as outFile:
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
if dataFmt == self.FMT_JSON:
|
||||
jsonData = []
|
||||
for _, sD, tT, wD, wA, wB, tI in self.filterData:
|
||||
@@ -436,8 +436,8 @@ class GuiWritingStats(QDialog):
|
||||
|
||||
ttNovel = 0
|
||||
ttNotes = 0
|
||||
ttTime = 0
|
||||
ttIdle = 0
|
||||
ttTime = 0
|
||||
ttIdle = 0
|
||||
|
||||
logFile = os.path.join(self.theProject.projMeta, nwFiles.SESS_STATS)
|
||||
if not os.path.isfile(logFile):
|
||||
@@ -445,7 +445,7 @@ class GuiWritingStats(QDialog):
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(logFile, mode="r", encoding="utf8") as inFile:
|
||||
with open(logFile, mode="r", encoding="utf-8") as inFile:
|
||||
for inLine in inFile:
|
||||
if inLine.startswith("#"):
|
||||
if inLine.startswith("# Offset"):
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ def fncConf(fncDir):
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def dummyGUI(monkeypatch, tmpConf):
|
||||
def mockGUI(monkeypatch, tmpConf):
|
||||
"""Create a mock instance of novelWriter's main GUI class.
|
||||
"""
|
||||
monkeypatch.setattr("nw.CONFIG", tmpConf)
|
||||
|
||||
@@ -23,16 +23,17 @@ import os
|
||||
import pytest
|
||||
|
||||
from mock import causeOSError
|
||||
from tools import readFile
|
||||
|
||||
from nw.core import NWProject, NWDoc
|
||||
from nw.enum import nwItemClass, nwItemLayout
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
||||
def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
|
||||
"""Test loading and saving a document with the NWDoc class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
assert theProject.projPath == nwMinimal
|
||||
|
||||
@@ -75,21 +76,18 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
||||
|
||||
# Check file content
|
||||
docPath = os.path.join(nwMinimal, "content", xHandle+".nwd")
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
assert inFile.read() == (
|
||||
"%%~name: New File\n"
|
||||
f"%%~path: a508bb932959c/{xHandle}\n"
|
||||
"%%~kind: NOVEL/SCENE\n"
|
||||
"### Test File\n\n"
|
||||
"Text ...\n\n"
|
||||
)
|
||||
assert readFile(docPath) == (
|
||||
"%%~name: New File\n"
|
||||
f"%%~path: a508bb932959c/{xHandle}\n"
|
||||
"%%~kind: NOVEL/SCENE\n"
|
||||
"### Test File\n\n"
|
||||
"Text ...\n\n"
|
||||
)
|
||||
|
||||
# Force no meta data
|
||||
theDoc._theItem = None
|
||||
assert theDoc.writeDocument(theText)
|
||||
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
assert inFile.read() == theText
|
||||
assert readFile(docPath) == theText
|
||||
|
||||
# Cause open() to fail while saving
|
||||
with monkeypatch.context() as mp:
|
||||
@@ -122,10 +120,10 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_Methods(dummyGUI, nwMinimal):
|
||||
def testCoreDocument_Methods(mockGUI, nwMinimal):
|
||||
"""Test other methods of the NWDoc class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
assert theProject.projPath == nwMinimal
|
||||
|
||||
@@ -151,15 +149,14 @@ def testCoreDocument_Methods(dummyGUI, nwMinimal):
|
||||
|
||||
# Add meta data garbage
|
||||
assert theDoc.writeDocument("%%~ stuff\n### Test File\n\nText ...\n\n")
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
assert inFile.read() == (
|
||||
"%%~name: New Scene\n"
|
||||
f"%%~path: a6d311a93600a/{sHandle}\n"
|
||||
"%%~kind: NOVEL/SCENE\n"
|
||||
"%%~ stuff\n"
|
||||
"### Test File\n\n"
|
||||
"Text ...\n\n"
|
||||
)
|
||||
assert readFile(docPath) == (
|
||||
"%%~name: New Scene\n"
|
||||
f"%%~path: a6d311a93600a/{sHandle}\n"
|
||||
"%%~kind: NOVEL/SCENE\n"
|
||||
"%%~ stuff\n"
|
||||
"### Test File\n\n"
|
||||
"Text ...\n\n"
|
||||
)
|
||||
|
||||
assert theDoc.readDocument() == "### Test File\n\nText ...\n\n"
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ from nw.enum import nwItemClass, nwItemLayout
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
|
||||
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
"""Test core functionality of scaning, saving, loading and checking
|
||||
the index cache file.
|
||||
"""
|
||||
@@ -42,7 +42,7 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
|
||||
testFile = os.path.join(outDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||
compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwLipsum)
|
||||
|
||||
@@ -126,10 +126,10 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_ScanThis(nwMinimal, dummyGUI):
|
||||
def testCoreIndex_ScanThis(nwMinimal, mockGUI):
|
||||
"""Test the tag scanner function scanThis.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
@@ -178,10 +178,10 @@ def testCoreIndex_ScanThis(nwMinimal, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
|
||||
def testCoreIndex_CheckThese(nwMinimal, mockGUI):
|
||||
"""Test the tag checker function checkThese.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
@@ -240,10 +240,10 @@ def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_ScanText(nwMinimal, dummyGUI):
|
||||
def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||
"""Check the index text scanner.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
@@ -446,10 +446,10 @@ def testCoreIndex_ScanText(nwMinimal, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
||||
def testCoreIndex_ExtractData(nwMinimal, mockGUI):
|
||||
"""Check the index data extraction functions.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
@@ -679,10 +679,10 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckTagIndex(dummyGUI):
|
||||
def testCoreIndex_CheckTagIndex(mockGUI):
|
||||
"""Test the tag index checker.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theIndex = NWIndex(theProject)
|
||||
|
||||
# Valid Index
|
||||
@@ -744,10 +744,10 @@ def testCoreIndex_CheckTagIndex(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckRefIndex(dummyGUI):
|
||||
def testCoreIndex_CheckRefIndex(mockGUI):
|
||||
"""Test the reference index checker.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theIndex = NWIndex(theProject)
|
||||
|
||||
# Valid Index
|
||||
@@ -867,10 +867,10 @@ def testCoreIndex_CheckRefIndex(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckNovelNoteIndex(dummyGUI):
|
||||
def testCoreIndex_CheckNovelNoteIndex(mockGUI):
|
||||
"""Test the novel and note index checkers.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theIndex = NWIndex(theProject)
|
||||
|
||||
# Valid Index
|
||||
@@ -1126,10 +1126,10 @@ def testCoreIndex_CheckNovelNoteIndex(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckTextCounts(dummyGUI):
|
||||
def testCoreIndex_CheckTextCounts(mockGUI):
|
||||
"""Test the text counts checker.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theIndex = NWIndex(theProject)
|
||||
|
||||
# Valid Index
|
||||
|
||||
@@ -29,10 +29,10 @@ from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_Setters(dummyGUI):
|
||||
def testCoreItem_Setters(mockGUI):
|
||||
"""Test all the simple setters for the NWItem class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Name
|
||||
@@ -167,11 +167,11 @@ def testCoreItem_Setters(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_TypeSetter(dummyGUI):
|
||||
def testCoreItem_TypeSetter(mockGUI):
|
||||
"""Test the setter for all the nwItemType values for the NWItem
|
||||
class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Type
|
||||
@@ -196,11 +196,11 @@ def testCoreItem_TypeSetter(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_ClassSetter(dummyGUI):
|
||||
def testCoreItem_ClassSetter(mockGUI):
|
||||
"""Test the setter for all the nwItemClass values for the NWItem
|
||||
class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Class
|
||||
@@ -237,11 +237,11 @@ def testCoreItem_ClassSetter(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_LayoutSetter(dummyGUI):
|
||||
def testCoreItem_LayoutSetter(mockGUI):
|
||||
"""Test the setter for all the nwItemLayout values for the NWItem
|
||||
class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Layout
|
||||
@@ -274,10 +274,10 @@ def testCoreItem_LayoutSetter(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_XMLPackUnpack(dummyGUI, caplog):
|
||||
def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||
"""Test packing and unpacking XML objects for the NWItem class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
nwXML = etree.Element("novelWriterXML")
|
||||
|
||||
# File
|
||||
|
||||
@@ -24,6 +24,7 @@ import json
|
||||
import pytest
|
||||
|
||||
from mock import causeOSError
|
||||
from tools import writeFile
|
||||
|
||||
from nw.core import NWProject
|
||||
from nw.core.options import OptionState
|
||||
@@ -31,28 +32,27 @@ from nw.constants import nwFiles
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir):
|
||||
def testCoreOptions_LoadSave(monkeypatch, mockGUI, tmpDir):
|
||||
"""Test loading and saving from the OptionState class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theOpts = OptionState(theProject)
|
||||
|
||||
# Write a test file
|
||||
optFile = os.path.join(tmpDir, nwFiles.OPTS_FILE)
|
||||
with open(optFile, mode="w+", encoding="utf8") as outFile:
|
||||
json.dump({
|
||||
"GuiBuildNovel": {
|
||||
"winWidth": 1000,
|
||||
"winHeight": 700,
|
||||
"addNovel": True,
|
||||
"addNotes": False,
|
||||
"textFont": "Cantarell",
|
||||
"dummyItem": None,
|
||||
},
|
||||
"DummyGroup": {
|
||||
"dummyItem": None,
|
||||
},
|
||||
}, outFile)
|
||||
writeFile(optFile, json.dumps({
|
||||
"GuiBuildNovel": {
|
||||
"winWidth": 1000,
|
||||
"winHeight": 700,
|
||||
"addNovel": True,
|
||||
"addNotes": False,
|
||||
"textFont": "Cantarell",
|
||||
"dummyItem": None,
|
||||
},
|
||||
"DummyGroup": {
|
||||
"dummyItem": None,
|
||||
},
|
||||
}))
|
||||
|
||||
# Load and save with no path set
|
||||
theProject.projMeta = None
|
||||
@@ -102,10 +102,10 @@ def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreOptions_SetGet(dummyGUI):
|
||||
def testCoreOptions_SetGet(mockGUI):
|
||||
"""Test setting and getting values from the OptionState class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theOpts = OptionState(theProject)
|
||||
|
||||
# Set invalid values
|
||||
|
||||
@@ -36,7 +36,7 @@ from nw.constants import nwFiles
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewMinimal(fncDir, outDir, refDir, dummyGUI):
|
||||
def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI):
|
||||
"""Create a new project from a project wizard dictionary. With
|
||||
default setting, creating a Minimal project.
|
||||
"""
|
||||
@@ -44,7 +44,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, dummyGUI):
|
||||
testFile = os.path.join(outDir, "coreProject_NewMinimal_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")
|
||||
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
# Setting no data should fail
|
||||
@@ -85,7 +85,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
|
||||
def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI):
|
||||
"""Create a new project from a project wizard dictionary.
|
||||
Custom type with chapters and scenes.
|
||||
"""
|
||||
@@ -113,7 +113,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
|
||||
"numScenes": 3,
|
||||
"chFolders": True,
|
||||
}
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
assert theProject.newProject(projData)
|
||||
@@ -127,7 +127,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
|
||||
def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI):
|
||||
"""Create a new project from a project wizard dictionary.
|
||||
Custom type without chapters, but with scenes.
|
||||
"""
|
||||
@@ -155,7 +155,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
|
||||
"numScenes": 6,
|
||||
"chFolders": True,
|
||||
}
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
assert theProject.newProject(projData)
|
||||
@@ -169,7 +169,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir):
|
||||
def testCoreProject_NewSampleA(fncDir, tmpConf, mockGUI, tmpDir):
|
||||
"""Check that we can create a new project can be created from the
|
||||
provided sample project via a zip file.
|
||||
"""
|
||||
@@ -182,7 +182,7 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir):
|
||||
"popMinimal": False,
|
||||
"popCustom": False,
|
||||
}
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
# Sample set, but no path
|
||||
@@ -218,7 +218,7 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir):
|
||||
def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir):
|
||||
"""Check that we can create a new project can be created from the
|
||||
provided sample project folder.
|
||||
"""
|
||||
@@ -231,7 +231,7 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir):
|
||||
"popMinimal": False,
|
||||
"popCustom": False,
|
||||
}
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
# Make sure we do not pick up the nw/assets/sample.zip file
|
||||
@@ -256,14 +256,14 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewRoot(fncDir, outDir, refDir, dummyGUI):
|
||||
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI):
|
||||
"""Check that new root folders can be added to the project.
|
||||
"""
|
||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
|
||||
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
assert theProject.newProject({"projPath": fncDir})
|
||||
@@ -293,14 +293,14 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_NewFile(fncDir, outDir, refDir, dummyGUI):
|
||||
def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI):
|
||||
"""Check that new files can be added to the project.
|
||||
"""
|
||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
|
||||
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
|
||||
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
|
||||
assert theProject.newProject({"projPath": fncDir})
|
||||
@@ -323,10 +323,10 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
||||
def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
|
||||
"""Test opening a project.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
|
||||
# Rename the project file to check handling
|
||||
rName = os.path.join(nwMinimal, nwFiles.PROJ_FILE)
|
||||
@@ -382,9 +382,9 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
||||
"</novelWriterXML>\n"
|
||||
))
|
||||
dummyGUI.askResponse = False
|
||||
mockGUI.askResponse = False
|
||||
assert theProject.openProject(nwMinimal) is False
|
||||
dummyGUI.undo()
|
||||
mockGUI.undo()
|
||||
|
||||
# Future file version
|
||||
writeFile(rName, (
|
||||
@@ -408,9 +408,9 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
||||
"</novelWriterXML>\n"
|
||||
))
|
||||
dummyGUI.askResponse = False
|
||||
mockGUI.askResponse = False
|
||||
assert theProject.openProject(nwMinimal) is False
|
||||
dummyGUI.undo()
|
||||
mockGUI.undo()
|
||||
|
||||
# Test skipping XML entries
|
||||
writeFile(rName, (
|
||||
@@ -436,19 +436,19 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
||||
writeFile(os.path.join(nwMinimal, "junk"), "stuff")
|
||||
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
||||
writeFile(os.path.join(nwMinimal, "data_0", "junk"), "stuff")
|
||||
dummyGUI.clear()
|
||||
mockGUI.clear()
|
||||
assert theProject.openProject(nwMinimal) is True
|
||||
assert "data_0" in dummyGUI.lastAlert
|
||||
assert "data_0" in mockGUI.lastAlert
|
||||
assert theProject.closeProject()
|
||||
|
||||
# END Test testCoreProject_Open
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Save(monkeypatch, nwMinimal, dummyGUI, refDir):
|
||||
def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
|
||||
"""Test saving a project.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
testFile = os.path.join(nwMinimal, "nwProject.nwx")
|
||||
compFile = os.path.join(refDir, os.path.pardir, "minimal", "nwProject.nwx")
|
||||
|
||||
@@ -491,10 +491,10 @@ def testCoreProject_Save(monkeypatch, nwMinimal, dummyGUI, refDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI):
|
||||
def testCoreProject_LockFile(monkeypatch, fncDir, mockGUI):
|
||||
"""Test lock file functions for the project folder.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
|
||||
lockFile = os.path.join(fncDir, nwFiles.PROJ_LOCK)
|
||||
|
||||
@@ -551,10 +551,10 @@ def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI):
|
||||
def testCoreProject_Helpers(monkeypatch, fncDir, mockGUI):
|
||||
"""Test helper functions for the project folder.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
|
||||
# No path
|
||||
assert theProject.ensureFolderStructure() is False
|
||||
@@ -595,10 +595,10 @@ def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_AccessItems(nwMinimal, dummyGUI):
|
||||
def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
||||
"""Test helper functions for the project folder.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.openProject(nwMinimal)
|
||||
|
||||
# Move Novel ROOT to after its files
|
||||
@@ -655,10 +655,10 @@ def testCoreProject_AccessItems(nwMinimal, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
|
||||
def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||
"""Test other project class methods and functions.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
assert theProject.projPath == nwMinimal
|
||||
@@ -906,13 +906,13 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
|
||||
def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
|
||||
"""Check that files in the content folder that are not tracked in
|
||||
the project XML file are handled correctly by the orphaned files
|
||||
function. It should also restore as much meta data as possible from
|
||||
the meta line at the top of the document file.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
|
||||
assert theProject.openProject(nwLipsum)
|
||||
assert theProject.projTree["636b6aa9b697b"] is None
|
||||
@@ -920,32 +920,29 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
|
||||
|
||||
# First Item with Meta Data
|
||||
orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
|
||||
with open(orphPath, mode="w", encoding="utf8") as outFile:
|
||||
outFile.write("%%~name:[Recovered] Mars\n")
|
||||
outFile.write("%%~path:5eaea4e8cdee8/636b6aa9b697b\n")
|
||||
outFile.write("%%~kind:WORLD/NOTE\n")
|
||||
outFile.write("%%~invalid\n")
|
||||
outFile.write("\n")
|
||||
writeFile(orphPath, (
|
||||
"%%~name:[Recovered] Mars\n"
|
||||
"%%~path:5eaea4e8cdee8/636b6aa9b697b\n"
|
||||
"%%~kind:WORLD/NOTE\n"
|
||||
"%%~invalid\n"
|
||||
"\n"
|
||||
))
|
||||
|
||||
# Second Item without Meta Data
|
||||
orphPath = os.path.join(nwLipsum, "content", "736b6aa9b697b.nwd")
|
||||
with open(orphPath, mode="w", encoding="utf8") as outFile:
|
||||
outFile.write("\n")
|
||||
writeFile(orphPath, "\n")
|
||||
|
||||
# Invalid File Name
|
||||
dummyPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.txt")
|
||||
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
||||
outFile.write("\n")
|
||||
tstPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.txt")
|
||||
writeFile(tstPath, "\n")
|
||||
|
||||
# Invalid File Name
|
||||
dummyPath = os.path.join(nwLipsum, "content", "636b6aa9b697bb.nwd")
|
||||
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
||||
outFile.write("\n")
|
||||
tstPath = os.path.join(nwLipsum, "content", "636b6aa9b697bb.nwd")
|
||||
writeFile(tstPath, "\n")
|
||||
|
||||
# Invalid File Name
|
||||
dummyPath = os.path.join(nwLipsum, "content", "abcdefghijklm.nwd")
|
||||
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
||||
outFile.write("\n")
|
||||
tstPath = os.path.join(nwLipsum, "content", "abcdefghijklm.nwd")
|
||||
writeFile(tstPath, "\n")
|
||||
|
||||
assert theProject.openProject(nwLipsum)
|
||||
assert theProject.projPath is not None
|
||||
@@ -983,13 +980,13 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_OldFormat(dummyGUI, nwOldProj):
|
||||
def testCoreProject_OldFormat(mockGUI, nwOldProj):
|
||||
"""Test that a project folder structure of version 1.0 can be
|
||||
converted to the latest folder structure. Version 1.0 split the
|
||||
documents into 'data_0' ... 'data_f' folders, which are now all
|
||||
contained in a single 'content' folder.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
|
||||
# Create mock files for known legacy files
|
||||
deleteFiles = [
|
||||
@@ -1073,11 +1070,11 @@ def testCoreProject_OldFormat(dummyGUI, nwOldProj):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir):
|
||||
def testCoreProject_LegacyData(monkeypatch, mockGUI, fncDir):
|
||||
"""Test the functins that handle legacy data folders and structure
|
||||
with additional tests of failure handling.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.setProjectPath(fncDir)
|
||||
|
||||
# assert theProject.newProject({"projPath": fncDir})
|
||||
@@ -1179,21 +1176,21 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreProject_Backup(monkeypatch, dummyGUI, nwMinimal, tmpDir):
|
||||
def testCoreProject_Backup(monkeypatch, mockGUI, nwMinimal, tmpDir):
|
||||
"""Test the automated backup feature of the project class. The test
|
||||
creates a backup of the Minimal test project, and then unzips the
|
||||
backupd file and checks that the project XML file is identical to
|
||||
the original file.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
# Test faulty settings
|
||||
|
||||
# No project
|
||||
dummyGUI.hasProject = False
|
||||
mockGUI.hasProject = False
|
||||
assert not theProject.zipIt(doNotify=False)
|
||||
dummyGUI.hasProject = True
|
||||
mockGUI.hasProject = True
|
||||
|
||||
# Invalid path
|
||||
theProject.mainConf.backupPath = None
|
||||
|
||||
@@ -28,11 +28,11 @@ from nw.core import NWProject, NWIndex, ToHtml
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Format(dummyGUI):
|
||||
def testCoreToHtml_Format(mockGUI):
|
||||
"""Test all the formatters for the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
dummyGUI.theIndex = NWIndex(theProject)
|
||||
theProject = NWProject(mockGUI)
|
||||
mockGUI.theIndex = NWIndex(theProject)
|
||||
theHtml = ToHtml(theProject)
|
||||
|
||||
# Export Mode
|
||||
@@ -81,11 +81,11 @@ def testCoreToHtml_Format(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Convert(dummyGUI):
|
||||
def testCoreToHtml_Convert(mockGUI):
|
||||
"""Test the converter of the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
dummyGUI.theIndex = NWIndex(theProject)
|
||||
theProject = NWProject(mockGUI)
|
||||
mockGUI.theIndex = NWIndex(theProject)
|
||||
theHtml = ToHtml(theProject)
|
||||
|
||||
# Export Mode
|
||||
@@ -347,10 +347,10 @@ def testCoreToHtml_Convert(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Complex(dummyGUI, fncDir):
|
||||
def testCoreToHtml_Complex(mockGUI, fncDir):
|
||||
"""Test the ave method of the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theHtml = ToHtml(theProject)
|
||||
|
||||
# Build Project
|
||||
@@ -421,10 +421,10 @@ def testCoreToHtml_Complex(dummyGUI, fncDir):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Methods(dummyGUI):
|
||||
def testCoreToHtml_Methods(mockGUI):
|
||||
"""Test all the other methods of the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theHtml = ToHtml(theProject)
|
||||
theHtml.setKeepMarkdown(True)
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@ from nw.core.tokenizer import Tokenizer
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_Setters(dummyGUI):
|
||||
def testCoreToken_Setters(mockGUI):
|
||||
"""Test all the setters for the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theToken = Tokenizer(theProject)
|
||||
|
||||
# Verify defaults
|
||||
@@ -113,10 +113,10 @@ def testCoreToken_Setters(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
|
||||
def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
|
||||
"""Test handling files and text in the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
theProject.projLang = "en"
|
||||
theProject._loadProjectLocalisation()
|
||||
@@ -196,10 +196,10 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_Tokenize(dummyGUI):
|
||||
def testCoreToken_Tokenize(mockGUI):
|
||||
"""Test the tokenization of the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theToken = Tokenizer(theProject)
|
||||
theToken.setKeepMarkdown(True)
|
||||
|
||||
@@ -498,10 +498,10 @@ def testCoreToken_Tokenize(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_Headers(dummyGUI):
|
||||
def testCoreToken_Headers(mockGUI):
|
||||
"""Test the header and page parser of the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theProject.projLang = "en"
|
||||
theProject._loadProjectLocalisation()
|
||||
theToken = Tokenizer(theProject)
|
||||
|
||||
@@ -45,11 +45,11 @@ def xmlToText(xElem):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToOdt_Convert(dummyGUI):
|
||||
def testCoreToOdt_Convert(mockGUI):
|
||||
"""Test the converter of the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
dummyGUI.theIndex = NWIndex(theProject)
|
||||
theProject = NWProject(mockGUI)
|
||||
mockGUI.theIndex = NWIndex(theProject)
|
||||
theDoc = ToOdt(theProject, isFlat=True)
|
||||
|
||||
# Export Mode
|
||||
|
||||
@@ -25,16 +25,18 @@ import pytest
|
||||
from lxml import etree
|
||||
from hashlib import sha256
|
||||
|
||||
from tools import readFile
|
||||
|
||||
from nw.core.project import NWProject, NWItem, NWTree
|
||||
from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
from nw.constants import nwFiles
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def dummyItems(dummyGUI):
|
||||
def dummyItems(mockGUI):
|
||||
"""Create a list of mock items.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
|
||||
itemA = NWItem(theProject)
|
||||
itemA.itemName = "Novel"
|
||||
@@ -108,10 +110,10 @@ def dummyItems(dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_BuildTree(dummyGUI, dummyItems):
|
||||
def testCoreTree_BuildTree(mockGUI, dummyItems):
|
||||
"""Test building a project tree from a list of items.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
theTree.setSeed(42)
|
||||
@@ -205,10 +207,10 @@ def testCoreTree_BuildTree(dummyGUI, dummyItems):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_Methods(dummyGUI, dummyItems):
|
||||
def testCoreTree_Methods(mockGUI, dummyItems):
|
||||
"""Test bvarious class methods.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHandle, nwItem in dummyItems:
|
||||
@@ -262,10 +264,10 @@ def testCoreTree_Methods(dummyGUI, dummyItems):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_UpdateItemLayout(dummyGUI, dummyItems):
|
||||
def testCoreTree_UpdateItemLayout(mockGUI, dummyItems):
|
||||
"""Test building a project tree from a list of items.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHandle, nwItem in dummyItems:
|
||||
@@ -388,10 +390,10 @@ def testCoreTree_UpdateItemLayout(dummyGUI, dummyItems):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_MakeHandles(monkeypatch, dummyGUI):
|
||||
def testCoreTree_MakeHandles(monkeypatch, mockGUI):
|
||||
"""Test generating item handles.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
theTree.setSeed(42)
|
||||
@@ -431,10 +433,10 @@ def testCoreTree_MakeHandles(monkeypatch, dummyGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_Stats(dummyGUI, dummyItems):
|
||||
def testCoreTree_Stats(mockGUI, dummyItems):
|
||||
"""Test project stats methods.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHandle, nwItem in dummyItems:
|
||||
@@ -458,10 +460,10 @@ def testCoreTree_Stats(dummyGUI, dummyItems):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_Reorder(dummyGUI, dummyItems):
|
||||
def testCoreTree_Reorder(mockGUI, dummyItems):
|
||||
"""Test changing tree order.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
aHandle = []
|
||||
@@ -490,10 +492,10 @@ def testCoreTree_Reorder(dummyGUI, dummyItems):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_XMLPackUnpack(dummyGUI, dummyItems):
|
||||
def testCoreTree_XMLPackUnpack(mockGUI, dummyItems):
|
||||
"""Test packing and unpacking the tree to and from XML.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHandle, nwItem in dummyItems:
|
||||
@@ -546,10 +548,10 @@ def testCoreTree_XMLPackUnpack(dummyGUI, dummyItems):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_ToCFile(monkeypatch, dummyGUI, dummyItems, tmpDir):
|
||||
def testCoreTree_ToCFile(monkeypatch, mockGUI, dummyItems, tmpDir):
|
||||
"""Test writing the ToC.txt file.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject = NWProject(mockGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHandle, nwItem in dummyItems:
|
||||
@@ -579,17 +581,16 @@ def testCoreTree_ToCFile(monkeypatch, dummyGUI, dummyItems, tmpDir):
|
||||
pathB = os.path.join("content", "c000000000002.nwd")
|
||||
pathC = os.path.join("content", "b000000000002.nwd")
|
||||
|
||||
with open(os.path.join(tmpDir, nwFiles.TOC_TXT), mode="r", encoding="utf8") as inFile:
|
||||
assert inFile.read() == (
|
||||
"\n"
|
||||
"Table of Contents\n"
|
||||
"=================\n"
|
||||
"\n"
|
||||
"File Name Class Layout Document Label\n"
|
||||
"-------------------------------------------------------------\n"
|
||||
f"{pathA} NOVEL CHAPTER Chapter One\n"
|
||||
f"{pathB} NOVEL SCENE Scene One\n"
|
||||
f"{pathC} CHARACTER NOTE Jane Doe\n"
|
||||
)
|
||||
assert readFile(os.path.join(tmpDir, nwFiles.TOC_TXT)) == (
|
||||
"\n"
|
||||
"Table of Contents\n"
|
||||
"=================\n"
|
||||
"\n"
|
||||
"File Name Class Layout Document Label\n"
|
||||
"-------------------------------------------------------------\n"
|
||||
f"{pathA} NOVEL CHAPTER Chapter One\n"
|
||||
f"{pathB} NOVEL SCENE Scene One\n"
|
||||
f"{pathC} CHARACTER NOTE Jane Doe\n"
|
||||
)
|
||||
|
||||
# END Test testCoreTree_ToCFile
|
||||
|
||||
@@ -26,6 +26,8 @@ from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QTextCursor, QTextBlock
|
||||
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
|
||||
|
||||
from tools import writeFile
|
||||
|
||||
from nw.gui.doceditor import GuiDocEditor
|
||||
from nw.enum import nwDocAction, nwDocInsert, nwWidget
|
||||
from nw.constants import nwKeyWords, nwUnicode
|
||||
@@ -659,8 +661,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
||||
assert not nwGUI.importDocument()
|
||||
|
||||
# Create the file and try again, but with no target document open
|
||||
with open(theFile, mode="w+", encoding="utf8") as outFile:
|
||||
outFile.write("Foo")
|
||||
writeFile(theFile, "Foo")
|
||||
assert not nwGUI.importDocument()
|
||||
|
||||
# Open the document from before, and add some text to it
|
||||
|
||||
@@ -54,7 +54,7 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
qtbot.mouseClick(nwGUI.projView, Qt.LeftButton)
|
||||
|
||||
nwGUI.projView._loadHeaderState()
|
||||
assert not nwGUI.projView.colHidden[nwOutline.CCOUNT]
|
||||
assert not nwGUI.projView._colHidden[nwOutline.CCOUNT]
|
||||
|
||||
# First Item
|
||||
nwGUI.rebuildOutline()
|
||||
|
||||
+4
-4
@@ -32,13 +32,13 @@ def cmpFiles(fileOne, fileTwo, ignoreLines=None):
|
||||
ignoreLines = []
|
||||
|
||||
try:
|
||||
foOne = open(fileOne, mode="r", encoding="utf8")
|
||||
foOne = open(fileOne, mode="r", encoding="utf-8")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return False
|
||||
|
||||
try:
|
||||
foTwo = open(fileTwo, mode="r", encoding="utf8")
|
||||
foTwo = open(fileTwo, mode="r", encoding="utf-8")
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return False
|
||||
@@ -83,14 +83,14 @@ def getGuiItem(theName):
|
||||
def readFile(fileName):
|
||||
"""Returns the content of a file as a string.
|
||||
"""
|
||||
with open(fileName, mode="r", encoding="utf8") as inFile:
|
||||
with open(fileName, mode="r", encoding="utf-8") as inFile:
|
||||
return inFile.read()
|
||||
|
||||
|
||||
def writeFile(fileName, fileData):
|
||||
"""Write the contents of a string to a file.
|
||||
"""
|
||||
with open(fileName, mode="w", encoding="utf8") as outFile:
|
||||
with open(fileName, mode="w", encoding="utf-8") as outFile:
|
||||
outFile.write(fileData)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user