Merge branch 'master' into improvements

This commit is contained in:
Veronica K. B. Olsen
2019-06-08 22:29:02 +02:00
18 changed files with 265 additions and 67 deletions
+16 -2
View File
@@ -11,6 +11,7 @@
"""
import logging
import re
import nw
from nw.convert.tokenizer import Tokenizer
@@ -24,6 +25,19 @@ class ToHtml(Tokenizer):
return
def doAutoReplace(self):
Tokenizer.doAutoReplace(self)
theDict = {
"<" : "&lt;",
">" : "&gt;",
"&" : "&amp;",
}
xRep = re.compile("|".join([re.escape(k) for k in theDict.keys()]), flags=re.DOTALL)
self.theText = xRep.sub(lambda x: theDict[x.group(0)], self.theText)
return
def doConvert(self):
htmlTags = {
@@ -31,8 +45,8 @@ class ToHtml(Tokenizer):
self.FMT_B_E : "</strong>",
self.FMT_I_B : "<em>",
self.FMT_I_E : "</em>",
self.FMT_U_B : "<mark>",
self.FMT_U_E : "</mark>",
self.FMT_U_B : "<u>",
self.FMT_U_E : "</u>",
}
self.theResult = ""
+10
View File
@@ -11,6 +11,7 @@
"""
import logging
import re
import nw
from operator import itemgetter
@@ -57,6 +58,15 @@ class Tokenizer():
return
def doAutoReplace(self):
if len(self.theProject.autoReplace) > 0:
theDict = {}
for aKey, aVal in self.theProject.autoReplace.items():
theDict["<%s>" % aKey] = aVal
xRep = re.compile("|".join([re.escape(k) for k in theDict.keys()]), flags=re.DOTALL)
self.theText = xRep.sub(lambda x: theDict[x.group(0)], self.theText)
return
def tokenizeText(self):
"""Scan the text for either lines starting with specific characters that indicate headers,
comments, commands etc, or just contains plain text. in the case of plain text, apply the
+13 -7
View File
@@ -45,6 +45,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.colVal = QColor(*self.theTheme.colVal)
self.colSpell = QColor(*self.theTheme.colSpell)
self.colTagErr = QColor(*self.theTheme.colTagErr)
self.colRepTag = QColor(*self.theTheme.colRepTag)
self.hStyles = {
"header1" : self._makeFormat(self.colHead, "bold",1.8),
@@ -62,6 +63,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
"dialogue1" : self._makeFormat(self.colDialN),
"dialogue2" : self._makeFormat(self.colDialD),
"dialogue3" : self._makeFormat(self.colDialS),
"replace" : self._makeFormat(self.colRepTag),
"hidden" : self._makeFormat(self.colComm),
"keyword" : self._makeFormat(self.colKey),
"value" : self._makeFormat(self.colVal),
@@ -148,6 +150,12 @@ class GuiDocHighlighter(QSyntaxHighlighter):
}
))
self.hRules.append((
"<(\S+?)>", {
0 : self.hStyles["replace"],
}
))
# Build a QRegExp for each pattern and for the spell checker
self.rules = [(QRegularExpression(a),b) for (a,b) in self.hRules]
self.spellRx = QRegularExpression(r"\b[^\s]+\b")
@@ -179,7 +187,6 @@ class GuiDocHighlighter(QSyntaxHighlighter):
def highlightBlock(self, theText):
if self.theHandle is None:
self.setCurrentBlockState(0)
return
if theText.startswith("@"):
@@ -213,8 +220,6 @@ class GuiDocHighlighter(QSyntaxHighlighter):
xLen = rxMatch.capturedLength(xM)
self.setFormat(xPos, xLen, xFmt[xM])
self.setCurrentBlockState(0)
if self.theDict is None or not self.spellCheck or theText.startswith("@"):
return
@@ -226,10 +231,11 @@ class GuiDocHighlighter(QSyntaxHighlighter):
continue
xPos = rxMatch.capturedStart(0)
xLen = rxMatch.capturedLength(0)
spFmt = self.format(xPos)
spFmt.setUnderlineColor(self.colSpell)
spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
self.setFormat(xPos, xLen, spFmt)
for x in range(xLen):
spFmt = self.format(xPos+x)
spFmt.setUnderlineColor(self.colSpell)
spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
self.setFormat(xPos+x, 1, spFmt)
return
+90 -8
View File
@@ -16,12 +16,12 @@ import nw
from os import path
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush
from PyQt5.QtGui import QIcon, QPixmap, QColor, QBrush, QStandardItemModel
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtWidgets import (
QDialog, QHBoxLayout, QVBoxLayout, QFormLayout, QLineEdit, QPlainTextEdit, QLabel,
QWidget, QTabWidget, QDialogButtonBox, QListWidget, QListWidgetItem, QPushButton,
QColorDialog, QAbstractItemView
QColorDialog, QAbstractItemView, QTreeWidget, QTreeWidgetItem
)
from nw.enum import nwAlert
@@ -47,14 +47,16 @@ class GuiProjectEditor(QDialog):
self.svgGradient.setFixedWidth(80)
self.theProject.countStatus()
self.tabMain = GuiProjectEditMain(self.theParent, self.theProject)
self.tabStatus = GuiProjectEditStatus(self.theParent, self.theProject.statusItems)
self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject.importItems)
self.tabMain = GuiProjectEditMain(self.theParent, self.theProject)
self.tabStatus = GuiProjectEditStatus(self.theParent, self.theProject.statusItems)
self.tabImport = GuiProjectEditStatus(self.theParent, self.theProject.importItems)
self.tabReplace = GuiProjectEditReplace(self.theParent, self.theProject)
self.tabWidget = QTabWidget()
self.tabWidget.addTab(self.tabMain, "Settings")
self.tabWidget.addTab(self.tabStatus,"Status")
self.tabWidget.addTab(self.tabImport,"Importance")
self.tabWidget.addTab(self.tabMain, "Settings")
self.tabWidget.addTab(self.tabStatus, "Status")
self.tabWidget.addTab(self.tabImport, "Importance")
self.tabWidget.addTab(self.tabReplace,"Auto-Replace")
self.setLayout(self.outerBox)
self.outerBox.addWidget(self.svgGradient)
@@ -91,6 +93,9 @@ class GuiProjectEditor(QDialog):
self.theProject.setImportColours(importCol)
if self.tabStatus.colChanged or self.tabImport.colChanged:
self.theParent.rebuildTree()
if self.tabReplace.arChanged:
newList = self.tabReplace.getNewList()
self.theProject.setAutoReplace(newList)
self.close()
@@ -292,3 +297,80 @@ class GuiProjectEditStatus(QWidget):
return
# END Class GuiProjectEditStatus
class GuiProjectEditReplace(QWidget):
def __init__(self, theParent, theProject):
QWidget.__init__(self, theParent)
self.theParent = theParent
self.theProject = theProject
self.arChanged = False
self.outerBox = QVBoxLayout()
self.bottomBox = QHBoxLayout()
self.listBox = QTreeWidget()
self.listBox.setHeaderLabels(["Keyword","Replace With"])
self.listBox.setIndentation(0)
for aKey, aVal in self.theProject.autoReplace.items():
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
self.listBox.addTopLevelItem(newItem)
self.editKey = QLineEdit()
self.editValue = QLineEdit()
self.addButton = QPushButton(QIcon.fromTheme("list-add"),"")
self.delButton = QPushButton(QIcon.fromTheme("list-remove"),"")
self.addButton.clicked.connect(self._addEntry)
self.delButton.clicked.connect(self._delEntry)
self.bottomBox.addWidget(self.editKey, 2)
self.bottomBox.addWidget(self.editValue, 3)
self.bottomBox.addWidget(self.addButton)
self.bottomBox.addWidget(self.delButton)
self.outerBox.addWidget(self.listBox)
self.outerBox.addLayout(self.bottomBox)
self.setLayout(self.outerBox)
return
def _addEntry(self):
newKey = self.editKey.text()
newVal = self.editValue.text()
saveKey = ""
for c in newKey:
if not c .isspace():
saveKey += c
if len(saveKey) > 0 and len(newVal) > 0:
newItem = QTreeWidgetItem(["<%s>" % saveKey, newVal])
self.listBox.addTopLevelItem(newItem)
self.editKey.clear()
self.editValue.clear()
self.arChanged = True
return True
def _delEntry(self):
selItem = self.listBox.selectedItems()
if len(selItem) == 0:
return False
self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem[0]))
self.arChanged = True
return True
def getNewList(self):
newList = {}
for n in range(self.listBox.topLevelItemCount()):
tItem = self.listBox.topLevelItem(n)
aKey = tItem.text(0)
aVal = tItem.text(1)
if len(aKey) > 2:
newList[aKey[1:-1]] = aVal
return newList
# END Class GuiProjectEditReplace
+4 -4
View File
@@ -22,7 +22,6 @@ from PyQt5.QtWidgets import (
QShortcut, QMessageBox, QProgressDialog
)
from nw.theme import Theme
from nw.gui.doctree import GuiDocTree
from nw.gui.doceditor import GuiDocEditor
from nw.gui.docviewer import GuiDocViewer
@@ -38,9 +37,10 @@ from nw.project.item import NWItem
from nw.project.index import NWIndex
from nw.convert.tokenizer import Tokenizer
from nw.convert.tohtml import ToHtml
from nw.tools.wordcount import countWords
from nw.theme import Theme
from nw.enum import nwItemType, nwAlert
from nw.constants import nwFiles
from nw.tools.wordcount import countWords
logger = logging.getLogger(__name__)
@@ -246,6 +246,7 @@ class GuiMain(QMainWindow):
self.docEditor.setSpellCheck(self.theProject.spellCheck)
self.statusBar.setRefTime(self.theProject.projOpened)
self.mainMenu.updateMenu()
self.hasProject = True
# Restore previously open documents, if any
if self.theProject.lastEdited is not None:
@@ -253,8 +254,6 @@ class GuiMain(QMainWindow):
if self.theProject.lastViewed is not None:
self.viewDocument(self.theProject.lastViewed)
self.hasProject = True
return True
def saveProject(self):
@@ -329,6 +328,7 @@ class GuiMain(QMainWindow):
logger.debug("Generating preview for item %s" % tHandle)
aDoc = ToHtml(self.theProject, self)
aDoc.setText(tHandle)
aDoc.doAutoReplace()
aDoc.tokenizeText()
aDoc.doConvert()
self.docViewer.setHtml(aDoc.theResult)
+44 -29
View File
@@ -32,37 +32,40 @@ class NWProject():
def __init__(self, theParent):
# Internal
self.theParent = theParent
self.mainConf = self.theParent.mainConf
self.projChanged = None
self.projOpened = None
self.theParent = theParent
self.mainConf = self.theParent.mainConf
self.projChanged = None
self.projOpened = None
# Debug
self.handleSeed = None
self.handleSeed = None
# Class Settings
self.projTree = None
self.treeOrder = None
self.treeRoots = None
self.trashRoot = None
self.projPath = None
self.projMeta = None
self.projCache = None
self.projFile = None
self.projTree = None
self.treeOrder = None
self.treeRoots = None
self.trashRoot = None
self.projPath = None
self.projMeta = None
self.projCache = None
self.projFile = None
# Project Meta
self.projName = None
self.bookTitle = None
self.bookAuthors = None
self.projName = None
self.bookTitle = None
self.bookAuthors = None
# Various
self.autoReplace = None
# Project Settings
self.spellCheck = False
self.statusItems = None
self.importItems = None
self.lastEdited = None
self.lastViewed = None
self.lastWCount = 0
self.currWCount = 0
self.spellCheck = False
self.statusItems = None
self.importItems = None
self.lastEdited = None
self.lastViewed = None
self.lastWCount = 0
self.currWCount = 0
# Set Defaults
self.clearProject()
@@ -150,6 +153,7 @@ class NWProject():
self.projName = ""
self.bookTitle = ""
self.bookAuthors = []
self.autoReplace = {}
self.spellCheck = False
self.statusItems = NWStatus()
self.statusItems.addEntry("New", (100,100,100))
@@ -220,16 +224,19 @@ class NWProject():
if xItem.text is None: continue
if xItem.tag == "spellCheck":
self.spellCheck = checkBool(xItem.text,False)
if xItem.tag == "lastEdited":
elif xItem.tag == "lastEdited":
self.lastEdited = checkString(xItem.text,None,True)
if xItem.tag == "lastViewed":
elif xItem.tag == "lastViewed":
self.lastViewed = checkString(xItem.text,None,True)
if xItem.tag == "lastWordCount":
elif xItem.tag == "lastWordCount":
self.lastWCount = checkInt(xItem.text,0,False)
if xItem.tag == "status":
elif xItem.tag == "status":
self.statusItems.unpackEntries(xItem)
if xItem.tag == "importance":
elif xItem.tag == "importance":
self.importItems.unpackEntries(xItem)
elif xItem.tag == "autoReplace":
for xEntry in xItem:
self.autoReplace[xEntry.tag] = checkString(xEntry.text,None,False)
elif xChild.tag == "content":
logger.debug("Found project content")
for xItem in xChild:
@@ -292,6 +299,10 @@ class NWProject():
self._saveProjectValue(xSettings,"lastEdited", self.lastEdited)
self._saveProjectValue(xSettings,"lastViewed", self.lastViewed)
self._saveProjectValue(xSettings,"lastWordCount",self.currWCount)
xAutoRep = etree.SubElement(xSettings,"autoReplace")
for aKey, aValue in self.autoReplace.items():
if len(aKey) > 0:
self._saveProjectValue(xAutoRep,aKey,aValue)
xStatus = etree.SubElement(xSettings,"status")
self.statusItems.packEntries(xStatus)
@@ -401,7 +412,7 @@ class NWProject():
self.setProjectChanged(True)
return
def setImportColours(self, newCols,):
def setImportColours(self, newCols):
replaceMap = self.importItems.setNewEntries(newCols)
if self.projTree is not None:
for nwItem in self.projTree.values():
@@ -411,6 +422,10 @@ class NWProject():
self.setProjectChanged(True)
return
def setAutoReplace(self, autoReplace):
self.autoReplace = autoReplace
return
def setProjectChanged(self, bValue):
self.projChanged = bValue
self.theParent.setProjectStatus(self.projChanged)
+2
View File
@@ -38,6 +38,7 @@ class Theme:
self.colVal = [0,0,0]
self.colSpell = [0,0,0]
self.colTagErr = [0,0,0]
self.colRepTag = [0,0,0]
# Changeable Settings
self.guiTheme = None
@@ -96,6 +97,7 @@ class Theme:
self.colVal = self._loadColour(confParser,cnfSec,"value")
self.colSpell = self._loadColour(confParser,cnfSec,"spellcheckline")
self.colTagErr = self._loadColour(confParser,cnfSec,"tagerror")
self.colRepTag = self._loadColour(confParser,cnfSec,"replacetag")
return True
+1
View File
@@ -10,3 +10,4 @@ keyword = 200, 46, 0
value = 184, 200, 0
spellcheckline = 200, 46, 0
tagerror = 46, 200, 0
replacetag = 0, 184, 46