Added replace quotes function, and cleaned up some buggy behaviour with undo and how changes are tracked
This commit is contained in:
+93
-21
@@ -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
|
||||
|
||||
# =============================================================================================== #
|
||||
|
||||
Reference in New Issue
Block a user