From 32caf6aa04755a27ef9165105e2485629db91403 Mon Sep 17 00:00:00 2001 From: Jan Wester Date: Sat, 10 Jul 2021 17:43:58 +0200 Subject: [PATCH] Partial fix for erroneous nbsp->space conversion QDocument::toPlainText() has the stupid decision to convert nbsp to spaces in it, which our users obviously hate. Unfortunately, this is out of our control to fix completely. It is a very deep rabbit hole. :( Typing non-breaking spaces in the editor now works. Reopening these files at a later point has these characters remain intact. What does NOT work is copy-pasting non-breaking spaces. These will end up looking like normal spaces when you paste them somewhere else, be it in Manuskript or some other document. In other words: it is impossible for users to verify whether something is a non-breaking space or an ordinary one. I realize that it makes this partial fix meaningless for many. Sorry. :/ Partially fixes issue 738. --- manuskript/models/outlineItem.py | 7 ++++++- manuskript/ui/views/textEditView.py | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/manuskript/models/outlineItem.py b/manuskript/models/outlineItem.py index 97160aa2..bcb46ae0 100644 --- a/manuskript/models/outlineItem.py +++ b/manuskript/models/outlineItem.py @@ -98,7 +98,7 @@ class outlineItem(abstractItem, searchableItem): ) __repr__ = __str__ - + def charCount(self): return self._data.get(self.enum.charCount, 0) @@ -116,6 +116,9 @@ class outlineItem(abstractItem, searchableItem): return [] else: + # Used to verify nbsp characters not getting clobbered. + #if column == E.text: + # print("GET", str(role), "-->", str([hex(ord(x)) for x in data])) return data elif role == Qt.DecorationRole and column == E.title: @@ -158,6 +161,8 @@ class outlineItem(abstractItem, searchableItem): # Stuff to do before if column == E.text: self.addRevision() + # Used to verify nbsp characters not getting clobbered. + #print("SET", str(role), "-->", str([hex(ord(x)) for x in data])) # Calling base class implementation abstractItem.setData(self, column, data, role) diff --git a/manuskript/ui/views/textEditView.py b/manuskript/ui/views/textEditView.py index b4f07e9d..0c5a7121 100644 --- a/manuskript/ui/views/textEditView.py +++ b/manuskript/ui/views/textEditView.py @@ -20,7 +20,11 @@ from manuskript.models.characterModel import Character, CharacterInfo import logging LOGGER = logging.getLogger(__name__) +# See implementation of QTextDocument::toPlainText() +PLAIN_TRANSLATION_TABLE = {0x2028: "\n", 0x2029: "\n", 0xfdd0: "\n", 0xfdd1: "\n"} + class textEditView(QTextEdit): + def __init__(self, parent=None, index=None, html=None, spellcheck=None, highlighting=False, dict="", autoResize=False): QTextEdit.__init__(self, parent) @@ -278,13 +282,20 @@ class textEditView(QTextEdit): def reconnectDocument(self): self.document().contentsChanged.connect(self.updateTimer.start, F.AUC) + def toIdealText(self): + """QTextDocument::toPlainText() replaces NBSP with spaces, which we don't want. + QTextDocument::toRawText() replaces nothing, but that leaves fancy paragraph and line separators that users would likely complain about. + This reimplements toPlainText(), except without the NBSP destruction.""" + return self.document().toRawText().translate(PLAIN_TRANSLATION_TABLE) + toPlainText = toIdealText + def updateText(self): self._updating.lock() # LOGGER.debug("Updating %s", self.objectName()) if self._index: self.disconnectDocument() - if self.toPlainText() != F.toString(self._index.data()): + if self.toIdealText() != F.toString(self._index.data()): # LOGGER.debug(" Updating plaintext") self.document().setPlainText(F.toString(self._index.data())) self.reconnectDocument() @@ -319,7 +330,7 @@ class textEditView(QTextEdit): self.updateTimer.stop() self._updating.lock() - text = self.toPlainText() + text = self.toIdealText() self._updating.unlock() # LOGGER.debug("Submitting %s", self.objectName())