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
|
### 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 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.
|
* 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
|
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.
|
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
|
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
|
tab as a space, so it may not show up like expected. Open Document exports should produce the
|
||||||
expected result.
|
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
|
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
|
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
|
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
|
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.
|
[documentation](https://novelwriter.readthedocs.io) for further details.
|
||||||
|
|
||||||
### Project Notes
|
### 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
|
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`
|
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
|
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
|
are located in `nw/assets/i18n`. The latter are JSON translation maps for the novelWriter projects,
|
||||||
projects, used by the Build Novel Project tool.
|
used by the Build Novel Project tool.
|
||||||
|
|
||||||
**Note**
|
**Note**
|
||||||
|
|
||||||
When making a new translation, or updating an existing one, only commit the `nw_XX.ts` you have
|
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`
|
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
|
## 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
|
novelWriter. The Preferences dialog should list the newly added language, and you can select it and
|
||||||
test it.
|
test it.
|
||||||
|
|
||||||
Please do not submit the `.qm` files to the repository. Only the `.ts` files in the `i18n` folder
|
Please do not submit the `.qm` files to the repository. Only the `.ts` file you just added in the
|
||||||
are needed.
|
`i18n` folder is needed.
|
||||||
|
|
||||||
**Note**
|
**Note**
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ sudo apt install qttools5-dev-tools pyqt5-dev-tools
|
|||||||
|
|
||||||
### Missing QtBase Translations
|
### 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
|
"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
|
your system, and novelWriter will collect those translations from there. However, these
|
||||||
translations are missing for many languages.
|
translations are missing for many languages.
|
||||||
|
|||||||
+3
-3
@@ -39,9 +39,9 @@ from nw.config import Config
|
|||||||
# Generally follows PEP 440
|
# Generally follows PEP 440
|
||||||
# Hex Version:
|
# Hex Version:
|
||||||
# - Digit 1,2 : Major Version (01-ff)
|
# - 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 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)
|
# - Digit 8 : Release Number (0-f)
|
||||||
#
|
#
|
||||||
# Example : Full Short Description
|
# Example : Full Short Description
|
||||||
@@ -111,7 +111,7 @@ CONFIG = Config()
|
|||||||
|
|
||||||
|
|
||||||
def main(sysArgs=None):
|
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:
|
if sysArgs is None:
|
||||||
sysArgs = sys.argv[1:]
|
sysArgs = sys.argv[1:]
|
||||||
|
|||||||
+8
-8
@@ -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/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
import shutil
|
|
||||||
import json
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
@@ -435,7 +435,7 @@ class Config:
|
|||||||
cnfParse = configparser.ConfigParser()
|
cnfParse = configparser.ConfigParser()
|
||||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||||
try:
|
try:
|
||||||
with open(cnfPath, mode="r", encoding="utf8") as inFile:
|
with open(cnfPath, mode="r", encoding="utf-8") as inFile:
|
||||||
cnfParse.read_file(inFile)
|
cnfParse.read_file(inFile)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Could not load config file")
|
logger.error("Could not load config file")
|
||||||
@@ -807,7 +807,7 @@ class Config:
|
|||||||
# Write config file
|
# Write config file
|
||||||
cnfPath = os.path.join(self.confPath, self.confFile)
|
cnfPath = os.path.join(self.confPath, self.confFile)
|
||||||
try:
|
try:
|
||||||
with open(cnfPath, mode="w", encoding="utf8") as outFile:
|
with open(cnfPath, mode="w", encoding="utf-8") as outFile:
|
||||||
cnfParse.write(outFile)
|
cnfParse.write(outFile)
|
||||||
self.confChanged = False
|
self.confChanged = False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -831,7 +831,7 @@ class Config:
|
|||||||
|
|
||||||
if os.path.isfile(cacheFile):
|
if os.path.isfile(cacheFile):
|
||||||
try:
|
try:
|
||||||
with open(cacheFile, mode="r", encoding="utf8") as inFile:
|
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
||||||
theData = json.load(inFile)
|
theData = json.load(inFile)
|
||||||
|
|
||||||
for projPath in theData.keys():
|
for projPath in theData.keys():
|
||||||
@@ -869,7 +869,7 @@ class Config:
|
|||||||
cacheTemp = os.path.join(self.dataPath, nwFiles.RECENT_FILE+"~")
|
cacheTemp = os.path.join(self.dataPath, nwFiles.RECENT_FILE+"~")
|
||||||
|
|
||||||
try:
|
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)
|
json.dump(self.recentProj, outFile, indent=2)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.hasError = True
|
self.hasError = True
|
||||||
|
|||||||
+2
-2
@@ -82,7 +82,7 @@ class NWDoc():
|
|||||||
self._docMeta = {}
|
self._docMeta = {}
|
||||||
if os.path.isfile(docPath):
|
if os.path.isfile(docPath):
|
||||||
try:
|
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
|
# Check the first <= 10 lines for metadata
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
@@ -136,7 +136,7 @@ class NWDoc():
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
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(docMeta)
|
||||||
outFile.write(docText)
|
outFile.write(docText)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
+2
-2
@@ -150,7 +150,7 @@ class NWIndex():
|
|||||||
if os.path.isfile(indexFile):
|
if os.path.isfile(indexFile):
|
||||||
logger.debug("Loading index file")
|
logger.debug("Loading index file")
|
||||||
try:
|
try:
|
||||||
with open(indexFile, mode="r", encoding="utf8") as inFile:
|
with open(indexFile, mode="r", encoding="utf-8") as inFile:
|
||||||
theData = json.load(inFile)
|
theData = json.load(inFile)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Failed to load index file")
|
logger.error("Failed to load index file")
|
||||||
@@ -181,7 +181,7 @@ class NWIndex():
|
|||||||
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
indexFile = os.path.join(self.theProject.projMeta, nwFiles.INDEX_FILE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(indexFile, mode="w+", encoding="utf8") as outFile:
|
with open(indexFile, mode="w+", encoding="utf-8") as outFile:
|
||||||
json.dump({
|
json.dump({
|
||||||
"tagIndex": self._tagIndex,
|
"tagIndex": self._tagIndex,
|
||||||
"refIndex": self._refIndex,
|
"refIndex": self._refIndex,
|
||||||
|
|||||||
+2
-2
@@ -130,7 +130,7 @@ class OptionState():
|
|||||||
if os.path.isfile(stateFile):
|
if os.path.isfile(stateFile):
|
||||||
logger.debug("Loading GUI options file")
|
logger.debug("Loading GUI options file")
|
||||||
try:
|
try:
|
||||||
with open(stateFile, mode="r", encoding="utf8") as inFile:
|
with open(stateFile, mode="r", encoding="utf-8") as inFile:
|
||||||
theState = json.load(inFile)
|
theState = json.load(inFile)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Failed to load GUI options file")
|
logger.error("Failed to load GUI options file")
|
||||||
@@ -157,7 +157,7 @@ class OptionState():
|
|||||||
logger.debug("Saving GUI options file")
|
logger.debug("Saving GUI options file")
|
||||||
|
|
||||||
try:
|
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)
|
json.dump(self.theState, outFile, indent=2)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Failed to save GUI options file")
|
logger.error("Failed to save GUI options file")
|
||||||
|
|||||||
+10
-11
@@ -355,7 +355,7 @@ class NWProject():
|
|||||||
|
|
||||||
def openProject(self, fileName, overrideLock=False):
|
def openProject(self, fileName, overrideLock=False):
|
||||||
"""Open the project file provided, or if doesn't exist, assume
|
"""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
|
parse the XML of the file and populate the project variables and
|
||||||
build the tree of project items.
|
build the tree of project items.
|
||||||
"""
|
"""
|
||||||
@@ -483,10 +483,11 @@ class NWProject():
|
|||||||
msgYes = self.theParent.askQuestion(
|
msgYes = self.theParent.askQuestion(
|
||||||
self.tr("Version Conflict"),
|
self.tr("Version Conflict"),
|
||||||
self.tr(
|
self.tr(
|
||||||
"This project was saved by a newer version of novelWriter, version "
|
"This project was saved by a newer version of "
|
||||||
"{0}. This is version {1}. If you continue to open the project, "
|
"novelWriter, version {0}. This is version {1}. If you "
|
||||||
"some attributes and settings may not be preserved, but the "
|
"continue to open the project, some attributes and "
|
||||||
"overall project should be fine. Continue opening the project?"
|
"settings may not be preserved, but the overall project "
|
||||||
|
"should be fine. Continue opening the project?"
|
||||||
).format(appVersion, nw.__version__)
|
).format(appVersion, nw.__version__)
|
||||||
)
|
)
|
||||||
if not msgYes:
|
if not msgYes:
|
||||||
@@ -1219,7 +1220,7 @@ class NWProject():
|
|||||||
loadFile = chkFile2
|
loadFile = chkFile2
|
||||||
|
|
||||||
try:
|
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)
|
self.langData = json.load(inFile)
|
||||||
logger.debug("Loaded project language file: %s", os.path.basename(loadFile))
|
logger.debug("Loaded project language file: %s", os.path.basename(loadFile))
|
||||||
|
|
||||||
@@ -1242,7 +1243,7 @@ class NWProject():
|
|||||||
|
|
||||||
theLines = []
|
theLines = []
|
||||||
try:
|
try:
|
||||||
with open(lockFile, mode="r", encoding="utf8") as inFile:
|
with open(lockFile, mode="r", encoding="utf-8") as inFile:
|
||||||
theData = inFile.read()
|
theData = inFile.read()
|
||||||
theLines = theData.splitlines()
|
theLines = theData.splitlines()
|
||||||
if len(theLines) != 4:
|
if len(theLines) != 4:
|
||||||
@@ -1263,7 +1264,7 @@ class NWProject():
|
|||||||
|
|
||||||
lockFile = os.path.join(self.projPath, nwFiles.PROJ_LOCK)
|
lockFile = os.path.join(self.projPath, nwFiles.PROJ_LOCK)
|
||||||
try:
|
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.hostName)
|
||||||
outFile.write("%s\n" % self.mainConf.osType)
|
outFile.write("%s\n" % self.mainConf.osType)
|
||||||
outFile.write("%s\n" % self.mainConf.kernelVer)
|
outFile.write("%s\n" % self.mainConf.kernelVer)
|
||||||
@@ -1442,7 +1443,7 @@ class NWProject():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(sessionFile, mode="a+", encoding="utf8") as outFile:
|
with open(sessionFile, mode="a+", encoding="utf-8") as outFile:
|
||||||
if not isFile:
|
if not isFile:
|
||||||
# It's a new file, so add a header
|
# It's a new file, so add a header
|
||||||
if self.lastWCount > 0:
|
if self.lastWCount > 0:
|
||||||
@@ -1481,7 +1482,6 @@ class NWProject():
|
|||||||
logger.info("Old data folder %s found", theFolder)
|
logger.info("Old data folder %s found", theFolder)
|
||||||
|
|
||||||
# Move Documents to Content
|
# Move Documents to Content
|
||||||
# =========================
|
|
||||||
for dataItem in os.listdir(theData):
|
for dataItem in os.listdir(theData):
|
||||||
theFile = os.path.join(theData, dataItem)
|
theFile = os.path.join(theData, dataItem)
|
||||||
if not os.path.isfile(theFile):
|
if not os.path.isfile(theFile):
|
||||||
@@ -1517,7 +1517,6 @@ class NWProject():
|
|||||||
errList.append(theErr)
|
errList.append(theErr)
|
||||||
|
|
||||||
# Remove Data Folder
|
# Remove Data Folder
|
||||||
# ==================
|
|
||||||
try:
|
try:
|
||||||
os.rmdir(theData)
|
os.rmdir(theData)
|
||||||
logger.info("Deleted folder: %s", theFolder)
|
logger.info("Deleted folder: %s", theFolder)
|
||||||
|
|||||||
@@ -235,8 +235,7 @@ class FakeEnchant:
|
|||||||
class NWSpellSimple(NWSpellCheck):
|
class NWSpellSimple(NWSpellCheck):
|
||||||
"""Internal spell check tool that uses standard Python packages with
|
"""Internal spell check tool that uses standard Python packages with
|
||||||
no other external dependencies. This is the fallback spell checker
|
no other external dependencies. This is the fallback spell checker
|
||||||
when no other is available. This method is fairly slow compared to
|
when no other is available. This method is slower than enchant.
|
||||||
other implementations.
|
|
||||||
"""
|
"""
|
||||||
theWords = set()
|
theWords = set()
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,14 @@ logger = logging.getLogger(__name__)
|
|||||||
class NWStatus():
|
class NWStatus():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self._theLabels = []
|
self._theLabels = []
|
||||||
self._theColours = []
|
self._theColours = []
|
||||||
self._theCounts = []
|
self._theCounts = []
|
||||||
self._theMap = {}
|
self._theMap = {}
|
||||||
self._theLength = 0
|
self._theLength = 0
|
||||||
self._theIndex = 0
|
self._theIndex = 0
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def addEntry(self, theLabel, theColours):
|
def addEntry(self, theLabel, theColours):
|
||||||
|
|||||||
+1
-1
@@ -268,7 +268,7 @@ class ToHtml(Tokenizer):
|
|||||||
def saveHTML5(self, savePath):
|
def saveHTML5(self, savePath):
|
||||||
"""Save the data to an .html file.
|
"""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 = self.getStyleSheet()
|
||||||
theStyle.append("article {width: 800px; margin: 40px auto;}")
|
theStyle.append("article {width: 800px; margin: 40px auto;}")
|
||||||
bodyText = "".join(self.fullHTML)
|
bodyText = "".join(self.fullHTML)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import re
|
import re
|
||||||
|
import logging
|
||||||
|
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from functools import partial
|
from functools import partial
|
||||||
@@ -320,7 +320,7 @@ class Tokenizer():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def doPreProcessing(self):
|
def doPreProcessing(self):
|
||||||
"""Reun trough the various replace doctionaries.
|
"""Run trough the various replace doctionaries.
|
||||||
"""
|
"""
|
||||||
# Process the user's auto-replace dictionary
|
# Process the user's auto-replace dictionary
|
||||||
if len(self.theProject.autoReplace) > 0:
|
if len(self.theProject.autoReplace) > 0:
|
||||||
@@ -701,7 +701,7 @@ class Tokenizer():
|
|||||||
def saveRawMarkdown(self, savePath):
|
def saveRawMarkdown(self, savePath):
|
||||||
"""Save the data to a plain text file.
|
"""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:
|
for nwdPage in self.theMarkdown:
|
||||||
outFile.write(nwdPage)
|
outFile.write(nwdPage)
|
||||||
return
|
return
|
||||||
|
|||||||
+14
-8
@@ -72,16 +72,22 @@ class ToMarkdown(Tokenizer):
|
|||||||
if self.genMode == self.M_STD:
|
if self.genMode == self.M_STD:
|
||||||
# Standard
|
# Standard
|
||||||
mdTags = {
|
mdTags = {
|
||||||
self.FMT_B_B: "**", self.FMT_B_E: "**",
|
self.FMT_B_B: "**",
|
||||||
self.FMT_I_B: "_", self.FMT_I_E: "_",
|
self.FMT_B_E: "**",
|
||||||
self.FMT_D_B: "", self.FMT_D_E: "",
|
self.FMT_I_B: "_",
|
||||||
|
self.FMT_I_E: "_",
|
||||||
|
self.FMT_D_B: "",
|
||||||
|
self.FMT_D_E: "",
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
# GitHub
|
# GitHub
|
||||||
mdTags = {
|
mdTags = {
|
||||||
self.FMT_B_B: "**", self.FMT_B_E: "**",
|
self.FMT_B_B: "**",
|
||||||
self.FMT_I_B: "_", self.FMT_I_E: "_",
|
self.FMT_B_E: "**",
|
||||||
self.FMT_D_B: "~~", self.FMT_D_E: "~~",
|
self.FMT_I_B: "_",
|
||||||
|
self.FMT_I_E: "_",
|
||||||
|
self.FMT_D_B: "~~",
|
||||||
|
self.FMT_D_E: "~~",
|
||||||
}
|
}
|
||||||
|
|
||||||
self.theResult = ""
|
self.theResult = ""
|
||||||
@@ -149,7 +155,7 @@ class ToMarkdown(Tokenizer):
|
|||||||
def saveMarkdown(self, savePath):
|
def saveMarkdown(self, savePath):
|
||||||
"""Save the data to a plain text file.
|
"""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)
|
theText = "".join(self.fullMD)
|
||||||
outFile.write(theText)
|
outFile.write(theText)
|
||||||
|
|
||||||
@@ -173,7 +179,7 @@ class ToMarkdown(Tokenizer):
|
|||||||
def _formatKeywords(self, tText, tStyle):
|
def _formatKeywords(self, tText, tStyle):
|
||||||
"""Apply Markdown formatting to keywords.
|
"""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:
|
if not isValid or not theBits:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -24,11 +24,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from lxml import etree
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
from nw.enum import nwItemType, nwItemClass, nwItemLayout
|
||||||
@@ -193,7 +193,7 @@ class NWTree():
|
|||||||
try:
|
try:
|
||||||
# Dump the text
|
# Dump the text
|
||||||
tocText = os.path.join(self.theProject.projPath, nwFiles.TOC_TXT)
|
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("\n")
|
||||||
outFile.write("Table of Contents\n")
|
outFile.write("Table of Contents\n")
|
||||||
outFile.write("=================\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 nw
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
|
||||||
from PyQt5.QtGui import QCursor
|
from PyQt5.QtGui import QCursor
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget,
|
qApp, QDialog, QHBoxLayout, QVBoxLayout, QDialogButtonBox, QTabWidget,
|
||||||
QTextBrowser, QLabel
|
QTextBrowser, QLabel
|
||||||
@@ -229,7 +229,7 @@ class GuiAbout(QDialog):
|
|||||||
"""
|
"""
|
||||||
docPath = os.path.join(self.mainConf.assetPath, "text", "release_notes.htm")
|
docPath = os.path.join(self.mainConf.assetPath, "text", "release_notes.htm")
|
||||||
if os.path.isfile(docPath):
|
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()
|
helpText = inFile.read()
|
||||||
self.pageNotes.setHtml(helpText)
|
self.pageNotes.setHtml(helpText)
|
||||||
else:
|
else:
|
||||||
@@ -241,7 +241,7 @@ class GuiAbout(QDialog):
|
|||||||
"""
|
"""
|
||||||
docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm")
|
docPath = os.path.join(self.mainConf.assetPath, "text", "gplv3_en.htm")
|
||||||
if os.path.isfile(docPath):
|
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()
|
helpText = inFile.read()
|
||||||
self.pageLicense.setHtml(helpText)
|
self.pageLicense.setHtml(helpText)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QLocale
|
|
||||||
from PyQt5.QtGui import QFont
|
from PyQt5.QtGui import QFont
|
||||||
|
from PyQt5.QtCore import Qt, QLocale
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
|
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
|
||||||
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
|
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
|
||||||
from PyQt5.QtGui import QKeySequence
|
from PyQt5.QtGui import QKeySequence
|
||||||
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QTreeWidget,
|
QDialog, QHBoxLayout, QVBoxLayout, QGridLayout, QPushButton, QTreeWidget,
|
||||||
QAbstractItemView, QTreeWidgetItem, QDialogButtonBox, QLabel, QShortcut,
|
QAbstractItemView, QTreeWidgetItem, QDialogButtonBox, QLabel, QShortcut,
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import nw
|
import nw
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QLocale
|
|
||||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush
|
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush
|
||||||
|
from PyQt5.QtCore import Qt, QLocale
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QHBoxLayout, QVBoxLayout, QLineEdit, QPlainTextEdit, QLabel, QWidget,
|
QHBoxLayout, QVBoxLayout, QLineEdit, QPlainTextEdit, QLabel, QWidget,
|
||||||
QDialogButtonBox, QPushButton, QColorDialog, QTreeWidget, QTreeWidgetItem,
|
QDialogButtonBox, QPushButton, QColorDialog, QTreeWidget, QTreeWidgetItem,
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
@@ -155,7 +155,7 @@ class GuiWordList(QDialog):
|
|||||||
tmpFile = dctFile + "~"
|
tmpFile = dctFile + "~"
|
||||||
|
|
||||||
try:
|
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()):
|
for i in range(self.listBox.count()):
|
||||||
outFile.write(self.listBox.item(i).text() + "\n")
|
outFile.write(self.listBox.item(i).text() + "\n")
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ class GuiWordList(QDialog):
|
|||||||
logger.debug("No project dictionary file found")
|
logger.debug("No project dictionary file found")
|
||||||
return False
|
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:
|
for inLine in inFile:
|
||||||
theWord = inLine.strip()
|
theWord = inLine.strip()
|
||||||
if len(theWord) == 0:
|
if len(theWord) == 0:
|
||||||
|
|||||||
+10
-7
@@ -379,18 +379,18 @@ class PagedDialog(QDialog):
|
|||||||
def __init__(self, theParent=None):
|
def __init__(self, theParent=None):
|
||||||
QDialog.__init__(self, parent=theParent)
|
QDialog.__init__(self, parent=theParent)
|
||||||
|
|
||||||
self._outerBox = QVBoxLayout()
|
|
||||||
self._buttonBox = QHBoxLayout()
|
|
||||||
self._tabBox = QTabWidget()
|
|
||||||
|
|
||||||
self._tabBar = VerticalTabBar(self)
|
self._tabBar = VerticalTabBar(self)
|
||||||
self._tabBox.setTabBar(self._tabBar)
|
|
||||||
self._tabBox.setTabPosition(QTabWidget.West)
|
|
||||||
self._tabBar.setExpanding(False)
|
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.addWidget(self._tabBox)
|
||||||
self._outerBox.addLayout(self._buttonBox)
|
self._outerBox.addLayout(self._buttonBox)
|
||||||
self.setLayout(self._outerBox)
|
|
||||||
|
|
||||||
# Default Margins
|
# Default Margins
|
||||||
qM = self._outerBox.contentsMargins()
|
qM = self._outerBox.contentsMargins()
|
||||||
@@ -399,11 +399,14 @@ class PagedDialog(QDialog):
|
|||||||
mT = qM.top()
|
mT = qM.top()
|
||||||
mB = qM.bottom()
|
mB = qM.bottom()
|
||||||
|
|
||||||
|
# Set Margins
|
||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
self._outerBox.setContentsMargins(0, 0, 0, mB)
|
self._outerBox.setContentsMargins(0, 0, 0, mB)
|
||||||
self._buttonBox.setContentsMargins(mL, 0, mR, 0)
|
self._buttonBox.setContentsMargins(mL, 0, mR, 0)
|
||||||
self._outerBox.setSpacing(mT)
|
self._outerBox.setSpacing(mT)
|
||||||
|
|
||||||
|
self.setLayout(self._outerBox)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def addTab(self, tabWidget, tabLabel):
|
def addTab(self, tabWidget, tabLabel):
|
||||||
|
|||||||
+17
-15
@@ -2028,7 +2028,7 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.searchOpt.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.searchOpt.setIconSize(QSize(tPx, tPx))
|
self.searchOpt.setIconSize(QSize(tPx, tPx))
|
||||||
self.searchOpt.setContentsMargins(0, 0, 0, 0)
|
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 = QLabel(self.tr("Search"))
|
||||||
self.searchLabel.setFont(boxFont)
|
self.searchLabel.setFont(boxFont)
|
||||||
@@ -2100,7 +2100,7 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self.showReplace.setArrowType(Qt.RightArrow)
|
self.showReplace.setArrowType(Qt.RightArrow)
|
||||||
self.showReplace.setCheckable(True)
|
self.showReplace.setCheckable(True)
|
||||||
self.showReplace.setToolTip(self.tr("Show/hide the replace text box"))
|
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.showReplace.toggled.connect(self._doToggleReplace)
|
||||||
|
|
||||||
self.searchButton = QPushButton(self.theTheme.getIcon("search"), "")
|
self.searchButton = QPushButton(self.theTheme.getIcon("search"), "")
|
||||||
@@ -2367,6 +2367,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
self.theParent = docEditor.theParent
|
self.theParent = docEditor.theParent
|
||||||
self.theProject = docEditor.theProject
|
self.theProject = docEditor.theProject
|
||||||
self.theTheme = docEditor.theTheme
|
self.theTheme = docEditor.theTheme
|
||||||
|
|
||||||
self._docHandle = None
|
self._docHandle = None
|
||||||
|
|
||||||
fPx = int(0.9*self.theTheme.fontPixelSize)
|
fPx = int(0.9*self.theTheme.fontPixelSize)
|
||||||
@@ -2591,8 +2592,9 @@ class GuiDocEditFooter(QWidget):
|
|||||||
self.theProject = docEditor.theProject
|
self.theProject = docEditor.theProject
|
||||||
self.theTheme = docEditor.theTheme
|
self.theTheme = docEditor.theTheme
|
||||||
self.optState = docEditor.theProject.optState
|
self.optState = docEditor.theProject.optState
|
||||||
|
|
||||||
|
self._theItem = None
|
||||||
self._docHandle = None
|
self._docHandle = None
|
||||||
self.theItem = None
|
|
||||||
|
|
||||||
self.sPx = int(round(0.9*self.theTheme.baseIconSize))
|
self.sPx = int(round(0.9*self.theTheme.baseIconSize))
|
||||||
fPx = int(0.9*self.theTheme.fontPixelSize)
|
fPx = int(0.9*self.theTheme.fontPixelSize)
|
||||||
@@ -2708,9 +2710,9 @@ class GuiDocEditFooter(QWidget):
|
|||||||
self._docHandle = tHandle
|
self._docHandle = tHandle
|
||||||
if self._docHandle is None:
|
if self._docHandle is None:
|
||||||
logger.verbose("No handle set, so clearing the editor footer")
|
logger.verbose("No handle set, so clearing the editor footer")
|
||||||
self.theItem = None
|
self._theItem = None
|
||||||
else:
|
else:
|
||||||
self.theItem = self.theProject.projTree[self._docHandle]
|
self._theItem = self.theProject.projTree[self._docHandle]
|
||||||
|
|
||||||
self.updateInfo()
|
self.updateInfo()
|
||||||
self.updateCounts()
|
self.updateCounts()
|
||||||
@@ -2720,12 +2722,12 @@ class GuiDocEditFooter(QWidget):
|
|||||||
def updateInfo(self):
|
def updateInfo(self):
|
||||||
"""Update the content of text labels.
|
"""Update the content of text labels.
|
||||||
"""
|
"""
|
||||||
if self.theItem is None:
|
if self._theItem is None:
|
||||||
sIcon = QPixmap()
|
sIcon = QPixmap()
|
||||||
sText = ""
|
sText = ""
|
||||||
else:
|
else:
|
||||||
iStatus = self.theItem.itemStatus
|
iStatus = self._theItem.itemStatus
|
||||||
if self.theItem.itemClass == nwItemClass.NOVEL:
|
if self._theItem.itemClass == nwItemClass.NOVEL:
|
||||||
iStatus = self.theProject.statusItems.checkEntry(iStatus)
|
iStatus = self.theProject.statusItems.checkEntry(iStatus)
|
||||||
theIcon = self.theParent.statusIcons[iStatus]
|
theIcon = self.theParent.statusIcons[iStatus]
|
||||||
else:
|
else:
|
||||||
@@ -2733,9 +2735,9 @@ class GuiDocEditFooter(QWidget):
|
|||||||
theIcon = self.theParent.importIcons[iStatus]
|
theIcon = self.theParent.importIcons[iStatus]
|
||||||
|
|
||||||
sIcon = theIcon.pixmap(self.sPx, self.sPx)
|
sIcon = theIcon.pixmap(self.sPx, self.sPx)
|
||||||
sClass = trConst(nwLabels.CLASS_NAME[self.theItem.itemClass])
|
sClass = trConst(nwLabels.CLASS_NAME[self._theItem.itemClass])
|
||||||
sLayout = trConst(nwLabels.LAYOUT_NAME[self.theItem.itemLayout])
|
sLayout = trConst(nwLabels.LAYOUT_NAME[self._theItem.itemLayout])
|
||||||
sText = f"{self.theItem.itemStatus} / {sClass} / {sLayout}"
|
sText = f"{self._theItem.itemStatus} / {sClass} / {sLayout}"
|
||||||
|
|
||||||
self.statusIcon.setPixmap(sIcon)
|
self.statusIcon.setPixmap(sIcon)
|
||||||
self.statusText.setText(sText)
|
self.statusText.setText(sText)
|
||||||
@@ -2745,7 +2747,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
def updateLineCount(self):
|
def updateLineCount(self):
|
||||||
"""Update the word count.
|
"""Update the word count.
|
||||||
"""
|
"""
|
||||||
if self.theItem is None:
|
if self._theItem is None:
|
||||||
iLine = 0
|
iLine = 0
|
||||||
iDist = 0
|
iDist = 0
|
||||||
else:
|
else:
|
||||||
@@ -2762,12 +2764,12 @@ class GuiDocEditFooter(QWidget):
|
|||||||
def updateCounts(self):
|
def updateCounts(self):
|
||||||
"""Update the word count.
|
"""Update the word count.
|
||||||
"""
|
"""
|
||||||
if self.theItem is None:
|
if self._theItem is None:
|
||||||
wCount = 0
|
wCount = 0
|
||||||
wDiff = 0
|
wDiff = 0
|
||||||
else:
|
else:
|
||||||
wCount = self.theItem.wordCount
|
wCount = self._theItem.wordCount
|
||||||
wDiff = wCount - self.theItem.initCount
|
wDiff = wCount - self._theItem.initCount
|
||||||
|
|
||||||
self.wordsText.setText(
|
self.wordsText.setText(
|
||||||
self.tr("Words: {0} ({1})").format(f"{wCount:n}", f"{wDiff:+n}")
|
self.tr("Words: {0} ({1})").format(f"{wCount:n}", f"{wDiff:+n}")
|
||||||
|
|||||||
+1
-5
@@ -312,9 +312,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def updateDocMargins(self):
|
def updateDocMargins(self):
|
||||||
"""Automatically adjust the margins so the text is centred if
|
"""Automatically adjust the margins so the text is centred.
|
||||||
Config.textFixedW is enabled or we're in Focus Mode. Otherwise,
|
|
||||||
just ensure the margins are set correctly.
|
|
||||||
"""
|
"""
|
||||||
vBar = self.verticalScrollBar()
|
vBar = self.verticalScrollBar()
|
||||||
sW = vBar.width() if vBar.isVisible() else 0
|
sW = vBar.width() if vBar.isVisible() else 0
|
||||||
@@ -1152,7 +1150,6 @@ class GuiDocViewDetails(QScrollArea):
|
|||||||
self.theParent = theParent
|
self.theParent = theParent
|
||||||
self.theProject = theParent.theProject
|
self.theProject = theParent.theProject
|
||||||
self.theTheme = theParent.theTheme
|
self.theTheme = theParent.theTheme
|
||||||
self.currHandle = None
|
|
||||||
|
|
||||||
self.refList = QLabel("")
|
self.refList = QLabel("")
|
||||||
self.refList.setWordWrap(True)
|
self.refList.setWordWrap(True)
|
||||||
@@ -1185,7 +1182,6 @@ class GuiDocViewDetails(QScrollArea):
|
|||||||
"""Update the current list of document references from the
|
"""Update the current list of document references from the
|
||||||
project index.
|
project index.
|
||||||
"""
|
"""
|
||||||
self.currHandle = tHandle
|
|
||||||
if self.theParent.docViewer.stickyRef:
|
if self.theParent.docViewer.stickyRef:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
+16
-18
@@ -47,7 +47,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.theProject = theParent.theProject
|
self.theProject = theParent.theProject
|
||||||
|
|
||||||
# Internals
|
# Internals
|
||||||
self.assistProc = None
|
self._assistProc = None
|
||||||
|
|
||||||
# Build Menu
|
# Build Menu
|
||||||
self._buildProjectMenu()
|
self._buildProjectMenu()
|
||||||
@@ -92,19 +92,19 @@ class GuiMainMenu(QMenuBar):
|
|||||||
def closeHelp(self):
|
def closeHelp(self):
|
||||||
"""Close the process used for the Qt Assistant, if it is open.
|
"""Close the process used for the Qt Assistant, if it is open.
|
||||||
"""
|
"""
|
||||||
if self.assistProc is None:
|
if self._assistProc is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.assistProc.state() == QProcess.Starting:
|
if self._assistProc.state() == QProcess.Starting:
|
||||||
if self.assistProc.waitForStarted(10000):
|
if self._assistProc.waitForStarted(10000):
|
||||||
self.assistProc.terminate()
|
self._assistProc.terminate()
|
||||||
else:
|
else:
|
||||||
self.assistProc.kill()
|
self._assistProc.kill()
|
||||||
|
|
||||||
elif self.assistProc.state() == QProcess.Running:
|
elif self._assistProc.state() == QProcess.Running:
|
||||||
self.assistProc.terminate()
|
self._assistProc.terminate()
|
||||||
if not self.assistProc.waitForFinished(10000):
|
if not self._assistProc.waitForFinished(10000):
|
||||||
self.assistProc.kill()
|
self._assistProc.kill()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
def _toggleSpellCheck(self, isChecked=False):
|
def _toggleSpellCheck(self):
|
||||||
"""Toggle spell checking. The active status of the spell check
|
"""Toggle spell checking. The active status of the spell check
|
||||||
flag is handled by the document editor class, so we make no
|
flag is handled by the document editor class, so we make no
|
||||||
decision, just pass a None to the function and let it decide.
|
decision, just pass a None to the function and let it decide.
|
||||||
@@ -155,10 +155,10 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self._openWebsite(nw.__docurl__)
|
self._openWebsite(nw.__docurl__)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.assistProc = QProcess(self)
|
self._assistProc = QProcess(self)
|
||||||
self.assistProc.start("assistant", ["-collectionFile", self.mainConf.helpPath])
|
self._assistProc.start("assistant", ["-collectionFile", self.mainConf.helpPath])
|
||||||
|
|
||||||
if not self.assistProc.waitForStarted(10000):
|
if not self._assistProc.waitForStarted(10000):
|
||||||
self._openWebsite(nw.__docurl__)
|
self._openWebsite(nw.__docurl__)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -239,11 +239,9 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self.rootItems[nwItemClass.ENTITY] = QAction(self.tr("Entity Root"), self.rootMenu)
|
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.CUSTOM] = QAction(self.tr("Custom Root"), self.rootMenu)
|
||||||
self.rootItems[nwItemClass.ARCHIVE] = QAction(self.tr("Outtakes Root"), self.rootMenu)
|
self.rootItems[nwItemClass.ARCHIVE] = QAction(self.tr("Outtakes Root"), self.rootMenu)
|
||||||
nCount = 0
|
for n, itemClass in enumerate(self.rootItems.keys()):
|
||||||
for itemClass in self.rootItems.keys():
|
|
||||||
nCount += 1 # This forces the lambdas to be unique
|
|
||||||
self.rootItems[itemClass].triggered.connect(
|
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])
|
self.rootMenu.addAction(self.rootItems[itemClass])
|
||||||
|
|
||||||
|
|||||||
+80
-78
@@ -28,7 +28,7 @@ import logging
|
|||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize, pyqtSlot
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView
|
QTreeWidget, QTreeWidgetItem, QMenu, QAction, QAbstractItemView
|
||||||
)
|
)
|
||||||
@@ -94,9 +94,6 @@ class GuiOutline(QTreeWidget):
|
|||||||
self.optState = theParent.theProject.optState
|
self.optState = theParent.theProject.optState
|
||||||
self.headerMenu = GuiOutlineHeaderMenu(self)
|
self.headerMenu = GuiOutlineHeaderMenu(self)
|
||||||
|
|
||||||
self.firstView = True
|
|
||||||
self.lastBuild = 0
|
|
||||||
|
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.setSelectionMode(QAbstractItemView.SingleSelection)
|
self.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||||
self.setExpandsOnDoubleClick(False)
|
self.setExpandsOnDoubleClick(False)
|
||||||
@@ -113,16 +110,18 @@ class GuiOutline(QTreeWidget):
|
|||||||
self.treeHead.customContextMenuRequested.connect(self._headerRightClick)
|
self.treeHead.customContextMenuRequested.connect(self._headerRightClick)
|
||||||
self.treeHead.sectionMoved.connect(self._columnMoved)
|
self.treeHead.sectionMoved.connect(self._columnMoved)
|
||||||
|
|
||||||
self.treeMap = {}
|
# Internals
|
||||||
self.treeOrder = []
|
self._treeOrder = []
|
||||||
self.colWidth = {}
|
self._colWidth = {}
|
||||||
self.colHidden = {}
|
self._colHidden = {}
|
||||||
self.colIndex = {}
|
self._colIdx = {}
|
||||||
self.treeNCols = 0
|
self._treeNCols = 0
|
||||||
|
self._firstView = True
|
||||||
|
self._lastBuild = 0
|
||||||
|
|
||||||
self.initOutline()
|
self.initOutline()
|
||||||
self.clearOutline()
|
self.clearOutline()
|
||||||
self.headerMenu.setHiddenState(self.colHidden)
|
self.headerMenu.setHiddenState(self._colHidden)
|
||||||
|
|
||||||
logger.debug("GuiOutline initialisation complete")
|
logger.debug("GuiOutline initialisation complete")
|
||||||
|
|
||||||
@@ -152,18 +151,18 @@ class GuiOutline(QTreeWidget):
|
|||||||
self.setColumnCount(1)
|
self.setColumnCount(1)
|
||||||
self.setHeaderLabel(trConst(nwLabels.OUTLINE_COLS[nwOutline.TITLE]))
|
self.setHeaderLabel(trConst(nwLabels.OUTLINE_COLS[nwOutline.TITLE]))
|
||||||
|
|
||||||
self.treeOrder = []
|
self._treeOrder = []
|
||||||
self.colWidth = {}
|
self._colWidth = {}
|
||||||
self.colHidden = {}
|
self._colHidden = {}
|
||||||
self.colIndex = {}
|
self._colIdx = {}
|
||||||
self.treeNCols = 0
|
self._treeNCols = 0
|
||||||
|
|
||||||
for i, hItem in enumerate(nwOutline):
|
for hItem in nwOutline:
|
||||||
self.treeOrder.append(hItem)
|
self._treeOrder.append(hItem)
|
||||||
self.colWidth[hItem] = self.DEF_WIDTH[hItem]
|
self._colWidth[hItem] = self.DEF_WIDTH[hItem]
|
||||||
self.colHidden[hItem] = self.DEF_HIDDEN[hItem]
|
self._colHidden[hItem] = self.DEF_HIDDEN[hItem]
|
||||||
|
|
||||||
self.treeNCols = len(self.treeOrder)
|
self._treeNCols = len(self._treeOrder)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -173,15 +172,15 @@ class GuiOutline(QTreeWidget):
|
|||||||
tree.
|
tree.
|
||||||
"""
|
"""
|
||||||
# If it's the first time, we always build
|
# 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._loadHeaderState()
|
||||||
self._populateTree()
|
self._populateTree()
|
||||||
self.firstView = False
|
self._firstView = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# If the novel index or novel tree has changed since the tree
|
# If the novel index or novel tree has changed since the tree
|
||||||
# was last built, we rebuild the tree from the updated index.
|
# 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
|
doBuild = (novelChanged or indexChanged) and self.theProject.autoOutline
|
||||||
if doBuild or overRide:
|
if doBuild or overRide:
|
||||||
logger.debug("Rebuilding Project Outline")
|
logger.debug("Rebuilding Project Outline")
|
||||||
@@ -194,21 +193,22 @@ class GuiOutline(QTreeWidget):
|
|||||||
"""
|
"""
|
||||||
self._saveHeaderState()
|
self._saveHeaderState()
|
||||||
self.clearOutline()
|
self.clearOutline()
|
||||||
self.firstView = True
|
self._firstView = True
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@pyqtSlot("QTreeWidgetItem*", int)
|
||||||
def _treeDoubleClick(self, tItem, tCol):
|
def _treeDoubleClick(self, tItem, tCol):
|
||||||
"""Extract the handle and line number of the title double-
|
"""Extract the handle and line number of the title double-
|
||||||
clicked, and send it to the main gui class for opening in the
|
clicked, and send it to the main gui class for opening in the
|
||||||
document editor.
|
document editor.
|
||||||
"""
|
"""
|
||||||
tHandle = tItem.data(self.colIndex[nwOutline.TITLE], Qt.UserRole)
|
tHandle = tItem.data(self._colIdx[nwOutline.TITLE], Qt.UserRole)
|
||||||
try:
|
try:
|
||||||
tLine = int(tItem.text(self.colIndex[nwOutline.LINE]))
|
tLine = int(tItem.text(self._colIdx[nwOutline.LINE]))
|
||||||
except Exception:
|
except Exception:
|
||||||
tLine = 1
|
tLine = 1
|
||||||
|
|
||||||
@@ -217,30 +217,33 @@ class GuiOutline(QTreeWidget):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
def _itemSelected(self):
|
def _itemSelected(self):
|
||||||
"""Extract the handle and line number of the currently selected
|
"""Extract the handle and line number of the currently selected
|
||||||
title, and send it to the details panel.
|
title, and send it to the details panel.
|
||||||
"""
|
"""
|
||||||
selItems = self.selectedItems()
|
selItems = self.selectedItems()
|
||||||
if selItems:
|
if selItems:
|
||||||
tHandle = selItems[0].data(self.colIndex[nwOutline.TITLE], Qt.UserRole)
|
tHandle = selItems[0].data(self._colIdx[nwOutline.TITLE], Qt.UserRole)
|
||||||
sTitle = selItems[0].data(self.colIndex[nwOutline.LINE], Qt.UserRole)
|
sTitle = selItems[0].data(self._colIdx[nwOutline.LINE], Qt.UserRole)
|
||||||
self.theParent.projMeta.showItem(tHandle, sTitle)
|
self.theParent.projMeta.showItem(tHandle, sTitle)
|
||||||
self.theParent.treeView.setSelectedHandle(tHandle)
|
self.theParent.treeView.setSelectedHandle(tHandle)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot("QPoint")
|
||||||
def _headerRightClick(self, clickPos):
|
def _headerRightClick(self, clickPos):
|
||||||
"""Show the header column menu.
|
"""Show the header column menu.
|
||||||
"""
|
"""
|
||||||
self.headerMenu.exec_(self.mapToGlobal(clickPos))
|
self.headerMenu.exec_(self.mapToGlobal(clickPos))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(int, int, int)
|
||||||
def _columnMoved(self, logIdx, oldVisualIdx, newVisualIdx):
|
def _columnMoved(self, logIdx, oldVisualIdx, newVisualIdx):
|
||||||
"""Make sure the order array is up to date with the actual order
|
"""Make sure the order array is up to date with the actual order
|
||||||
of the columns.
|
of the columns.
|
||||||
"""
|
"""
|
||||||
self.treeOrder.insert(newVisualIdx, self.treeOrder.pop(oldVisualIdx))
|
self._treeOrder.insert(newVisualIdx, self._treeOrder.pop(oldVisualIdx))
|
||||||
self._saveHeaderState()
|
self._saveHeaderState()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -249,8 +252,8 @@ class GuiOutline(QTreeWidget):
|
|||||||
header context menu.
|
header context menu.
|
||||||
"""
|
"""
|
||||||
logger.verbose("User toggled Outline column '%s'", theItem.name)
|
logger.verbose("User toggled Outline column '%s'", theItem.name)
|
||||||
if theItem in self.colIndex:
|
if theItem in self._colIdx:
|
||||||
self.setColumnHidden(self.colIndex[theItem], not isChecked)
|
self.setColumnHidden(self._colIdx[theItem], not isChecked)
|
||||||
self._saveHeaderState()
|
self._saveHeaderState()
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -281,29 +284,29 @@ class GuiOutline(QTreeWidget):
|
|||||||
|
|
||||||
# Check that we now have a complete list, and only if so, save
|
# Check that we now have a complete list, and only if so, save
|
||||||
# the order loaded from file. Otherwise, we keep the default.
|
# the order loaded from file. Otherwise, we keep the default.
|
||||||
if len(treeOrder) == self.treeNCols:
|
if len(treeOrder) == self._treeNCols:
|
||||||
self.treeOrder = treeOrder
|
self._treeOrder = treeOrder
|
||||||
else:
|
else:
|
||||||
logger.error("Failed to extract outline column order from previous session")
|
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
|
# We load whatever column widths and hidden states we find in
|
||||||
# the file, and leave the rest in their default state.
|
# the file, and leave the rest in their default state.
|
||||||
tmpWidth = self.optState.getValue("GuiOutline", "columnWidth", {})
|
tmpWidth = self.optState.getValue("GuiOutline", "columnWidth", {})
|
||||||
for hName in tmpWidth:
|
for hName in tmpWidth:
|
||||||
try:
|
try:
|
||||||
self.colWidth[nwOutline[hName]] = self.mainConf.pxInt(tmpWidth[hName])
|
self._colWidth[nwOutline[hName]] = self.mainConf.pxInt(tmpWidth[hName])
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warning("Ignored unknown outline column '%s'", str(hName))
|
logger.warning("Ignored unknown outline column '%s'", str(hName))
|
||||||
|
|
||||||
tmpHidden = self.optState.getValue("GuiOutline", "columnHidden", {})
|
tmpHidden = self.optState.getValue("GuiOutline", "columnHidden", {})
|
||||||
for hName in tmpHidden:
|
for hName in tmpHidden:
|
||||||
try:
|
try:
|
||||||
self.colHidden[nwOutline[hName]] = tmpHidden[hName]
|
self._colHidden[nwOutline[hName]] = tmpHidden[hName]
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warning("Ignored unknown outline column '%s'", str(hName))
|
logger.warning("Ignored unknown outline column '%s'", str(hName))
|
||||||
|
|
||||||
self.headerMenu.setHiddenState(self.colHidden)
|
self.headerMenu.setHiddenState(self._colHidden)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -314,7 +317,7 @@ class GuiOutline(QTreeWidget):
|
|||||||
the last known width in case they're unhidden again.
|
the last known width in case they're unhidden again.
|
||||||
"""
|
"""
|
||||||
# If we haven't built the tree, there is nothing to save.
|
# If we haven't built the tree, there is nothing to save.
|
||||||
if self.lastBuild == 0:
|
if self._lastBuild == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
treeOrder = []
|
treeOrder = []
|
||||||
@@ -322,11 +325,11 @@ class GuiOutline(QTreeWidget):
|
|||||||
colHidden = {}
|
colHidden = {}
|
||||||
|
|
||||||
for hItem in nwOutline:
|
for hItem in nwOutline:
|
||||||
colWidth[hItem.name] = self.mainConf.rpxInt(self.colWidth[hItem])
|
colWidth[hItem.name] = self.mainConf.rpxInt(self._colWidth[hItem])
|
||||||
colHidden[hItem.name] = self.colHidden[hItem]
|
colHidden[hItem.name] = self._colHidden[hItem]
|
||||||
|
|
||||||
for iCol in range(self.columnCount()):
|
for iCol in range(self.columnCount()):
|
||||||
hName = self.treeOrder[iCol].name
|
hName = self._treeOrder[iCol].name
|
||||||
treeOrder.append(hName)
|
treeOrder.append(hName)
|
||||||
|
|
||||||
iLog = self.treeHead.logicalIndex(iCol)
|
iLog = self.treeHead.logicalIndex(iCol)
|
||||||
@@ -353,25 +356,25 @@ class GuiOutline(QTreeWidget):
|
|||||||
"""
|
"""
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
if self.firstView:
|
if self._firstView:
|
||||||
theLabels = []
|
theLabels = []
|
||||||
for i, hItem in enumerate(self.treeOrder):
|
for i, hItem in enumerate(self._treeOrder):
|
||||||
theLabels.append(trConst(nwLabels.OUTLINE_COLS[hItem]))
|
theLabels.append(trConst(nwLabels.OUTLINE_COLS[hItem]))
|
||||||
self.colIndex[hItem] = i
|
self._colIdx[hItem] = i
|
||||||
|
|
||||||
self.setHeaderLabels(theLabels)
|
self.setHeaderLabels(theLabels)
|
||||||
for hItem in self.treeOrder:
|
for hItem in self._treeOrder:
|
||||||
self.setColumnWidth(self.colIndex[hItem], self.colWidth[hItem])
|
self.setColumnWidth(self._colIdx[hItem], self._colWidth[hItem])
|
||||||
self.setColumnHidden(self.colIndex[hItem], self.colHidden[hItem])
|
self.setColumnHidden(self._colIdx[hItem], self._colHidden[hItem])
|
||||||
|
|
||||||
# Make sure title column is always visible,
|
# Make sure title column is always visible,
|
||||||
# and handle column always hidden
|
# and handle column always hidden
|
||||||
self.setColumnHidden(self.colIndex[nwOutline.TITLE], False)
|
self.setColumnHidden(self._colIdx[nwOutline.TITLE], False)
|
||||||
|
|
||||||
headItem = self.headerItem()
|
headItem = self.headerItem()
|
||||||
headItem.setTextAlignment(self.colIndex[nwOutline.CCOUNT], Qt.AlignRight)
|
headItem.setTextAlignment(self._colIdx[nwOutline.CCOUNT], Qt.AlignRight)
|
||||||
headItem.setTextAlignment(self.colIndex[nwOutline.WCOUNT], Qt.AlignRight)
|
headItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight)
|
||||||
headItem.setTextAlignment(self.colIndex[nwOutline.PCOUNT], Qt.AlignRight)
|
headItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight)
|
||||||
|
|
||||||
currTitle = None
|
currTitle = None
|
||||||
currChapter = None
|
currChapter = None
|
||||||
@@ -380,7 +383,6 @@ class GuiOutline(QTreeWidget):
|
|||||||
for tKey, tHandle, sTitle, novIdx in self.theIndex.novelStructure(skipExcluded=True):
|
for tKey, tHandle, sTitle, novIdx in self.theIndex.novelStructure(skipExcluded=True):
|
||||||
|
|
||||||
tItem = self._createTreeItem(tHandle, sTitle, novIdx)
|
tItem = self._createTreeItem(tHandle, sTitle, novIdx)
|
||||||
self.treeMap[tKey] = tItem
|
|
||||||
|
|
||||||
tLevel = novIdx["level"]
|
tLevel = novIdx["level"]
|
||||||
if tLevel == "H1":
|
if tLevel == "H1":
|
||||||
@@ -421,7 +423,7 @@ class GuiOutline(QTreeWidget):
|
|||||||
|
|
||||||
tItem.setExpanded(True)
|
tItem.setExpanded(True)
|
||||||
|
|
||||||
self.lastBuild = time()
|
self._lastBuild = time()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -436,32 +438,32 @@ class GuiOutline(QTreeWidget):
|
|||||||
wC = int(novIdx["wCount"])
|
wC = int(novIdx["wCount"])
|
||||||
pC = int(novIdx["pCount"])
|
pC = int(novIdx["pCount"])
|
||||||
|
|
||||||
newItem.setText(self.colIndex[nwOutline.TITLE], novIdx["title"])
|
newItem.setText(self._colIdx[nwOutline.TITLE], novIdx["title"])
|
||||||
newItem.setData(self.colIndex[nwOutline.TITLE], Qt.UserRole, tHandle)
|
newItem.setData(self._colIdx[nwOutline.TITLE], Qt.UserRole, tHandle)
|
||||||
newItem.setIcon(self.colIndex[nwOutline.TITLE], self.theTheme.getIcon(hIcon))
|
newItem.setIcon(self._colIdx[nwOutline.TITLE], self.theTheme.getIcon(hIcon))
|
||||||
newItem.setText(self.colIndex[nwOutline.LEVEL], novIdx["level"])
|
newItem.setText(self._colIdx[nwOutline.LEVEL], novIdx["level"])
|
||||||
newItem.setText(self.colIndex[nwOutline.LABEL], nwItem.itemName)
|
newItem.setText(self._colIdx[nwOutline.LABEL], nwItem.itemName)
|
||||||
newItem.setIcon(self.colIndex[nwOutline.LABEL], self.theTheme.getIcon("proj_document"))
|
newItem.setIcon(self._colIdx[nwOutline.LABEL], self.theTheme.getIcon("proj_document"))
|
||||||
newItem.setText(self.colIndex[nwOutline.LINE], sTitle[1:].lstrip("0"))
|
newItem.setText(self._colIdx[nwOutline.LINE], sTitle[1:].lstrip("0"))
|
||||||
newItem.setData(self.colIndex[nwOutline.LINE], Qt.UserRole, sTitle)
|
newItem.setData(self._colIdx[nwOutline.LINE], Qt.UserRole, sTitle)
|
||||||
newItem.setText(self.colIndex[nwOutline.SYNOP], novIdx["synopsis"])
|
newItem.setText(self._colIdx[nwOutline.SYNOP], novIdx["synopsis"])
|
||||||
newItem.setText(self.colIndex[nwOutline.CCOUNT], f"{cC:n}")
|
newItem.setText(self._colIdx[nwOutline.CCOUNT], f"{cC:n}")
|
||||||
newItem.setText(self.colIndex[nwOutline.WCOUNT], f"{wC:n}")
|
newItem.setText(self._colIdx[nwOutline.WCOUNT], f"{wC:n}")
|
||||||
newItem.setText(self.colIndex[nwOutline.PCOUNT], f"{pC:n}")
|
newItem.setText(self._colIdx[nwOutline.PCOUNT], f"{pC:n}")
|
||||||
newItem.setTextAlignment(self.colIndex[nwOutline.CCOUNT], Qt.AlignRight)
|
newItem.setTextAlignment(self._colIdx[nwOutline.CCOUNT], Qt.AlignRight)
|
||||||
newItem.setTextAlignment(self.colIndex[nwOutline.WCOUNT], Qt.AlignRight)
|
newItem.setTextAlignment(self._colIdx[nwOutline.WCOUNT], Qt.AlignRight)
|
||||||
newItem.setTextAlignment(self.colIndex[nwOutline.PCOUNT], Qt.AlignRight)
|
newItem.setTextAlignment(self._colIdx[nwOutline.PCOUNT], Qt.AlignRight)
|
||||||
|
|
||||||
theRefs = self.theIndex.getReferences(tHandle, sTitle)
|
theRefs = self.theIndex.getReferences(tHandle, sTitle)
|
||||||
newItem.setText(self.colIndex[nwOutline.POV], ", ".join(theRefs[nwKeyWords.POV_KEY]))
|
newItem.setText(self._colIdx[nwOutline.POV], ", ".join(theRefs[nwKeyWords.POV_KEY]))
|
||||||
newItem.setText(self.colIndex[nwOutline.FOCUS], ", ".join(theRefs[nwKeyWords.FOCUS_KEY]))
|
newItem.setText(self._colIdx[nwOutline.FOCUS], ", ".join(theRefs[nwKeyWords.FOCUS_KEY]))
|
||||||
newItem.setText(self.colIndex[nwOutline.CHAR], ", ".join(theRefs[nwKeyWords.CHAR_KEY]))
|
newItem.setText(self._colIdx[nwOutline.CHAR], ", ".join(theRefs[nwKeyWords.CHAR_KEY]))
|
||||||
newItem.setText(self.colIndex[nwOutline.PLOT], ", ".join(theRefs[nwKeyWords.PLOT_KEY]))
|
newItem.setText(self._colIdx[nwOutline.PLOT], ", ".join(theRefs[nwKeyWords.PLOT_KEY]))
|
||||||
newItem.setText(self.colIndex[nwOutline.TIME], ", ".join(theRefs[nwKeyWords.TIME_KEY]))
|
newItem.setText(self._colIdx[nwOutline.TIME], ", ".join(theRefs[nwKeyWords.TIME_KEY]))
|
||||||
newItem.setText(self.colIndex[nwOutline.WORLD], ", ".join(theRefs[nwKeyWords.WORLD_KEY]))
|
newItem.setText(self._colIdx[nwOutline.WORLD], ", ".join(theRefs[nwKeyWords.WORLD_KEY]))
|
||||||
newItem.setText(self.colIndex[nwOutline.OBJECT], ", ".join(theRefs[nwKeyWords.OBJECT_KEY]))
|
newItem.setText(self._colIdx[nwOutline.OBJECT], ", ".join(theRefs[nwKeyWords.OBJECT_KEY]))
|
||||||
newItem.setText(self.colIndex[nwOutline.ENTITY], ", ".join(theRefs[nwKeyWords.ENTITY_KEY]))
|
newItem.setText(self._colIdx[nwOutline.ENTITY], ", ".join(theRefs[nwKeyWords.ENTITY_KEY]))
|
||||||
newItem.setText(self.colIndex[nwOutline.CUSTOM], ", ".join(theRefs[nwKeyWords.CUSTOM_KEY]))
|
newItem.setText(self._colIdx[nwOutline.CUSTOM], ", ".join(theRefs[nwKeyWords.CUSTOM_KEY]))
|
||||||
|
|
||||||
return newItem
|
return newItem
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import math
|
import math
|
||||||
|
import logging
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize
|
from PyQt5.QtCore import Qt, QSize
|
||||||
from PyQt5.QtGui import QFont
|
from PyQt5.QtGui import QFont
|
||||||
|
|||||||
+8
-8
@@ -25,9 +25,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
|
import os
|
||||||
import logging
|
import logging
|
||||||
import configparser
|
import configparser
|
||||||
import os
|
|
||||||
|
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from functools import partial
|
from functools import partial
|
||||||
@@ -272,7 +272,7 @@ class GuiTheme:
|
|||||||
cssData = ""
|
cssData = ""
|
||||||
try:
|
try:
|
||||||
if os.path.isfile(self.cssFile):
|
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()
|
cssData = inFile.read()
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not load theme css file")
|
logger.error("Could not load theme css file")
|
||||||
@@ -282,7 +282,7 @@ class GuiTheme:
|
|||||||
# Config File
|
# Config File
|
||||||
confParser = configparser.ConfigParser()
|
confParser = configparser.ConfigParser()
|
||||||
try:
|
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)
|
confParser.read_file(inFile)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not load theme settings from: %s", self.confFile)
|
logger.error("Could not load theme settings from: %s", self.confFile)
|
||||||
@@ -340,7 +340,7 @@ class GuiTheme:
|
|||||||
|
|
||||||
confParser = configparser.ConfigParser()
|
confParser = configparser.ConfigParser()
|
||||||
try:
|
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)
|
confParser.read_file(inFile)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not load syntax colours from: %s", self.syntaxFile)
|
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)
|
logger.verbose("Checking theme config for '%s'", themeDir)
|
||||||
try:
|
try:
|
||||||
with open(themeConf, mode="r", encoding="utf8") as inFile:
|
with open(themeConf, mode="r", encoding="utf-8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.makeAlert([
|
self.makeAlert([
|
||||||
@@ -428,7 +428,7 @@ class GuiTheme:
|
|||||||
continue
|
continue
|
||||||
logger.verbose("Checking theme syntax for '%s'", syntaxFile)
|
logger.verbose("Checking theme syntax for '%s'", syntaxFile)
|
||||||
try:
|
try:
|
||||||
with open(syntaxPath, mode="r", encoding="utf8") as inFile:
|
with open(syntaxPath, mode="r", encoding="utf-8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.makeAlert([
|
self.makeAlert([
|
||||||
@@ -646,7 +646,7 @@ class GuiIcons:
|
|||||||
# Config File
|
# Config File
|
||||||
confParser = configparser.ConfigParser()
|
confParser = configparser.ConfigParser()
|
||||||
try:
|
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)
|
confParser.read_file(inFile)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not load icon theme settings from: %s", self.confFile)
|
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)
|
themeConf = os.path.join(themePath, self.confName)
|
||||||
logger.verbose("Checking icon theme config for '%s'", themeDir)
|
logger.verbose("Checking icon theme config for '%s'", themeDir)
|
||||||
try:
|
try:
|
||||||
with open(themeConf, mode="r", encoding="utf8") as inFile:
|
with open(themeConf, mode="r", encoding="utf-8") as inFile:
|
||||||
confParser.read_file(inFile)
|
confParser.read_file(inFile)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.makeAlert([
|
self.makeAlert([
|
||||||
|
|||||||
+22
-23
@@ -24,11 +24,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QTimer, QSize, QThreadPool, pyqtSlot
|
from PyQt5.QtCore import Qt, QTimer, QSize, QThreadPool, pyqtSlot
|
||||||
from PyQt5.QtGui import QIcon, QPixmap, QColor, QKeySequence, QCursor
|
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("OS: %s", self.mainConf.osType)
|
||||||
logger.info("Kernel: %s", self.mainConf.kernelVer)
|
logger.info("Kernel: %s", self.mainConf.kernelVer)
|
||||||
logger.info("Host: %s", self.mainConf.hostName)
|
logger.info("Host: %s", self.mainConf.hostName)
|
||||||
logger.info(
|
logger.info("Qt5 Version: %s (%d)",
|
||||||
"Qt5 Version: %s (%d)", self.mainConf.verQtString, self.mainConf.verQtValue
|
self.mainConf.verQtString, self.mainConf.verQtValue)
|
||||||
)
|
logger.info("PyQt5 Version: %s (%d)",
|
||||||
logger.info(
|
self.mainConf.verPyQtString, self.mainConf.verPyQtValue)
|
||||||
"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(
|
|
||||||
"Python Version: %s (0x%x)", self.mainConf.verPyString, self.mainConf.verPyHexVal
|
|
||||||
)
|
|
||||||
logger.info("GUI Language: %s", self.mainConf.guiLang)
|
logger.info("GUI Language: %s", self.mainConf.guiLang)
|
||||||
|
|
||||||
# Core Classes
|
# Core Classes
|
||||||
@@ -137,7 +134,7 @@ class GuiMain(QMainWindow):
|
|||||||
# Project Tree Tabs
|
# Project Tree Tabs
|
||||||
self.projTabs = QTabWidget()
|
self.projTabs = QTabWidget()
|
||||||
self.projTabs.setTabPosition(QTabWidget.South)
|
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.treeView, self.tr("Project"))
|
||||||
self.projTabs.addTab(self.novelView, self.tr("Novel"))
|
self.projTabs.addTab(self.novelView, self.tr("Novel"))
|
||||||
self.projTabs.currentChanged.connect(self._projTabsChanged)
|
self.projTabs.currentChanged.connect(self._projTabsChanged)
|
||||||
@@ -152,7 +149,7 @@ class GuiMain(QMainWindow):
|
|||||||
self.treeButtons.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
self.treeButtons.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
self.treeButtons.setIconSize(QSize(btnSize, btnSize))
|
self.treeButtons.setIconSize(QSize(btnSize, btnSize))
|
||||||
self.treeButtons.setContentsMargins(0, 0, 0, 0)
|
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.projTabs.setCornerWidget(self.treeButtons, Qt.BottomRightCorner)
|
||||||
|
|
||||||
self.projDetailsBtn = QAction(self.tr("Project Details"))
|
self.projDetailsBtn = QAction(self.tr("Project Details"))
|
||||||
@@ -199,7 +196,7 @@ class GuiMain(QMainWindow):
|
|||||||
# Main Tabs : Editor / Outline
|
# Main Tabs : Editor / Outline
|
||||||
self.mainTabs = QTabWidget()
|
self.mainTabs = QTabWidget()
|
||||||
self.mainTabs.setTabPosition(QTabWidget.East)
|
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.splitDocs, self.tr("Editor"))
|
||||||
self.mainTabs.addTab(self.splitOutline, self.tr("Outline"))
|
self.mainTabs.addTab(self.splitOutline, self.tr("Outline"))
|
||||||
self.mainTabs.currentChanged.connect(self._mainTabChanged)
|
self.mainTabs.currentChanged.connect(self._mainTabChanged)
|
||||||
@@ -490,14 +487,16 @@ class GuiMain(QMainWindow):
|
|||||||
self, self.tr("Project Locked"),
|
self, self.tr("Project Locked"),
|
||||||
"%s<br><br>%s<br>%s" % (
|
"%s<br><br>%s<br>%s" % (
|
||||||
self.tr(
|
self.tr(
|
||||||
"The project is already open by another instance of novelWriter, and "
|
"The project is already open by another instance of "
|
||||||
"is therefore locked. Override lock and continue anyway?"
|
"novelWriter, and is therefore locked. Override lock "
|
||||||
|
"and continue anyway?"
|
||||||
),
|
),
|
||||||
self.tr(
|
self.tr(
|
||||||
"Note: If the program or the computer previously crashed, the lock "
|
"Note: If the program or the computer previously "
|
||||||
"can safely be overridden. If, however, another instance of "
|
"crashed, the lock can safely be overridden. If, "
|
||||||
"novelWriter has the project open, overriding the lock may corrupt "
|
"however, another instance of novelWriter has the "
|
||||||
"the project, and is not recommended."
|
"project open, overriding the lock may corrupt the "
|
||||||
|
"project, and is not recommended."
|
||||||
),
|
),
|
||||||
lockDetails
|
lockDetails
|
||||||
),
|
),
|
||||||
@@ -721,7 +720,7 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
theText = None
|
theText = None
|
||||||
try:
|
try:
|
||||||
with open(loadFile, mode="rt", encoding="utf8") as inFile:
|
with open(loadFile, mode="rt", encoding="utf-8") as inFile:
|
||||||
theText = inFile.read()
|
theText = inFile.read()
|
||||||
self.mainConf.setLastPath(loadFile)
|
self.mainConf.setLastPath(loadFile)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -740,8 +739,8 @@ class GuiMain(QMainWindow):
|
|||||||
msgYes = self.askQuestion(
|
msgYes = self.askQuestion(
|
||||||
self.tr("Import Document"),
|
self.tr("Import Document"),
|
||||||
self.tr(
|
self.tr(
|
||||||
"Importing the file will overwrite the current content of the document. "
|
"Importing the file will overwrite the current content of "
|
||||||
"Do you want to proceed?"
|
"the document. Do you want to proceed?"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if not msgYes:
|
if not msgYes:
|
||||||
|
|||||||
+11
-15
@@ -24,24 +24,24 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QTimer
|
|
||||||
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
|
|
||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QPalette, QColor, QFont, QCursor, QFontInfo
|
QPalette, QColor, QFont, QCursor, QFontInfo
|
||||||
)
|
)
|
||||||
|
from PyQt5.QtCore import Qt, QTimer
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
qApp, QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel,
|
qApp, QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel,
|
||||||
QLineEdit, QGroupBox, QGridLayout, QProgressBar, QMenu, QAction,
|
QLineEdit, QGroupBox, QGridLayout, QProgressBar, QMenu, QAction,
|
||||||
QFileDialog, QFontDialog, QSpinBox, QScrollArea, QSplitter, QWidget,
|
QFileDialog, QFontDialog, QSpinBox, QScrollArea, QSplitter, QWidget,
|
||||||
QSizePolicy, QDoubleSpinBox, QComboBox
|
QSizePolicy, QDoubleSpinBox, QComboBox
|
||||||
)
|
)
|
||||||
|
from PyQt5.QtPrintSupport import QPrinter, QPrintPreviewDialog
|
||||||
|
|
||||||
from nw.core import ToHtml, ToOdt, ToMarkdown
|
from nw.core import ToHtml, ToOdt, ToMarkdown
|
||||||
from nw.enum import nwAlert, nwItemType, nwItemLayout, nwItemClass
|
from nw.enum import nwAlert, nwItemType, nwItemLayout, nwItemClass
|
||||||
@@ -55,14 +55,12 @@ logger = logging.getLogger(__name__)
|
|||||||
class GuiBuildNovel(QDialog):
|
class GuiBuildNovel(QDialog):
|
||||||
|
|
||||||
FMT_PDF = 1 # Print to PDF
|
FMT_PDF = 1 # Print to PDF
|
||||||
|
|
||||||
FMT_ODT = 2 # Open Document file
|
FMT_ODT = 2 # Open Document file
|
||||||
FMT_FODT = 3 # Flat Open Document file
|
FMT_FODT = 3 # Flat Open Document file
|
||||||
FMT_HTM = 4 # HTML5
|
FMT_HTM = 4 # HTML5
|
||||||
FMT_NWD = 5 # nW Markdown
|
FMT_NWD = 5 # nW Markdown
|
||||||
FMT_MD = 6 # Standard Markdown
|
FMT_MD = 6 # Standard Markdown
|
||||||
FMT_GH = 7 # GitHub Markdown
|
FMT_GH = 7 # GitHub Markdown
|
||||||
|
|
||||||
FMT_JSON_H = 8 # HTML5 wrapped in JSON
|
FMT_JSON_H = 8 # HTML5 wrapped in JSON
|
||||||
FMT_JSON_M = 9 # nW Markdown wrapped in JSON
|
FMT_JSON_M = 9 # nW Markdown wrapped in JSON
|
||||||
|
|
||||||
@@ -967,7 +965,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
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))
|
outFile.write(json.dumps(jsonData, indent=2))
|
||||||
wSuccess = True
|
wSuccess = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1045,10 +1043,9 @@ class GuiBuildNovel(QDialog):
|
|||||||
buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
|
buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
|
||||||
dataCount = 0
|
dataCount = 0
|
||||||
if os.path.isfile(buildCache):
|
if os.path.isfile(buildCache):
|
||||||
|
|
||||||
logger.debug("Loading build cache")
|
logger.debug("Loading build cache")
|
||||||
try:
|
try:
|
||||||
with open(buildCache, mode="r", encoding="utf8") as inFile:
|
with open(buildCache, mode="r", encoding="utf-8") as inFile:
|
||||||
theJson = inFile.read()
|
theJson = inFile.read()
|
||||||
theData = json.loads(theJson)
|
theData = json.loads(theJson)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -1071,10 +1068,9 @@ class GuiBuildNovel(QDialog):
|
|||||||
"""Save the current data to cache.
|
"""Save the current data to cache.
|
||||||
"""
|
"""
|
||||||
buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
|
buildCache = os.path.join(self.theProject.projCache, nwFiles.BUILD_CACHE)
|
||||||
|
|
||||||
logger.debug("Saving build cache")
|
logger.debug("Saving build cache")
|
||||||
try:
|
try:
|
||||||
with open(buildCache, mode="w+", encoding="utf8") as outFile:
|
with open(buildCache, mode="w+", encoding="utf-8") as outFile:
|
||||||
outFile.write(json.dumps({
|
outFile.write(json.dumps({
|
||||||
"buildTime": self.buildTime,
|
"buildTime": self.buildTime,
|
||||||
"htmlStyle": self.htmlStyle,
|
"htmlStyle": self.htmlStyle,
|
||||||
@@ -1311,10 +1307,10 @@ class GuiBuildNovelDocView(QTextBrowser):
|
|||||||
"""Set the stylesheet for the preview document.
|
"""Set the stylesheet for the preview document.
|
||||||
"""
|
"""
|
||||||
if not theStyles:
|
if not theStyles:
|
||||||
theStyles.append(r"h1, h2 {color: rgb(66, 113, 174);}")
|
theStyles.append("h1, h2 {color: rgb(66, 113, 174);}")
|
||||||
theStyles.append(r"h3, h4 {color: rgb(50, 50, 50);}")
|
theStyles.append("h3, h4 {color: rgb(50, 50, 50);}")
|
||||||
theStyles.append(r"a {color: rgb(66, 113, 174);}")
|
theStyles.append("a {color: rgb(66, 113, 174);}")
|
||||||
theStyles.append(r".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
theStyles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||||
|
|
||||||
self.qDocument.setDefaultStyleSheet("\n".join(theStyles))
|
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 nw
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import nw
|
import nw
|
||||||
import logging
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
|
||||||
from PyQt5.QtGui import QPixmap, QCursor
|
from PyQt5.QtGui import QPixmap, QCursor
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
qApp, QDialog, QTreeWidget, QTreeWidgetItem, QDialogButtonBox, QGridLayout,
|
qApp, QDialog, QTreeWidget, QTreeWidgetItem, QDialogButtonBox, QGridLayout,
|
||||||
QLabel, QGroupBox, QMenu, QAction, QFileDialog, QSpinBox, QHBoxLayout
|
QLabel, QGroupBox, QMenu, QAction, QFileDialog, QSpinBox, QHBoxLayout
|
||||||
@@ -382,7 +382,7 @@ class GuiWritingStats(QDialog):
|
|||||||
errMsg = ""
|
errMsg = ""
|
||||||
|
|
||||||
try:
|
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:
|
if dataFmt == self.FMT_JSON:
|
||||||
jsonData = []
|
jsonData = []
|
||||||
for _, sD, tT, wD, wA, wB, tI in self.filterData:
|
for _, sD, tT, wD, wA, wB, tI in self.filterData:
|
||||||
@@ -445,7 +445,7 @@ class GuiWritingStats(QDialog):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(logFile, mode="r", encoding="utf8") as inFile:
|
with open(logFile, mode="r", encoding="utf-8") as inFile:
|
||||||
for inLine in inFile:
|
for inLine in inFile:
|
||||||
if inLine.startswith("#"):
|
if inLine.startswith("#"):
|
||||||
if inLine.startswith("# Offset"):
|
if inLine.startswith("# Offset"):
|
||||||
|
|||||||
+1
-1
@@ -143,7 +143,7 @@ def fncConf(fncDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def dummyGUI(monkeypatch, tmpConf):
|
def mockGUI(monkeypatch, tmpConf):
|
||||||
"""Create a mock instance of novelWriter's main GUI class.
|
"""Create a mock instance of novelWriter's main GUI class.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr("nw.CONFIG", tmpConf)
|
monkeypatch.setattr("nw.CONFIG", tmpConf)
|
||||||
|
|||||||
@@ -23,16 +23,17 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
|
from tools import readFile
|
||||||
|
|
||||||
from nw.core import NWProject, NWDoc
|
from nw.core import NWProject, NWDoc
|
||||||
from nw.enum import nwItemClass, nwItemLayout
|
from nw.enum import nwItemClass, nwItemLayout
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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.
|
"""Test loading and saving a document with the NWDoc class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
assert theProject.openProject(nwMinimal)
|
assert theProject.openProject(nwMinimal)
|
||||||
assert theProject.projPath == nwMinimal
|
assert theProject.projPath == nwMinimal
|
||||||
|
|
||||||
@@ -75,8 +76,7 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
|||||||
|
|
||||||
# Check file content
|
# Check file content
|
||||||
docPath = os.path.join(nwMinimal, "content", xHandle+".nwd")
|
docPath = os.path.join(nwMinimal, "content", xHandle+".nwd")
|
||||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
assert readFile(docPath) == (
|
||||||
assert inFile.read() == (
|
|
||||||
"%%~name: New File\n"
|
"%%~name: New File\n"
|
||||||
f"%%~path: a508bb932959c/{xHandle}\n"
|
f"%%~path: a508bb932959c/{xHandle}\n"
|
||||||
"%%~kind: NOVEL/SCENE\n"
|
"%%~kind: NOVEL/SCENE\n"
|
||||||
@@ -87,9 +87,7 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
|||||||
# Force no meta data
|
# Force no meta data
|
||||||
theDoc._theItem = None
|
theDoc._theItem = None
|
||||||
assert theDoc.writeDocument(theText)
|
assert theDoc.writeDocument(theText)
|
||||||
|
assert readFile(docPath) == theText
|
||||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
|
||||||
assert inFile.read() == theText
|
|
||||||
|
|
||||||
# Cause open() to fail while saving
|
# Cause open() to fail while saving
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
@@ -122,10 +120,10 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreDocument_Methods(dummyGUI, nwMinimal):
|
def testCoreDocument_Methods(mockGUI, nwMinimal):
|
||||||
"""Test other methods of the NWDoc class.
|
"""Test other methods of the NWDoc class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
assert theProject.openProject(nwMinimal)
|
assert theProject.openProject(nwMinimal)
|
||||||
assert theProject.projPath == nwMinimal
|
assert theProject.projPath == nwMinimal
|
||||||
|
|
||||||
@@ -151,8 +149,7 @@ def testCoreDocument_Methods(dummyGUI, nwMinimal):
|
|||||||
|
|
||||||
# Add meta data garbage
|
# Add meta data garbage
|
||||||
assert theDoc.writeDocument("%%~ stuff\n### Test File\n\nText ...\n\n")
|
assert theDoc.writeDocument("%%~ stuff\n### Test File\n\nText ...\n\n")
|
||||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
assert readFile(docPath) == (
|
||||||
assert inFile.read() == (
|
|
||||||
"%%~name: New Scene\n"
|
"%%~name: New Scene\n"
|
||||||
f"%%~path: a6d311a93600a/{sHandle}\n"
|
f"%%~path: a6d311a93600a/{sHandle}\n"
|
||||||
"%%~kind: NOVEL/SCENE\n"
|
"%%~kind: NOVEL/SCENE\n"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from nw.enum import nwItemClass, nwItemLayout
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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
|
"""Test core functionality of scaning, saving, loading and checking
|
||||||
the index cache file.
|
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")
|
testFile = os.path.join(outDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||||
compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")
|
compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||||
|
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
assert theProject.openProject(nwLipsum)
|
assert theProject.openProject(nwLipsum)
|
||||||
|
|
||||||
@@ -126,10 +126,10 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_ScanThis(nwMinimal, dummyGUI):
|
def testCoreIndex_ScanThis(nwMinimal, mockGUI):
|
||||||
"""Test the tag scanner function scanThis.
|
"""Test the tag scanner function scanThis.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
assert theProject.openProject(nwMinimal)
|
assert theProject.openProject(nwMinimal)
|
||||||
|
|
||||||
@@ -178,10 +178,10 @@ def testCoreIndex_ScanThis(nwMinimal, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
|
def testCoreIndex_CheckThese(nwMinimal, mockGUI):
|
||||||
"""Test the tag checker function checkThese.
|
"""Test the tag checker function checkThese.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
assert theProject.openProject(nwMinimal)
|
assert theProject.openProject(nwMinimal)
|
||||||
|
|
||||||
@@ -240,10 +240,10 @@ def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_ScanText(nwMinimal, dummyGUI):
|
def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||||
"""Check the index text scanner.
|
"""Check the index text scanner.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
assert theProject.openProject(nwMinimal)
|
assert theProject.openProject(nwMinimal)
|
||||||
|
|
||||||
@@ -446,10 +446,10 @@ def testCoreIndex_ScanText(nwMinimal, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
def testCoreIndex_ExtractData(nwMinimal, mockGUI):
|
||||||
"""Check the index data extraction functions.
|
"""Check the index data extraction functions.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
assert theProject.openProject(nwMinimal)
|
assert theProject.openProject(nwMinimal)
|
||||||
|
|
||||||
@@ -679,10 +679,10 @@ def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_CheckTagIndex(dummyGUI):
|
def testCoreIndex_CheckTagIndex(mockGUI):
|
||||||
"""Test the tag index checker.
|
"""Test the tag index checker.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theIndex = NWIndex(theProject)
|
theIndex = NWIndex(theProject)
|
||||||
|
|
||||||
# Valid Index
|
# Valid Index
|
||||||
@@ -744,10 +744,10 @@ def testCoreIndex_CheckTagIndex(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_CheckRefIndex(dummyGUI):
|
def testCoreIndex_CheckRefIndex(mockGUI):
|
||||||
"""Test the reference index checker.
|
"""Test the reference index checker.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theIndex = NWIndex(theProject)
|
theIndex = NWIndex(theProject)
|
||||||
|
|
||||||
# Valid Index
|
# Valid Index
|
||||||
@@ -867,10 +867,10 @@ def testCoreIndex_CheckRefIndex(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_CheckNovelNoteIndex(dummyGUI):
|
def testCoreIndex_CheckNovelNoteIndex(mockGUI):
|
||||||
"""Test the novel and note index checkers.
|
"""Test the novel and note index checkers.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theIndex = NWIndex(theProject)
|
theIndex = NWIndex(theProject)
|
||||||
|
|
||||||
# Valid Index
|
# Valid Index
|
||||||
@@ -1126,10 +1126,10 @@ def testCoreIndex_CheckNovelNoteIndex(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreIndex_CheckTextCounts(dummyGUI):
|
def testCoreIndex_CheckTextCounts(mockGUI):
|
||||||
"""Test the text counts checker.
|
"""Test the text counts checker.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theIndex = NWIndex(theProject)
|
theIndex = NWIndex(theProject)
|
||||||
|
|
||||||
# Valid Index
|
# Valid Index
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_Setters(dummyGUI):
|
def testCoreItem_Setters(mockGUI):
|
||||||
"""Test all the simple setters for the NWItem class.
|
"""Test all the simple setters for the NWItem class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theItem = NWItem(theProject)
|
theItem = NWItem(theProject)
|
||||||
|
|
||||||
# Name
|
# Name
|
||||||
@@ -167,11 +167,11 @@ def testCoreItem_Setters(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_TypeSetter(dummyGUI):
|
def testCoreItem_TypeSetter(mockGUI):
|
||||||
"""Test the setter for all the nwItemType values for the NWItem
|
"""Test the setter for all the nwItemType values for the NWItem
|
||||||
class.
|
class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theItem = NWItem(theProject)
|
theItem = NWItem(theProject)
|
||||||
|
|
||||||
# Type
|
# Type
|
||||||
@@ -196,11 +196,11 @@ def testCoreItem_TypeSetter(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_ClassSetter(dummyGUI):
|
def testCoreItem_ClassSetter(mockGUI):
|
||||||
"""Test the setter for all the nwItemClass values for the NWItem
|
"""Test the setter for all the nwItemClass values for the NWItem
|
||||||
class.
|
class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theItem = NWItem(theProject)
|
theItem = NWItem(theProject)
|
||||||
|
|
||||||
# Class
|
# Class
|
||||||
@@ -237,11 +237,11 @@ def testCoreItem_ClassSetter(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_LayoutSetter(dummyGUI):
|
def testCoreItem_LayoutSetter(mockGUI):
|
||||||
"""Test the setter for all the nwItemLayout values for the NWItem
|
"""Test the setter for all the nwItemLayout values for the NWItem
|
||||||
class.
|
class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theItem = NWItem(theProject)
|
theItem = NWItem(theProject)
|
||||||
|
|
||||||
# Layout
|
# Layout
|
||||||
@@ -274,10 +274,10 @@ def testCoreItem_LayoutSetter(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreItem_XMLPackUnpack(dummyGUI, caplog):
|
def testCoreItem_XMLPackUnpack(mockGUI, caplog):
|
||||||
"""Test packing and unpacking XML objects for the NWItem class.
|
"""Test packing and unpacking XML objects for the NWItem class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
nwXML = etree.Element("novelWriterXML")
|
nwXML = etree.Element("novelWriterXML")
|
||||||
|
|
||||||
# File
|
# File
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import json
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
|
from tools import writeFile
|
||||||
|
|
||||||
from nw.core import NWProject
|
from nw.core import NWProject
|
||||||
from nw.core.options import OptionState
|
from nw.core.options import OptionState
|
||||||
@@ -31,16 +32,15 @@ from nw.constants import nwFiles
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir):
|
def testCoreOptions_LoadSave(monkeypatch, mockGUI, tmpDir):
|
||||||
"""Test loading and saving from the OptionState class.
|
"""Test loading and saving from the OptionState class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theOpts = OptionState(theProject)
|
theOpts = OptionState(theProject)
|
||||||
|
|
||||||
# Write a test file
|
# Write a test file
|
||||||
optFile = os.path.join(tmpDir, nwFiles.OPTS_FILE)
|
optFile = os.path.join(tmpDir, nwFiles.OPTS_FILE)
|
||||||
with open(optFile, mode="w+", encoding="utf8") as outFile:
|
writeFile(optFile, json.dumps({
|
||||||
json.dump({
|
|
||||||
"GuiBuildNovel": {
|
"GuiBuildNovel": {
|
||||||
"winWidth": 1000,
|
"winWidth": 1000,
|
||||||
"winHeight": 700,
|
"winHeight": 700,
|
||||||
@@ -52,7 +52,7 @@ def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir):
|
|||||||
"DummyGroup": {
|
"DummyGroup": {
|
||||||
"dummyItem": None,
|
"dummyItem": None,
|
||||||
},
|
},
|
||||||
}, outFile)
|
}))
|
||||||
|
|
||||||
# Load and save with no path set
|
# Load and save with no path set
|
||||||
theProject.projMeta = None
|
theProject.projMeta = None
|
||||||
@@ -102,10 +102,10 @@ def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreOptions_SetGet(dummyGUI):
|
def testCoreOptions_SetGet(mockGUI):
|
||||||
"""Test setting and getting values from the OptionState class.
|
"""Test setting and getting values from the OptionState class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theOpts = OptionState(theProject)
|
theOpts = OptionState(theProject)
|
||||||
|
|
||||||
# Set invalid values
|
# Set invalid values
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from nw.constants import nwFiles
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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
|
"""Create a new project from a project wizard dictionary. With
|
||||||
default setting, creating a Minimal project.
|
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")
|
testFile = os.path.join(outDir, "coreProject_NewMinimal_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")
|
||||||
|
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
|
|
||||||
# Setting no data should fail
|
# Setting no data should fail
|
||||||
@@ -85,7 +85,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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.
|
"""Create a new project from a project wizard dictionary.
|
||||||
Custom type with chapters and scenes.
|
Custom type with chapters and scenes.
|
||||||
"""
|
"""
|
||||||
@@ -113,7 +113,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
|
|||||||
"numScenes": 3,
|
"numScenes": 3,
|
||||||
"chFolders": True,
|
"chFolders": True,
|
||||||
}
|
}
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
|
|
||||||
assert theProject.newProject(projData)
|
assert theProject.newProject(projData)
|
||||||
@@ -127,7 +127,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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.
|
"""Create a new project from a project wizard dictionary.
|
||||||
Custom type without chapters, but with scenes.
|
Custom type without chapters, but with scenes.
|
||||||
"""
|
"""
|
||||||
@@ -155,7 +155,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
|
|||||||
"numScenes": 6,
|
"numScenes": 6,
|
||||||
"chFolders": True,
|
"chFolders": True,
|
||||||
}
|
}
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
|
|
||||||
assert theProject.newProject(projData)
|
assert theProject.newProject(projData)
|
||||||
@@ -169,7 +169,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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
|
"""Check that we can create a new project can be created from the
|
||||||
provided sample project via a zip file.
|
provided sample project via a zip file.
|
||||||
"""
|
"""
|
||||||
@@ -182,7 +182,7 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir):
|
|||||||
"popMinimal": False,
|
"popMinimal": False,
|
||||||
"popCustom": False,
|
"popCustom": False,
|
||||||
}
|
}
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
|
|
||||||
# Sample set, but no path
|
# Sample set, but no path
|
||||||
@@ -218,7 +218,7 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, dummyGUI, tmpDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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
|
"""Check that we can create a new project can be created from the
|
||||||
provided sample project folder.
|
provided sample project folder.
|
||||||
"""
|
"""
|
||||||
@@ -231,7 +231,7 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, dummyGUI, tmpDir):
|
|||||||
"popMinimal": False,
|
"popMinimal": False,
|
||||||
"popCustom": False,
|
"popCustom": False,
|
||||||
}
|
}
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
|
|
||||||
# Make sure we do not pick up the nw/assets/sample.zip file
|
# 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
|
@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.
|
"""Check that new root folders can be added to the project.
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
|
testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")
|
||||||
|
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
|
|
||||||
assert theProject.newProject({"projPath": fncDir})
|
assert theProject.newProject({"projPath": fncDir})
|
||||||
@@ -293,14 +293,14 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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.
|
"""Check that new files can be added to the project.
|
||||||
"""
|
"""
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
|
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
|
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
|
||||||
|
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
|
|
||||||
assert theProject.newProject({"projPath": fncDir})
|
assert theProject.newProject({"projPath": fncDir})
|
||||||
@@ -323,10 +323,10 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
|
||||||
"""Test opening a project.
|
"""Test opening a project.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
|
||||||
# Rename the project file to check handling
|
# Rename the project file to check handling
|
||||||
rName = os.path.join(nwMinimal, nwFiles.PROJ_FILE)
|
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"
|
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
||||||
"</novelWriterXML>\n"
|
"</novelWriterXML>\n"
|
||||||
))
|
))
|
||||||
dummyGUI.askResponse = False
|
mockGUI.askResponse = False
|
||||||
assert theProject.openProject(nwMinimal) is False
|
assert theProject.openProject(nwMinimal) is False
|
||||||
dummyGUI.undo()
|
mockGUI.undo()
|
||||||
|
|
||||||
# Future file version
|
# Future file version
|
||||||
writeFile(rName, (
|
writeFile(rName, (
|
||||||
@@ -408,9 +408,9 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
|||||||
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
"timeStamp=\"2020-01-01 00:00:00\">\n"
|
||||||
"</novelWriterXML>\n"
|
"</novelWriterXML>\n"
|
||||||
))
|
))
|
||||||
dummyGUI.askResponse = False
|
mockGUI.askResponse = False
|
||||||
assert theProject.openProject(nwMinimal) is False
|
assert theProject.openProject(nwMinimal) is False
|
||||||
dummyGUI.undo()
|
mockGUI.undo()
|
||||||
|
|
||||||
# Test skipping XML entries
|
# Test skipping XML entries
|
||||||
writeFile(rName, (
|
writeFile(rName, (
|
||||||
@@ -436,19 +436,19 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
|||||||
writeFile(os.path.join(nwMinimal, "junk"), "stuff")
|
writeFile(os.path.join(nwMinimal, "junk"), "stuff")
|
||||||
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
||||||
writeFile(os.path.join(nwMinimal, "data_0", "junk"), "stuff")
|
writeFile(os.path.join(nwMinimal, "data_0", "junk"), "stuff")
|
||||||
dummyGUI.clear()
|
mockGUI.clear()
|
||||||
assert theProject.openProject(nwMinimal) is True
|
assert theProject.openProject(nwMinimal) is True
|
||||||
assert "data_0" in dummyGUI.lastAlert
|
assert "data_0" in mockGUI.lastAlert
|
||||||
assert theProject.closeProject()
|
assert theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreProject_Open
|
# END Test testCoreProject_Open
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_Save(monkeypatch, nwMinimal, dummyGUI, refDir):
|
def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
|
||||||
"""Test saving a project.
|
"""Test saving a project.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
testFile = os.path.join(nwMinimal, "nwProject.nwx")
|
testFile = os.path.join(nwMinimal, "nwProject.nwx")
|
||||||
compFile = os.path.join(refDir, os.path.pardir, "minimal", "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
|
@pytest.mark.core
|
||||||
def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI):
|
def testCoreProject_LockFile(monkeypatch, fncDir, mockGUI):
|
||||||
"""Test lock file functions for the project folder.
|
"""Test lock file functions for the project folder.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
|
||||||
lockFile = os.path.join(fncDir, nwFiles.PROJ_LOCK)
|
lockFile = os.path.join(fncDir, nwFiles.PROJ_LOCK)
|
||||||
|
|
||||||
@@ -551,10 +551,10 @@ def testCoreProject_LockFile(monkeypatch, fncDir, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI):
|
def testCoreProject_Helpers(monkeypatch, fncDir, mockGUI):
|
||||||
"""Test helper functions for the project folder.
|
"""Test helper functions for the project folder.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
|
||||||
# No path
|
# No path
|
||||||
assert theProject.ensureFolderStructure() is False
|
assert theProject.ensureFolderStructure() is False
|
||||||
@@ -595,10 +595,10 @@ def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_AccessItems(nwMinimal, dummyGUI):
|
def testCoreProject_AccessItems(nwMinimal, mockGUI):
|
||||||
"""Test helper functions for the project folder.
|
"""Test helper functions for the project folder.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.openProject(nwMinimal)
|
theProject.openProject(nwMinimal)
|
||||||
|
|
||||||
# Move Novel ROOT to after its files
|
# Move Novel ROOT to after its files
|
||||||
@@ -655,10 +655,10 @@ def testCoreProject_AccessItems(nwMinimal, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
|
def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
|
||||||
"""Test other project class methods and functions.
|
"""Test other project class methods and functions.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
assert theProject.openProject(nwMinimal)
|
assert theProject.openProject(nwMinimal)
|
||||||
assert theProject.projPath == nwMinimal
|
assert theProject.projPath == nwMinimal
|
||||||
@@ -906,13 +906,13 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, dummyGUI, tmpDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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
|
"""Check that files in the content folder that are not tracked in
|
||||||
the project XML file are handled correctly by the orphaned files
|
the project XML file are handled correctly by the orphaned files
|
||||||
function. It should also restore as much meta data as possible from
|
function. It should also restore as much meta data as possible from
|
||||||
the meta line at the top of the document file.
|
the meta line at the top of the document file.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
|
||||||
assert theProject.openProject(nwLipsum)
|
assert theProject.openProject(nwLipsum)
|
||||||
assert theProject.projTree["636b6aa9b697b"] is None
|
assert theProject.projTree["636b6aa9b697b"] is None
|
||||||
@@ -920,32 +920,29 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
|
|||||||
|
|
||||||
# First Item with Meta Data
|
# First Item with Meta Data
|
||||||
orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
|
orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
|
||||||
with open(orphPath, mode="w", encoding="utf8") as outFile:
|
writeFile(orphPath, (
|
||||||
outFile.write("%%~name:[Recovered] Mars\n")
|
"%%~name:[Recovered] Mars\n"
|
||||||
outFile.write("%%~path:5eaea4e8cdee8/636b6aa9b697b\n")
|
"%%~path:5eaea4e8cdee8/636b6aa9b697b\n"
|
||||||
outFile.write("%%~kind:WORLD/NOTE\n")
|
"%%~kind:WORLD/NOTE\n"
|
||||||
outFile.write("%%~invalid\n")
|
"%%~invalid\n"
|
||||||
outFile.write("\n")
|
"\n"
|
||||||
|
))
|
||||||
|
|
||||||
# Second Item without Meta Data
|
# Second Item without Meta Data
|
||||||
orphPath = os.path.join(nwLipsum, "content", "736b6aa9b697b.nwd")
|
orphPath = os.path.join(nwLipsum, "content", "736b6aa9b697b.nwd")
|
||||||
with open(orphPath, mode="w", encoding="utf8") as outFile:
|
writeFile(orphPath, "\n")
|
||||||
outFile.write("\n")
|
|
||||||
|
|
||||||
# Invalid File Name
|
# Invalid File Name
|
||||||
dummyPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.txt")
|
tstPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.txt")
|
||||||
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
writeFile(tstPath, "\n")
|
||||||
outFile.write("\n")
|
|
||||||
|
|
||||||
# Invalid File Name
|
# Invalid File Name
|
||||||
dummyPath = os.path.join(nwLipsum, "content", "636b6aa9b697bb.nwd")
|
tstPath = os.path.join(nwLipsum, "content", "636b6aa9b697bb.nwd")
|
||||||
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
writeFile(tstPath, "\n")
|
||||||
outFile.write("\n")
|
|
||||||
|
|
||||||
# Invalid File Name
|
# Invalid File Name
|
||||||
dummyPath = os.path.join(nwLipsum, "content", "abcdefghijklm.nwd")
|
tstPath = os.path.join(nwLipsum, "content", "abcdefghijklm.nwd")
|
||||||
with open(dummyPath, mode="w", encoding="utf8") as outFile:
|
writeFile(tstPath, "\n")
|
||||||
outFile.write("\n")
|
|
||||||
|
|
||||||
assert theProject.openProject(nwLipsum)
|
assert theProject.openProject(nwLipsum)
|
||||||
assert theProject.projPath is not None
|
assert theProject.projPath is not None
|
||||||
@@ -983,13 +980,13 @@ def testCoreProject_OrphanedFiles(dummyGUI, nwLipsum):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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
|
"""Test that a project folder structure of version 1.0 can be
|
||||||
converted to the latest folder structure. Version 1.0 split the
|
converted to the latest folder structure. Version 1.0 split the
|
||||||
documents into 'data_0' ... 'data_f' folders, which are now all
|
documents into 'data_0' ... 'data_f' folders, which are now all
|
||||||
contained in a single 'content' folder.
|
contained in a single 'content' folder.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
|
||||||
# Create mock files for known legacy files
|
# Create mock files for known legacy files
|
||||||
deleteFiles = [
|
deleteFiles = [
|
||||||
@@ -1073,11 +1070,11 @@ def testCoreProject_OldFormat(dummyGUI, nwOldProj):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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
|
"""Test the functins that handle legacy data folders and structure
|
||||||
with additional tests of failure handling.
|
with additional tests of failure handling.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.setProjectPath(fncDir)
|
theProject.setProjectPath(fncDir)
|
||||||
|
|
||||||
# assert theProject.newProject({"projPath": fncDir})
|
# assert theProject.newProject({"projPath": fncDir})
|
||||||
@@ -1179,21 +1176,21 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@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
|
"""Test the automated backup feature of the project class. The test
|
||||||
creates a backup of the Minimal test project, and then unzips the
|
creates a backup of the Minimal test project, and then unzips the
|
||||||
backupd file and checks that the project XML file is identical to
|
backupd file and checks that the project XML file is identical to
|
||||||
the original file.
|
the original file.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
assert theProject.openProject(nwMinimal)
|
assert theProject.openProject(nwMinimal)
|
||||||
|
|
||||||
# Test faulty settings
|
# Test faulty settings
|
||||||
|
|
||||||
# No project
|
# No project
|
||||||
dummyGUI.hasProject = False
|
mockGUI.hasProject = False
|
||||||
assert not theProject.zipIt(doNotify=False)
|
assert not theProject.zipIt(doNotify=False)
|
||||||
dummyGUI.hasProject = True
|
mockGUI.hasProject = True
|
||||||
|
|
||||||
# Invalid path
|
# Invalid path
|
||||||
theProject.mainConf.backupPath = None
|
theProject.mainConf.backupPath = None
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ from nw.core import NWProject, NWIndex, ToHtml
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_Format(dummyGUI):
|
def testCoreToHtml_Format(mockGUI):
|
||||||
"""Test all the formatters for the ToHtml class.
|
"""Test all the formatters for the ToHtml class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
dummyGUI.theIndex = NWIndex(theProject)
|
mockGUI.theIndex = NWIndex(theProject)
|
||||||
theHtml = ToHtml(theProject)
|
theHtml = ToHtml(theProject)
|
||||||
|
|
||||||
# Export Mode
|
# Export Mode
|
||||||
@@ -81,11 +81,11 @@ def testCoreToHtml_Format(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_Convert(dummyGUI):
|
def testCoreToHtml_Convert(mockGUI):
|
||||||
"""Test the converter of the ToHtml class.
|
"""Test the converter of the ToHtml class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
dummyGUI.theIndex = NWIndex(theProject)
|
mockGUI.theIndex = NWIndex(theProject)
|
||||||
theHtml = ToHtml(theProject)
|
theHtml = ToHtml(theProject)
|
||||||
|
|
||||||
# Export Mode
|
# Export Mode
|
||||||
@@ -347,10 +347,10 @@ def testCoreToHtml_Convert(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_Complex(dummyGUI, fncDir):
|
def testCoreToHtml_Complex(mockGUI, fncDir):
|
||||||
"""Test the ave method of the ToHtml class.
|
"""Test the ave method of the ToHtml class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theHtml = ToHtml(theProject)
|
theHtml = ToHtml(theProject)
|
||||||
|
|
||||||
# Build Project
|
# Build Project
|
||||||
@@ -421,10 +421,10 @@ def testCoreToHtml_Complex(dummyGUI, fncDir):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToHtml_Methods(dummyGUI):
|
def testCoreToHtml_Methods(mockGUI):
|
||||||
"""Test all the other methods of the ToHtml class.
|
"""Test all the other methods of the ToHtml class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theHtml = ToHtml(theProject)
|
theHtml = ToHtml(theProject)
|
||||||
theHtml.setKeepMarkdown(True)
|
theHtml.setKeepMarkdown(True)
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ from nw.core.tokenizer import Tokenizer
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToken_Setters(dummyGUI):
|
def testCoreToken_Setters(mockGUI):
|
||||||
"""Test all the setters for the Tokenizer class.
|
"""Test all the setters for the Tokenizer class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theToken = Tokenizer(theProject)
|
theToken = Tokenizer(theProject)
|
||||||
|
|
||||||
# Verify defaults
|
# Verify defaults
|
||||||
@@ -113,10 +113,10 @@ def testCoreToken_Setters(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
|
def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
|
||||||
"""Test handling files and text in the Tokenizer class.
|
"""Test handling files and text in the Tokenizer class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projTree.setSeed(42)
|
theProject.projTree.setSeed(42)
|
||||||
theProject.projLang = "en"
|
theProject.projLang = "en"
|
||||||
theProject._loadProjectLocalisation()
|
theProject._loadProjectLocalisation()
|
||||||
@@ -196,10 +196,10 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToken_Tokenize(dummyGUI):
|
def testCoreToken_Tokenize(mockGUI):
|
||||||
"""Test the tokenization of the Tokenizer class.
|
"""Test the tokenization of the Tokenizer class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theToken = Tokenizer(theProject)
|
theToken = Tokenizer(theProject)
|
||||||
theToken.setKeepMarkdown(True)
|
theToken.setKeepMarkdown(True)
|
||||||
|
|
||||||
@@ -498,10 +498,10 @@ def testCoreToken_Tokenize(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToken_Headers(dummyGUI):
|
def testCoreToken_Headers(mockGUI):
|
||||||
"""Test the header and page parser of the Tokenizer class.
|
"""Test the header and page parser of the Tokenizer class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theProject.projLang = "en"
|
theProject.projLang = "en"
|
||||||
theProject._loadProjectLocalisation()
|
theProject._loadProjectLocalisation()
|
||||||
theToken = Tokenizer(theProject)
|
theToken = Tokenizer(theProject)
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ def xmlToText(xElem):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreToOdt_Convert(dummyGUI):
|
def testCoreToOdt_Convert(mockGUI):
|
||||||
"""Test the converter of the ToHtml class.
|
"""Test the converter of the ToHtml class.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
dummyGUI.theIndex = NWIndex(theProject)
|
mockGUI.theIndex = NWIndex(theProject)
|
||||||
theDoc = ToOdt(theProject, isFlat=True)
|
theDoc = ToOdt(theProject, isFlat=True)
|
||||||
|
|
||||||
# Export Mode
|
# Export Mode
|
||||||
|
|||||||
@@ -25,16 +25,18 @@ import pytest
|
|||||||
from lxml import etree
|
from lxml import etree
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
|
from tools import readFile
|
||||||
|
|
||||||
from nw.core.project import NWProject, NWItem, NWTree
|
from nw.core.project import NWProject, NWItem, NWTree
|
||||||
from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
from nw.constants import nwFiles
|
from nw.constants import nwFiles
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def dummyItems(dummyGUI):
|
def dummyItems(mockGUI):
|
||||||
"""Create a list of mock items.
|
"""Create a list of mock items.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
|
|
||||||
itemA = NWItem(theProject)
|
itemA = NWItem(theProject)
|
||||||
itemA.itemName = "Novel"
|
itemA.itemName = "Novel"
|
||||||
@@ -108,10 +110,10 @@ def dummyItems(dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_BuildTree(dummyGUI, dummyItems):
|
def testCoreTree_BuildTree(mockGUI, dummyItems):
|
||||||
"""Test building a project tree from a list of items.
|
"""Test building a project tree from a list of items.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theTree = NWTree(theProject)
|
theTree = NWTree(theProject)
|
||||||
|
|
||||||
theTree.setSeed(42)
|
theTree.setSeed(42)
|
||||||
@@ -205,10 +207,10 @@ def testCoreTree_BuildTree(dummyGUI, dummyItems):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_Methods(dummyGUI, dummyItems):
|
def testCoreTree_Methods(mockGUI, dummyItems):
|
||||||
"""Test bvarious class methods.
|
"""Test bvarious class methods.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theTree = NWTree(theProject)
|
theTree = NWTree(theProject)
|
||||||
|
|
||||||
for tHandle, pHandle, nwItem in dummyItems:
|
for tHandle, pHandle, nwItem in dummyItems:
|
||||||
@@ -262,10 +264,10 @@ def testCoreTree_Methods(dummyGUI, dummyItems):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_UpdateItemLayout(dummyGUI, dummyItems):
|
def testCoreTree_UpdateItemLayout(mockGUI, dummyItems):
|
||||||
"""Test building a project tree from a list of items.
|
"""Test building a project tree from a list of items.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theTree = NWTree(theProject)
|
theTree = NWTree(theProject)
|
||||||
|
|
||||||
for tHandle, pHandle, nwItem in dummyItems:
|
for tHandle, pHandle, nwItem in dummyItems:
|
||||||
@@ -388,10 +390,10 @@ def testCoreTree_UpdateItemLayout(dummyGUI, dummyItems):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_MakeHandles(monkeypatch, dummyGUI):
|
def testCoreTree_MakeHandles(monkeypatch, mockGUI):
|
||||||
"""Test generating item handles.
|
"""Test generating item handles.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theTree = NWTree(theProject)
|
theTree = NWTree(theProject)
|
||||||
|
|
||||||
theTree.setSeed(42)
|
theTree.setSeed(42)
|
||||||
@@ -431,10 +433,10 @@ def testCoreTree_MakeHandles(monkeypatch, dummyGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_Stats(dummyGUI, dummyItems):
|
def testCoreTree_Stats(mockGUI, dummyItems):
|
||||||
"""Test project stats methods.
|
"""Test project stats methods.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theTree = NWTree(theProject)
|
theTree = NWTree(theProject)
|
||||||
|
|
||||||
for tHandle, pHandle, nwItem in dummyItems:
|
for tHandle, pHandle, nwItem in dummyItems:
|
||||||
@@ -458,10 +460,10 @@ def testCoreTree_Stats(dummyGUI, dummyItems):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_Reorder(dummyGUI, dummyItems):
|
def testCoreTree_Reorder(mockGUI, dummyItems):
|
||||||
"""Test changing tree order.
|
"""Test changing tree order.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theTree = NWTree(theProject)
|
theTree = NWTree(theProject)
|
||||||
|
|
||||||
aHandle = []
|
aHandle = []
|
||||||
@@ -490,10 +492,10 @@ def testCoreTree_Reorder(dummyGUI, dummyItems):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_XMLPackUnpack(dummyGUI, dummyItems):
|
def testCoreTree_XMLPackUnpack(mockGUI, dummyItems):
|
||||||
"""Test packing and unpacking the tree to and from XML.
|
"""Test packing and unpacking the tree to and from XML.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theTree = NWTree(theProject)
|
theTree = NWTree(theProject)
|
||||||
|
|
||||||
for tHandle, pHandle, nwItem in dummyItems:
|
for tHandle, pHandle, nwItem in dummyItems:
|
||||||
@@ -546,10 +548,10 @@ def testCoreTree_XMLPackUnpack(dummyGUI, dummyItems):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreTree_ToCFile(monkeypatch, dummyGUI, dummyItems, tmpDir):
|
def testCoreTree_ToCFile(monkeypatch, mockGUI, dummyItems, tmpDir):
|
||||||
"""Test writing the ToC.txt file.
|
"""Test writing the ToC.txt file.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(mockGUI)
|
||||||
theTree = NWTree(theProject)
|
theTree = NWTree(theProject)
|
||||||
|
|
||||||
for tHandle, pHandle, nwItem in dummyItems:
|
for tHandle, pHandle, nwItem in dummyItems:
|
||||||
@@ -579,8 +581,7 @@ def testCoreTree_ToCFile(monkeypatch, dummyGUI, dummyItems, tmpDir):
|
|||||||
pathB = os.path.join("content", "c000000000002.nwd")
|
pathB = os.path.join("content", "c000000000002.nwd")
|
||||||
pathC = os.path.join("content", "b000000000002.nwd")
|
pathC = os.path.join("content", "b000000000002.nwd")
|
||||||
|
|
||||||
with open(os.path.join(tmpDir, nwFiles.TOC_TXT), mode="r", encoding="utf8") as inFile:
|
assert readFile(os.path.join(tmpDir, nwFiles.TOC_TXT)) == (
|
||||||
assert inFile.read() == (
|
|
||||||
"\n"
|
"\n"
|
||||||
"Table of Contents\n"
|
"Table of Contents\n"
|
||||||
"=================\n"
|
"=================\n"
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ from PyQt5.QtCore import Qt
|
|||||||
from PyQt5.QtGui import QTextCursor, QTextBlock
|
from PyQt5.QtGui import QTextCursor, QTextBlock
|
||||||
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
|
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
|
||||||
|
|
||||||
|
from tools import writeFile
|
||||||
|
|
||||||
from nw.gui.doceditor import GuiDocEditor
|
from nw.gui.doceditor import GuiDocEditor
|
||||||
from nw.enum import nwDocAction, nwDocInsert, nwWidget
|
from nw.enum import nwDocAction, nwDocInsert, nwWidget
|
||||||
from nw.constants import nwKeyWords, nwUnicode
|
from nw.constants import nwKeyWords, nwUnicode
|
||||||
@@ -659,8 +661,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
|
|||||||
assert not nwGUI.importDocument()
|
assert not nwGUI.importDocument()
|
||||||
|
|
||||||
# Create the file and try again, but with no target document open
|
# Create the file and try again, but with no target document open
|
||||||
with open(theFile, mode="w+", encoding="utf8") as outFile:
|
writeFile(theFile, "Foo")
|
||||||
outFile.write("Foo")
|
|
||||||
assert not nwGUI.importDocument()
|
assert not nwGUI.importDocument()
|
||||||
|
|
||||||
# Open the document from before, and add some text to it
|
# 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)
|
qtbot.mouseClick(nwGUI.projView, Qt.LeftButton)
|
||||||
|
|
||||||
nwGUI.projView._loadHeaderState()
|
nwGUI.projView._loadHeaderState()
|
||||||
assert not nwGUI.projView.colHidden[nwOutline.CCOUNT]
|
assert not nwGUI.projView._colHidden[nwOutline.CCOUNT]
|
||||||
|
|
||||||
# First Item
|
# First Item
|
||||||
nwGUI.rebuildOutline()
|
nwGUI.rebuildOutline()
|
||||||
|
|||||||
+4
-4
@@ -32,13 +32,13 @@ def cmpFiles(fileOne, fileTwo, ignoreLines=None):
|
|||||||
ignoreLines = []
|
ignoreLines = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
foOne = open(fileOne, mode="r", encoding="utf8")
|
foOne = open(fileOne, mode="r", encoding="utf-8")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
print(str(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
foTwo = open(fileTwo, mode="r", encoding="utf8")
|
foTwo = open(fileTwo, mode="r", encoding="utf-8")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
print(str(e))
|
||||||
return False
|
return False
|
||||||
@@ -83,14 +83,14 @@ def getGuiItem(theName):
|
|||||||
def readFile(fileName):
|
def readFile(fileName):
|
||||||
"""Returns the content of a file as a string.
|
"""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()
|
return inFile.read()
|
||||||
|
|
||||||
|
|
||||||
def writeFile(fileName, fileData):
|
def writeFile(fileName, fileData):
|
||||||
"""Write the contents of a string to a file.
|
"""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)
|
outFile.write(fileData)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user