From 3cdba90bb04642b7ccf9a182347b172225b51933 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 24 Feb 2022 21:06:28 +0100
Subject: [PATCH 1/5] Fix document margin recursion error (#1007)
---
novelwriter/gui/doceditor.py | 2 +-
novelwriter/gui/docviewer.py | 5 +++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py
index 41442385..b1760be4 100644
--- a/novelwriter/gui/doceditor.py
+++ b/novelwriter/gui/doceditor.py
@@ -1061,8 +1061,8 @@ class GuiDocEditor(QTextEdit):
"""If the text editor is resized, we must make sure the document
has its margins adjusted according to user preferences.
"""
- QTextEdit.resizeEvent(self, theEvent)
self.updateDocMargins()
+ QTextEdit.resizeEvent(self, theEvent)
return
##
diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py
index 72c142a0..4ec59570 100644
--- a/novelwriter/gui/docviewer.py
+++ b/novelwriter/gui/docviewer.py
@@ -471,10 +471,11 @@ class GuiDocViewer(QTextBrowser):
##
def resizeEvent(self, theEvent):
- """Make sure the document title is the same width as the window.
+ """If the text editor is resized, we must make sure the document
+ has its margins adjusted according to user preferences.
"""
- QTextBrowser.resizeEvent(self, theEvent)
self.updateDocMargins()
+ QTextBrowser.resizeEvent(self, theEvent)
return
def mouseReleaseEvent(self, theEvent):
From 7532fdb1fc696066cb831baad53c0c0411e9ff89 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 24 Feb 2022 21:08:42 +0100
Subject: [PATCH 2/5] Fix project file icon path in Windows installer (#1006)
---
setup/win_setup_embed.iss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/setup/win_setup_embed.iss b/setup/win_setup_embed.iss
index 84911592..7505f6c1 100644
--- a/setup/win_setup_embed.iss
+++ b/setup/win_setup_embed.iss
@@ -51,6 +51,6 @@ Filename: "{app}\pythonw.exe"; Parameters: "{#nwAppExeName}"; Description: "{cm
[Registry]
Root: HKA; Subkey: "Software\Classes\.nwx\OpenWithProgids"; ValueType: string; ValueName: "novelWriterProject.nwx"; ValueData: ""; Flags: uninsdeletevalue
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx"; ValueType: string; ValueName: ""; ValueData: "novelWriter Project File"; Flags: uninsdeletekey
-Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\assets\icons\x-novelwriter-project.ico"
+Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\novelwriter\assets\icons\x-novelwriter-project.ico"
Root: HKA; Subkey: "Software\Classes\novelWriterProject.nwx\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\pythonw.exe"" ""{app}\{#nwAppExeName}"" ""%1"""
Root: HKA; Subkey: "Software\Classes\Applications\{#nwAppExeName}\SupportedTypes"; ValueType: string; ValueName: ".nwx"; ValueData: ""; Flags: uninsdeletekey
From 1c7708babd433de4b486efd612bbab8d2fc74bd9 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 16 Mar 2022 22:34:29 +0100
Subject: [PATCH 3/5] Fix infinite search loop bug (#1016)
* Make sure the search loop in the editor cannot run infinitely
* Add test coverage of infinite search case
* Run GitHub actions against the patch branch
---
.github/workflows/syntax.yml | 2 ++
.github/workflows/test_linux.yml | 2 ++
.github/workflows/test_mac.yml | 2 ++
.github/workflows/test_win.yml | 2 ++
novelwriter/gui/doceditor.py | 14 ++++++++++----
tests/test_gui/test_gui_doceditor.py | 17 ++++++++++++-----
6 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml
index 560dd7dc..a0b56ce1 100644
--- a/.github/workflows/syntax.yml
+++ b/.github/workflows/syntax.yml
@@ -4,9 +4,11 @@ on:
push:
branches:
- main
+ - patch
pull_request:
branches:
- main
+ - patch
jobs:
checkSyntax:
diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml
index 5699d8a8..caf147ef 100644
--- a/.github/workflows/test_linux.yml
+++ b/.github/workflows/test_linux.yml
@@ -4,9 +4,11 @@ on:
push:
branches:
- main
+ - patch
pull_request:
branches:
- main
+ - patch
jobs:
testLinux:
diff --git a/.github/workflows/test_mac.yml b/.github/workflows/test_mac.yml
index c9b4497c..a011b51e 100644
--- a/.github/workflows/test_mac.yml
+++ b/.github/workflows/test_mac.yml
@@ -4,9 +4,11 @@ on:
push:
branches:
- main
+ - patch
pull_request:
branches:
- main
+ - patch
jobs:
testMac:
diff --git a/.github/workflows/test_win.yml b/.github/workflows/test_win.yml
index 85a0fda6..b41ccbf5 100644
--- a/.github/workflows/test_win.yml
+++ b/.github/workflows/test_win.yml
@@ -4,9 +4,11 @@ on:
push:
branches:
- main
+ - patch
pull_request:
branches:
- main
+ - patch
jobs:
testWin:
diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py
index b1760be4..4d35c978 100644
--- a/novelwriter/gui/doceditor.py
+++ b/novelwriter/gui/doceditor.py
@@ -1436,10 +1436,16 @@ class GuiDocEditor(QTextEdit):
theCursor.setPosition(0)
self.setTextCursor(theCursor)
- while self.find(searchFor, findOpt):
+ # Search up to a maximum of 1000, and make sure certain special
+ # searches like a regex search for .* turns into an infinite loop
+ while self.find(searchFor, findOpt) and len(resE) <= 1000:
theCursor = self.textCursor()
- resS.append(theCursor.selectionStart())
- resE.append(theCursor.selectionEnd())
+ if theCursor.hasSelection():
+ resS.append(theCursor.selectionStart())
+ resE.append(theCursor.selectionEnd())
+ else:
+ logger.warning("The search returned an empty result")
+ break
if hasSelection:
theCursor.setPosition(origA, QTextCursor.MoveAnchor)
@@ -2410,7 +2416,7 @@ class GuiDocEditSearch(QFrame):
"""Set the count values for the current search.
"""
currRes = "?" if currRes is None else currRes
- resCount = "?" if resCount is None else resCount
+ resCount = "?" if resCount is None else "1000+" if resCount > 1000 else resCount
minWidth = self.theTheme.getTextWidth(f"{resCount}//{resCount}", self.boxFont)
self.resultLabel.setText(f"{currRes}/{resCount}")
self.resultLabel.setMinimumWidth(minWidth)
diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py
index 241acd24..8057ca41 100644
--- a/tests/test_gui/test_gui_doceditor.py
+++ b/tests/test_gui/test_gui_doceditor.py
@@ -1286,13 +1286,13 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
nwGUI.theProject.projTree.setSeed(42)
- assert nwGUI.openProject(nwLipsum)
- assert nwGUI.openDocument("4c4f28287af27")
+ assert nwGUI.openProject(nwLipsum) is True
+ assert nwGUI.openDocument("4c4f28287af27") is True
origText = nwGUI.docEditor.getText()
qtbot.wait(stepDelay)
# Select the Word "est"
- assert nwGUI.docEditor.setCursorPosition(630)
+ nwGUI.docEditor.setCursorPosition(630)
nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor)
theCursor = nwGUI.docEditor.textCursor()
assert theCursor.selectedText() == "est"
@@ -1331,7 +1331,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docEditor.docSearch.isVisible() is True
# Search for non-existing
- assert nwGUI.docEditor.setCursorPosition(0)
+ nwGUI.docEditor.setCursorPosition(0)
assert nwGUI.docEditor.docSearch.setSearchText("abcdef")
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
@@ -1342,11 +1342,18 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docEditor.docSearch.isRegEx is True
# Set invalid RegEx
- assert nwGUI.docEditor.setCursorPosition(0)
+ nwGUI.docEditor.setCursorPosition(0)
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[")
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
assert nwGUI.docEditor.getCursorPosition() < 3 # No result
+ # Set dangerous RegEx (issue #1015)
+ # If this doesn't get caught, the app will hang
+ nwGUI.docEditor.setCursorPosition(0)
+ assert nwGUI.docEditor.docSearch.setSearchText(r".*")
+ qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
+ assert abs(nwGUI.docEditor.getCursorPosition() - 14) < 3
+
# Set valid RegEx
assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus")
qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay)
From 9e76498f65516a102d725c5b16d0e42ecc8378ef Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 16 Mar 2022 22:41:52 +0100
Subject: [PATCH 4/5] Update Latin American Spanish translation (#1017)
---
i18n/nw_es_419.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/i18n/nw_es_419.ts b/i18n/nw_es_419.ts
index 223d9acf..e465c54f 100644
--- a/i18n/nw_es_419.ts
+++ b/i18n/nw_es_419.ts
@@ -535,12 +535,12 @@
See also the Releases page.
+This is a bugfix and patch release that fixes two recursion/loop issues. One can cause a crash +if the window is resized rapidly, and one can cause a hang with certain search parameters in the +editor's search box. The Latin American Spanish translation has also been updated.
+