From d8f962cf845215e4680be14129133373bcf521d0 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" Date: Sat, 20 Apr 2019 17:46:00 +0200 Subject: [PATCH] Added text autoreplace functionality to doc editor. --- nw/config.py | 34 ++++++++++++++++---- nw/gui/doceditor.py | 71 +++++++++++++++++++++++++++++++++++++++--- nw/project/document.py | 24 ++++++++++++-- nw/themes/default.css | 10 ++++++ 4 files changed, 126 insertions(+), 13 deletions(-) diff --git a/nw/config.py b/nw/config.py index 182048e6..0ed2b914 100644 --- a/nw/config.py +++ b/nw/config.py @@ -58,9 +58,14 @@ class Config: self.mainPanePos = [300, 800] ## Text Editor - self.textFixedW = True - self.textWidth = 600 - self.textMargin = [40, 40] + self.textFixedW = True + self.textWidth = 600 + self.textMargin = [40, 40] + self.doReplace = True + self.doReplaceQuote = True + self.doReplaceDash = True + self.doReplaceDots = True + self.replaceQuotes = ["“","”"] # Check if config file exists if path.isfile(path.join(self.confPath,self.confFile)): @@ -104,13 +109,25 @@ class Config: cnfSec = "Editor" if confParser.has_section(cnfSec): if confParser.has_option(cnfSec,"fixedwidth"): - self.textFixedW = confParser.getboolean(cnfSec,"fixedwidth") + self.textFixedW = confParser.getboolean(cnfSec,"fixedwidth") if confParser.has_option(cnfSec,"width"): - self.textWidth = confParser.getint(cnfSec,"width") + self.textWidth = confParser.getint(cnfSec,"width") if confParser.has_option(cnfSec,"margins"): - self.textMargin = self.unpackList( + self.textMargin = self.unpackList( confParser.get(cnfSec,"margins"), 2, self.textMargin ) + if confParser.has_option(cnfSec,"autoreplace"): + self.doReplace = confParser.getboolean(cnfSec,"autoreplace") + if confParser.has_option(cnfSec,"repquotes"): + self.doReplaceQuote = confParser.getboolean(cnfSec,"repquotes") + if confParser.has_option(cnfSec,"repdash"): + self.doReplaceDash = confParser.getboolean(cnfSec,"repdash") + if confParser.has_option(cnfSec,"repdots"): + self.doReplaceDots = confParser.getboolean(cnfSec,"repdots") + if confParser.has_option(cnfSec,"usequotes"): + self.replaceQuotes = self.unpackList( + confParser.get(cnfSec,"usequotes"), 2, self.replaceQuotes + ) ## Path cnfSec = "Path" @@ -146,6 +163,11 @@ class Config: confParser.set(cnfSec,"fixedwidth", str(self.textFixedW)) confParser.set(cnfSec,"width", str(self.textWidth)) confParser.set(cnfSec,"margins", self.packList(self.textMargin)) + confParser.set(cnfSec,"autoreplace",str(self.doReplace)) + confParser.set(cnfSec,"repquotes", str(self.doReplaceQuote)) + confParser.set(cnfSec,"repdash", str(self.doReplaceDash)) + confParser.set(cnfSec,"repdots", str(self.doReplaceDots)) + confParser.set(cnfSec,"usequotes", self.packList(self.replaceQuotes)) ## Path cnfSec = "Path" diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index 5b272d73..dc824aad 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -13,6 +13,8 @@ import logging import nw +from time import time + from PyQt5.QtWidgets import QWidget, QTextEdit, QHBoxLayout, QVBoxLayout, QFrame, QSplitter, QToolBar, QAction, QScrollArea from PyQt5.QtCore import Qt, QSize, QSizeF from PyQt5.QtGui import QIcon, QFont, QTextCursor, QTextFormat, QTextBlockFormat @@ -23,11 +25,14 @@ logger = logging.getLogger(__name__) class GuiDocEditor(QWidget): - def __init__(self): + def __init__(self, theParent): QWidget.__init__(self) logger.debug("Initialising DocEditor ...") self.mainConf = nw.CONFIG + self.theParent = theParent + self.charCount = 0 + self.lineCount = 0 self.outerBox = QVBoxLayout() self.guiEditor = QTextEdit() @@ -51,9 +56,10 @@ class GuiDocEditor(QWidget): self.guiEditor.setMinimumWidth(400) self.guiEditor.setAcceptRichText(False) - theDoc = self.guiEditor.document() - # theDoc.setDefaultFont(QFont("Source Sans Pro",13)) - theDoc.setDocumentMargin(0) + + self.theDoc = self.guiEditor.document() + self.theDoc.setDocumentMargin(0) + self.theDoc.contentsChange.connect(self._docChange) logger.debug("DocEditor initialisation complete") @@ -61,6 +67,7 @@ class GuiDocEditor(QWidget): def setText(self, theText): self.guiEditor.setPlainText(theText) + self.charCount = self.theDoc.characterCount() return True def getText(self): @@ -76,10 +83,64 @@ class GuiDocEditor(QWidget): sW = self.guiEditor.verticalScrollBar().width() tM = int((tW - sW - self.mainConf.textWidth)/2) mTB = self.mainConf.textMargin[0] - # print(tW, sW, tM, mTB) self.guiEditor.setViewportMargins(tM,mTB,0,mTB) return + def _docChange(self, thePos, charsRemoved, charsAdded): + tStart = time() + # logger.debug("Contents changed: %d %d %d" % (thePos, charsRemoved, charsAdded)) + self.charCount = self.theDoc.characterCount() + self.lineCount = self.theDoc.lineCount() + self.theParent.statusBar.setCharCount(self.charCount) + if self.mainConf.doReplace: + self._docAutoReplace(self.theDoc.findBlock(thePos)) + logger.verbose("Doc change signal took %.3f µs" % ((time()-tStart)*1e6)) + return + + def _docAutoReplace(self, theBlock): + + theText = theBlock.text() + theCursor = self.guiEditor.textCursor() + thePos = theCursor.positionInBlock() + theLen = len(theText) + + if theLen < 1 or thePos-1 > theLen: + return + + theOne = theText[thePos-1:thePos] + theTwo = theText[thePos-2:thePos] + theThree = theText[thePos-3:thePos] + + if self.mainConf.doReplaceQuote and theOne == "\"": + qOpen = self.mainConf.replaceQuotes[0] + qClose = self.mainConf.replaceQuotes[1] + nOpen = theText.count(qOpen) + nClose = theText.count(qClose) + if nOpen > nClose: + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().insertText(qClose) + else: + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().insertText(qOpen) + + if self.mainConf.doReplaceDash and theTwo == "--": + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().insertText("–") + + if self.mainConf.doReplaceDash and theTwo == "–-": + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().insertText("—") + + if self.mainConf.doReplaceDots and theThree == "...": + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().deletePreviousChar() + self.guiEditor.textCursor().insertText("…") + + return + def _buildTabToolBar(self): toolBar = self.editToolBar toolBar.setToolButtonStyle(Qt.ToolButtonIconOnly) diff --git a/nw/project/document.py b/nw/project/document.py index 182374dc..1a1fd99f 100644 --- a/nw/project/document.py +++ b/nw/project/document.py @@ -23,12 +23,18 @@ class NWDoc(): FILE_MN = "main.nwd" - def __init__(self, theProject): + def __init__(self, theProject, theParent): self.mainConf = nw.CONFIG self.theProject = theProject - self.docHandle = None + self.theParent = theParent self.theItem = None + self.docHandle = None + + # Document Info + self.charCount = None + self.lineCount = None + self.wordCount = None return @@ -36,6 +42,7 @@ class NWDoc(): self.docHandle = tHandle self.theItem = self.theProject.getItem(tHandle) + self.theParent.statusBar.setDocHandleCount(tHandle) docDir, docFile = self._assemblePath(self.FILE_MN) logger.debug("Opening document %s" % path.join(docDir,docFile)) @@ -78,6 +85,7 @@ class NWDoc(): docAna = TextAnalysis(docText,"en_GB") wC, sC, pC = docAna.getStats() + # rScr, gLev = docAna.getReadabilityScore() self.theItem.setWordCount(wC) self.theItem.setSentCount(sC) @@ -85,6 +93,18 @@ class NWDoc(): return True + ## + # Setters + ## + + def setCharCount(self, theCount): + self.charCount = theCount + return + + def setLineCount(self, theCount): + self.lineCount = theCount + return + ## # Internal Functions ## diff --git a/nw/themes/default.css b/nw/themes/default.css index 65ab3161..1eaf6b4f 100644 --- a/nw/themes/default.css +++ b/nw/themes/default.css @@ -6,3 +6,13 @@ QTextEdit { background-color: #141414; color: #c7cfd0; } + +QStatusBar::item { + background-color: rgba(0,0,0,0.1); + border: 1px solid #666666; +} + +QStatusBar::item QLabel { + background-color: rgba(0,0,0,0.0); + padding: 1px 4px; +}