Merge branch 'main' into release_2.0rc1
This commit is contained in:
@@ -34,7 +34,7 @@ The following Python packages are needed to run novelWriter:
|
|||||||
* ``lxml`` – needed for full XML support.
|
* ``lxml`` – needed for full XML support.
|
||||||
* ``PyEnchant`` – needed for spell checking (optional).
|
* ``PyEnchant`` – needed for spell checking (optional).
|
||||||
|
|
||||||
PyQt/Qt should be at least 5.3, but ideally 5.10 or higher for nearly all features to work. For
|
PyQt/Qt should be at least 5.10, but ideally 5.13 or higher for nearly all features to work. For
|
||||||
instance, searching using regular expressions with full Unicode support requires 5.13. There is no
|
instance, searching using regular expressions with full Unicode support requires 5.13. There is no
|
||||||
known minimum version requirement for package ``lxml``, but the code was originally written with
|
known minimum version requirement for package ``lxml``, but the code was originally written with
|
||||||
4.2, which is therefore set as the minimum. It may work on lower versions. You have to test it.
|
4.2, which is therefore set as the minimum. It may work on lower versions. You have to test it.
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ Application Data
|
|||||||
|
|
||||||
novelWriter also stores a bit of data that is generated by the user's actions. This includes the
|
novelWriter also stores a bit of data that is generated by the user's actions. This includes the
|
||||||
list of recent projects form the :guilabel:`Open Project` dialog. Custom themes are also saved
|
list of recent projects form the :guilabel:`Open Project` dialog. Custom themes are also saved
|
||||||
here. The system paths are provided by the Qt QStandardPaths_ class and its AppDataLocation value
|
here. The system paths are provided by the Qt QStandardPaths_ class and its AppDataLocation.
|
||||||
on Qt 5.4 or greater, or DataLocation for earlier versions.
|
|
||||||
|
|
||||||
The standard paths are:
|
The standard paths are:
|
||||||
|
|
||||||
|
|||||||
@@ -214,14 +214,14 @@ def main(sysArgs=None):
|
|||||||
"At least Python 3.7 is required, found %s" % CONFIG.verPyString
|
"At least Python 3.7 is required, found %s" % CONFIG.verPyString
|
||||||
)
|
)
|
||||||
errorCode |= 0x04
|
errorCode |= 0x04
|
||||||
if CONFIG.verQtValue < 50300:
|
if CONFIG.verQtValue < 51000:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least Qt5 version 5.3 is required, found %s" % CONFIG.verQtString
|
"At least Qt5 version 5.10 is required, found %s" % CONFIG.verQtString
|
||||||
)
|
)
|
||||||
errorCode |= 0x08
|
errorCode |= 0x08
|
||||||
if CONFIG.verPyQtValue < 50300:
|
if CONFIG.verPyQtValue < 51000:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least PyQt5 version 5.3 is required, found %s" % CONFIG.verPyQtString
|
"At least PyQt5 version 5.10 is required, found %s" % CONFIG.verPyQtString
|
||||||
)
|
)
|
||||||
errorCode |= 0x10
|
errorCode |= 0x10
|
||||||
|
|
||||||
|
|||||||
+3
-11
@@ -220,8 +220,8 @@ class Config:
|
|||||||
self.osUnknown = True
|
self.osUnknown = True
|
||||||
|
|
||||||
# Other System Info
|
# Other System Info
|
||||||
self.hostName = "Unknown"
|
self.hostName = QSysInfo.machineHostName()
|
||||||
self.kernelVer = "Unknown"
|
self.kernelVer = QSysInfo.kernelVersion()
|
||||||
|
|
||||||
# Packages
|
# Packages
|
||||||
self.hasEnchant = False # The pyenchant package
|
self.hasEnchant = False # The pyenchant package
|
||||||
@@ -264,10 +264,7 @@ class Config:
|
|||||||
self.confPath = confPath
|
self.confPath = confPath
|
||||||
|
|
||||||
if dataPath is None:
|
if dataPath is None:
|
||||||
if self.verQtValue >= 50400:
|
dataRoot = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
|
||||||
dataRoot = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
|
|
||||||
else:
|
|
||||||
dataRoot = QStandardPaths.writableLocation(QStandardPaths.DataLocation)
|
|
||||||
self.dataPath = os.path.join(os.path.abspath(dataRoot), self.appHandle)
|
self.dataPath = os.path.join(os.path.abspath(dataRoot), self.appHandle)
|
||||||
else:
|
else:
|
||||||
logger.info("Setting data path from alternative path: %s", dataPath)
|
logger.info("Setting data path from alternative path: %s", dataPath)
|
||||||
@@ -347,11 +344,6 @@ class Config:
|
|||||||
self.errData.append(formatException(exc))
|
self.errData.append(formatException(exc))
|
||||||
self.dataPath = None
|
self.dataPath = None
|
||||||
|
|
||||||
# Host and Kernel
|
|
||||||
if self.verQtValue >= 50600:
|
|
||||||
self.hostName = QSysInfo.machineHostName()
|
|
||||||
self.kernelVer = QSysInfo.kernelVersion()
|
|
||||||
|
|
||||||
# Load recent projects cache
|
# Load recent projects cache
|
||||||
self.loadRecentCache()
|
self.loadRecentCache()
|
||||||
|
|
||||||
|
|||||||
@@ -303,10 +303,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||||
|
|
||||||
# Refresh the tab stops
|
# Refresh the tab stops
|
||||||
if self.mainConf.verQtValue >= 51000:
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
|
||||||
else: # pragma: no cover
|
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
|
||||||
|
|
||||||
# Initialise the syntax highlighter
|
# Initialise the syntax highlighter
|
||||||
self.highLight.initHighlighter()
|
self.highLight.initHighlighter()
|
||||||
@@ -604,19 +601,15 @@ class GuiDocEditor(QTextEdit):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def getText(self):
|
def getText(self):
|
||||||
"""Get the text content of the current document. This method
|
"""Get the text content of the current document. This method uses
|
||||||
uses QTextEdit->toPlainText for Qt versions lower than 5.9, and
|
QTextDocument->toRawText instead of toPlainText(). The former preserves
|
||||||
the QTextDocument->toRawText for higher version. The latter
|
non-breaking spaces, the latter does not. We still want to get rid of
|
||||||
preserves non-breaking spaces, which the former does not.
|
page and line separators though.
|
||||||
We still want to get rid of page and line separators though.
|
|
||||||
See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText
|
See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText
|
||||||
"""
|
"""
|
||||||
if self.mainConf.verQtValue >= 50900:
|
theText = self.document().toRawText()
|
||||||
theText = self.document().toRawText()
|
theText = theText.replace(nwUnicode.U_LSEP, "\n") # Line separators
|
||||||
theText = theText.replace(nwUnicode.U_LSEP, "\n") # Line separators
|
theText = theText.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators
|
||||||
theText = theText.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators
|
|
||||||
else:
|
|
||||||
theText = self.toPlainText()
|
|
||||||
return theText
|
return theText
|
||||||
|
|
||||||
def getCursorPosition(self):
|
def getCursorPosition(self):
|
||||||
@@ -2478,7 +2471,8 @@ class GuiDocEditSearch(QFrame):
|
|||||||
self._alertSearchValid(theRegEx.isValid())
|
self._alertSearchValid(theRegEx.isValid())
|
||||||
return theRegEx
|
return theRegEx
|
||||||
|
|
||||||
else: # >= 50300 to < 51300
|
else: # pragma: no cover
|
||||||
|
# >= 50300 to < 51300
|
||||||
if self.isCaseSense:
|
if self.isCaseSense:
|
||||||
rxOpt = Qt.CaseSensitive
|
rxOpt = Qt.CaseSensitive
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -150,10 +150,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||||
|
|
||||||
# Refresh the tab stops
|
# Refresh the tab stops
|
||||||
if self.mainConf.verQtValue >= 51000:
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
|
||||||
else:
|
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
|
||||||
|
|
||||||
# If we have a document open, we should reload it in case the font changed
|
# If we have a document open, we should reload it in case the font changed
|
||||||
if self._docHandle is not None:
|
if self._docHandle is not None:
|
||||||
@@ -193,10 +190,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Refresh the tab stops
|
# Refresh the tab stops
|
||||||
if self.mainConf.verQtValue >= 51000:
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
|
||||||
else:
|
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
|
||||||
|
|
||||||
# Must be before setHtml
|
# Must be before setHtml
|
||||||
if updateHistory:
|
if updateHistory:
|
||||||
|
|||||||
@@ -1265,10 +1265,7 @@ class GuiBuildNovelDocView(QTextBrowser):
|
|||||||
self.setFont(theFont)
|
self.setFont(theFont)
|
||||||
|
|
||||||
# Set the tab stops
|
# Set the tab stops
|
||||||
if self.mainConf.verQtValue >= 51000:
|
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
|
||||||
else:
|
|
||||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
|
||||||
|
|
||||||
docPalette = self.palette()
|
docPalette = self.palette()
|
||||||
docPalette.setColor(QPalette.Base, QColor(255, 255, 255))
|
docPalette.setColor(QPalette.Base, QColor(255, 255, 255))
|
||||||
|
|||||||
@@ -96,12 +96,6 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
|
|||||||
# Let the config class figure out the path
|
# Let the config class figure out the path
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("PyQt5.QtCore.QStandardPaths.writableLocation", lambda *a: fncDir)
|
mp.setattr("PyQt5.QtCore.QStandardPaths.writableLocation", lambda *a: fncDir)
|
||||||
tstConf.verQtValue = 50600
|
|
||||||
tstConf.initConfig()
|
|
||||||
assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle)
|
|
||||||
assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle)
|
|
||||||
assert not os.path.isfile(confFile)
|
|
||||||
tstConf.verQtValue = 50000
|
|
||||||
tstConf.initConfig()
|
tstConf.initConfig()
|
||||||
assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle)
|
assert tstConf.confPath == os.path.join(fncDir, tstConf.appHandle)
|
||||||
assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle)
|
assert tstConf.dataPath == os.path.join(fncDir, tstConf.appHandle)
|
||||||
|
|||||||
@@ -213,7 +213,8 @@ def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
qtbot.wait(stepDelay)
|
qtbot.wait(stepDelay)
|
||||||
|
|
||||||
# Get Text
|
# Get Text
|
||||||
# Both methods should return the same result for line breaks, but not for spaces
|
# This should replace line and paragraph separators, but preserve
|
||||||
|
# non-breaking spaces.
|
||||||
newText = (
|
newText = (
|
||||||
"### New Scene\u2029\u2029"
|
"### New Scene\u2029\u2029"
|
||||||
"Some\u2028text.\u2029"
|
"Some\u2028text.\u2029"
|
||||||
@@ -221,10 +222,6 @@ def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
)
|
)
|
||||||
assert nwGUI.docEditor.replaceText(newText)
|
assert nwGUI.docEditor.replaceText(newText)
|
||||||
assert nwGUI.docEditor.getText() == "### New Scene\n\nSome\ntext.\nMore\u00a0text.\n"
|
assert nwGUI.docEditor.getText() == "### New Scene\n\nSome\ntext.\nMore\u00a0text.\n"
|
||||||
verQtValue = nwGUI.mainConf.verQtValue
|
|
||||||
nwGUI.mainConf.verQtValue = 50800
|
|
||||||
assert nwGUI.docEditor.getText() == "### New Scene\n\nSome\ntext.\nMore text.\n"
|
|
||||||
nwGUI.mainConf.verQtValue = verQtValue
|
|
||||||
|
|
||||||
# Check Propertoes
|
# Check Propertoes
|
||||||
assert nwGUI.docEditor.docChanged() is True
|
assert nwGUI.docEditor.docChanged() is True
|
||||||
@@ -250,7 +247,7 @@ def testGuiEditor_MetaData(qtbot, monkeypatch, nwGUI, nwMinimal):
|
|||||||
nwGUI.docEditor.setDocumentChanged(True)
|
nwGUI.docEditor.setDocumentChanged(True)
|
||||||
assert nwGUI.docEditor._docChanged is True
|
assert nwGUI.docEditor._docChanged is True
|
||||||
|
|
||||||
# qtbot.stopForInteraction()
|
# qtbot.stop()
|
||||||
|
|
||||||
# END Test testGuiEditor_MetaData
|
# END Test testGuiEditor_MetaData
|
||||||
|
|
||||||
|
|||||||
@@ -566,10 +566,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
nwGUI.mainMenu.aInsNBSpace.activate(QAction.Trigger)
|
nwGUI.mainMenu.aInsNBSpace.activate(QAction.Trigger)
|
||||||
if nwGUI.mainConf.verQtValue >= 50900:
|
assert nwGUI.docEditor.getText() == nwUnicode.U_NBSP
|
||||||
assert nwGUI.docEditor.getText() == nwUnicode.U_NBSP
|
|
||||||
else:
|
|
||||||
assert nwGUI.docEditor.getText() == " "
|
|
||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
nwGUI.mainMenu.aInsThinSpace.activate(QAction.Trigger)
|
nwGUI.mainMenu.aInsThinSpace.activate(QAction.Trigger)
|
||||||
@@ -577,10 +574,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
|
|||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
nwGUI.mainMenu.aInsThinNBSpace.activate(QAction.Trigger)
|
nwGUI.mainMenu.aInsThinNBSpace.activate(QAction.Trigger)
|
||||||
if nwGUI.mainConf.verQtValue >= 50900:
|
assert nwGUI.docEditor.getText() == nwUnicode.U_THNBSP
|
||||||
assert nwGUI.docEditor.getText() == nwUnicode.U_THNBSP
|
|
||||||
else:
|
|
||||||
assert nwGUI.docEditor.getText() == " "
|
|
||||||
nwGUI.docEditor.clear()
|
nwGUI.docEditor.clear()
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
Reference in New Issue
Block a user