Added replace quotes function, and cleaned up some buggy behaviour with undo and how changes are tracked

This commit is contained in:
Veronica K. B. Olsen
2020-06-19 21:56:37 +02:00
parent b0380adc5f
commit 03f1142e40
3 changed files with 111 additions and 22 deletions
+2
View File
@@ -93,6 +93,8 @@ class nwDocAction(Enum):
BLOCK_H4 = 22
BLOCK_COM = 23
BLOCK_TXT = 24
REPL_SNG = 25
REPL_DBL = 26
# END Enum nwDocAction
+93 -21
View File
@@ -45,11 +45,12 @@ from PyQt5.QtWidgets import (
QFrame
)
from nw.core import NWDoc
from nw.core import NWDoc, NWSpellSimple, countWords
from nw.gui.dochighlight import GuiDocHighlighter
from nw.core import NWSpellSimple, countWords
from nw.constants import nwUnicode, nwDocAction, nwDocInsert, nwInsertSymbols
from nw.common import transferCase
from nw.constants import (
nwAlert, nwUnicode, nwDocAction, nwDocInsert, nwInsertSymbols
)
logger = logging.getLogger(__name__)
@@ -76,6 +77,7 @@ class GuiDocEditor(QTextEdit):
self.paraCount = 0
self.lastEdit = 0
self.bigDoc = False
self.doReplace = False
self.nonWord = "\"'"
# Document State
@@ -157,6 +159,7 @@ class GuiDocEditor(QTextEdit):
self.paraCount = 0
self.lastEdit = 0
self.bigDoc = False
self.doReplace = False
self.hasSelection = False
@@ -261,7 +264,9 @@ class GuiDocEditor(QTextEdit):
self.hLight.spellCheck = False
bfTime = time()
self._allowAutoReplace(False)
self.setPlainText(theDoc)
self._allowAutoReplace(True)
afTime = time()
logger.debug("Document highlighted in %.3f milliseconds" % (1000*(afTime-bfTime)))
@@ -388,8 +393,8 @@ class GuiDocEditor(QTextEdit):
"""
if self.mainConf.verQtValue >= 50900:
theText = self.qDocument.toRawText()
theText = theText.replace("\u2028", "\n") # Line separators
theText = theText.replace("\u2029", "\n") # Paragraph separators
theText = theText.replace(nwUnicode.U_LSEP, "\n") # Line separators
theText = theText.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators
else:
theText = self.toPlainText()
return theText
@@ -496,6 +501,7 @@ class GuiDocEditor(QTextEdit):
if not self.theParent.hasProject:
logger.error("No project open")
return False
self._allowAutoReplace(False)
if theAction == nwDocAction.UNDO:
self.undo()
elif theAction == nwDocAction.REDO:
@@ -544,9 +550,15 @@ class GuiDocEditor(QTextEdit):
self._formatBlock(nwDocAction.BLOCK_COM)
elif theAction == nwDocAction.BLOCK_TXT:
self._formatBlock(nwDocAction.BLOCK_TXT)
elif theAction == nwDocAction.REPL_SNG:
self._replaceQuotes("'", self.typSQOpen, self.typSQClose)
elif theAction == nwDocAction.REPL_DBL:
self._replaceQuotes("\"", self.typDQOpen, self.typDQClose)
else:
logger.debug("Unknown or unsupported document action %s" % str(theAction))
self._allowAutoReplace(True)
return False
self._allowAutoReplace(True)
return True
def isEmpty(self):
@@ -597,24 +609,23 @@ class GuiDocEditor(QTextEdit):
def keyPressEvent(self, keyEvent):
"""Intercept key press events.
We need to intercept key presses briefly to record the state of
selection. This is in order to know whether we had a selection
prior to triggering the _docChange slot, as we do not want to
trigger autoreplace on selections. Autoreplace on selections
messes with undo/redo history.
We also need to intercept the Shift key modifier for certain key
combinations that modifies standard keys like enter and space.
However, we don't want to spend a lot of time in this function
as it is triggered on every keypress when typing.
We need to intercept a few key sequences:
* The return key redirects here even if the search box has
focus. Since we need the return key to continue search, we
block any further interaction here while it's in focus.
* The undo sequence bypasses the doAction pathway from the
menu, so we redirect it back from here.
* The default redo sequence is Ctrl+Shift+Z, which we don't
use, so we block it.
"""
self.hasSelection = self.textCursor().hasSelection()
if self.docSearch.searchBox.hasFocus():
# Block the event when the focus is on the search bar as it
# seems to be capturing the return key.
return
QTextEdit.keyPressEvent(self, keyEvent)
elif keyEvent == QKeySequence.Redo:
return
elif keyEvent == QKeySequence.Undo:
self.docAction(nwDocAction.UNDO)
else:
QTextEdit.keyPressEvent(self, keyEvent)
return
def mouseReleaseEvent(self, mEvent):
@@ -650,7 +661,7 @@ class GuiDocEditor(QTextEdit):
self.setDocumentChanged(True)
if not self.wcTimer.isActive():
self.wcTimer.start()
if self.mainConf.doReplace and not self.hasSelection:
if self.doReplace and charsAdded == 1:
self._docAutoReplace(self.qDocument.findBlock(thePos))
return
@@ -862,6 +873,58 @@ class GuiDocEditor(QTextEdit):
return
def _replaceQuotes(self, sQuote, oQuote, cQuote):
"""Replace all straight quotes in the selected text.
"""
theCursor = self.textCursor()
if theCursor.hasSelection():
posS = theCursor.selectionStart()
posE = theCursor.selectionEnd()
closeCheck = (
" ", "\n", nwUnicode.U_LSEP, nwUnicode.U_PSEP
)
self._allowAutoReplace(False)
for posC in range(posS, posE+1):
theCursor.setPosition(posC)
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2)
selText = theCursor.selectedText()
nS = len(selText)
if nS == 2:
pC = selText[0]
cC = selText[1]
elif nS == 1:
pC = " "
cC = selText[0]
else:
continue
if cC != sQuote:
continue
theCursor.clearSelection()
theCursor.setPosition(posC)
if pC in closeCheck:
theCursor.beginEditBlock()
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
theCursor.insertText(oQuote)
theCursor.endEditBlock()
else:
theCursor.beginEditBlock()
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
theCursor.insertText(cQuote)
theCursor.endEditBlock()
self._allowAutoReplace(True)
else:
self.theParent.makeAlert(
"Please selection some text before calling replace quotes.", nwAlert.ERROR
)
return
def _checkDocSize(self, theSize):
"""Check if document size crosses the big document limit set in
config. If so, we will set the big document flag to True.
@@ -1225,6 +1288,15 @@ class GuiDocEditor(QTextEdit):
return
def _allowAutoReplace(self, theState):
"""used to enable/disable the auto-replace feature temporarily.
"""
if theState:
self.doReplace = self.mainConf.doReplace
else:
self.doReplace = False
return
# END Class GuiDocEditor
# =============================================================================================== #
+16 -1
View File
@@ -577,7 +577,7 @@ class GuiMainMenu(QMenuBar):
self.aFmtSQuote.triggered.connect(lambda: self._docAction(nwDocAction.S_QUOTE))
self.fmtMenu.addAction(self.aFmtSQuote)
# Edit > Separator
# Format > Separator
self.fmtMenu.addSeparator()
# Format > Header 1
@@ -622,6 +622,21 @@ class GuiMainMenu(QMenuBar):
self.aFmtNoFormat.triggered.connect(lambda: self._docAction(nwDocAction.BLOCK_TXT))
self.fmtMenu.addAction(self.aFmtNoFormat)
# Format > Separator
self.fmtMenu.addSeparator()
# Format > Replace Single Quotes
self.aFmtReplSng = QAction("Replace Single Quotes", self)
self.aFmtReplSng.setStatusTip("Replace all straight single quotes in selected text")
self.aFmtReplSng.triggered.connect(lambda: self._docAction(nwDocAction.REPL_SNG))
self.fmtMenu.addAction(self.aFmtReplSng)
# Format > Replace Double Quotes
self.aFmtReplDbl = QAction("Replace Double Quotes", self)
self.aFmtReplDbl.setStatusTip("Replace all straight double quotes in selected text")
self.aFmtReplDbl.triggered.connect(lambda: self._docAction(nwDocAction.REPL_DBL))
self.fmtMenu.addAction(self.aFmtReplDbl)
return
def _buildViewMenu(self):