diff --git a/nw/common.py b/nw/common.py
new file mode 100644
index 00000000..ab413a43
--- /dev/null
+++ b/nw/common.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+"""novelWriter Common Functions
+
+ novelWriter – Common Functions
+================================
+ Various functions used multiple places
+
+ File History:
+ Created: 2019-05-12 [0.1.0]
+
+"""
+
+import logging
+import nw
+
+logger = logging.getLogger(__name__)
+
+def checkString(checkValue, defaultValue, allowNone=False):
+ if allowNone:
+ if checkValue == None: return None
+ if checkValue == "None": return None
+ if isinstance(checkValue,str): return str(checkValue)
+ return defaultValue
+
+def checkInt(checkValue, defaultValue, allowNone=False):
+ if allowNone:
+ if checkValue == None: return None
+ if checkValue == "None": return None
+ try:
+ return int(checkValue)
+ except:
+ return defaultValue
+
+def checkBool(checkValue, defaultValue, allowNone=False):
+ if allowNone:
+ if checkValue == None: return None
+ if checkValue == "None": return None
+ if isinstance(checkValue, str):
+ if checkValue == "True":
+ return True
+ elif checkValue == "False":
+ return False
+ else:
+ return defaultValue
+ elif isinstance(checkValue, int):
+ if checkValue == 1:
+ return True
+ elif checkValue == 0:
+ return False
+ else:
+ return defaultValue
+ return defaultValue
diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py
index 90e7072b..5ff38d78 100644
--- a/nw/gui/doceditor.py
+++ b/nw/gui/doceditor.py
@@ -38,6 +38,7 @@ class GuiDocEditor(QTextEdit):
self.theParent = theParent
self.docChanged = False
self.pwlFile = None
+ self.spellCheck = False
# Document Variables
self.charCount = 0
@@ -105,7 +106,7 @@ class GuiDocEditor(QTextEdit):
def setDocumentChanged(self, bValue):
self.docChanged = bValue
self.theParent.statusBar.setDocumentStatus(self.docChanged)
- return
+ return self.docChanged
def setText(self, theText):
self.setPlainText(theText)
@@ -120,7 +121,18 @@ class GuiDocEditor(QTextEdit):
self.pwlFile = pwlFile
self.theDict = enchant.DictWithPWL(self.mainConf.spellLanguage,pwlFile)
self.hLight.setDict(self.theDict)
- return
+ return True
+
+ def setSpellCheck(self, theMode):
+ self.spellCheck = theMode
+ self.hLight.setSpellCheck(theMode)
+ self.rehighlightDocument()
+ return True
+
+ def updateSpellCheck(self):
+ if self.spellCheck:
+ self.rehighlightDocument()
+ return True
def getText(self):
theText = self.toPlainText()
@@ -154,6 +166,11 @@ class GuiDocEditor(QTextEdit):
elif theAction == nwDocAction.SEL_PARA: self._makeSelection(QTextCursor.BlockUnderCursor)
else:
logger.error("Unknown or unsupported document action %s" % str(theAction))
+ return False
+ return True
+
+ def rehighlightDocument(self):
+ self.hLight.rehighlight()
return
##
@@ -181,6 +198,9 @@ class GuiDocEditor(QTextEdit):
def _openContextMenu(self, thePos):
+ if not self.spellCheck:
+ return
+
theCursor = self.cursorForPosition(thePos)
theCursor.select(QTextCursor.WordUnderCursor)
theWord = theCursor.selectedText()
@@ -237,7 +257,7 @@ class GuiDocEditor(QTextEdit):
self.wcTimer.start()
if self.mainConf.doReplace and not self.hasSelection:
self._docAutoReplace(self.theDoc.findBlock(thePos))
- # logger.verbose("Doc change signal took %.3f µs" % ((time()-self.lastEdit)*1e6))
+ logger.verbose("Doc change signal took %.3f µs" % ((time()-self.lastEdit)*1e6))
return
def _docAutoReplace(self, theBlock):
diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py
index 59923808..30787793 100644
--- a/nw/gui/dochighlight.py
+++ b/nw/gui/dochighlight.py
@@ -24,22 +24,23 @@ class GuiDocHighlighter(QSyntaxHighlighter):
QSyntaxHighlighter.__init__(self, theDoc)
logger.debug("Initialising DocHighlighter ...")
- self.mainConf = nw.CONFIG
- self.theDoc = theDoc
- self.theDict = None
- self.hRules = []
+ self.mainConf = nw.CONFIG
+ self.theDoc = theDoc
+ self.theDict = None
+ self.spellCheck = False
+ self.hRules = []
- self.colHead = QColor( 0,155,200)
- self.colHeadH = QColor( 0,105,135)
- self.colEmph = QColor(200,120, 0)
- self.colDialN = QColor(200, 46, 0)
- self.colDialD = QColor(184,200, 0)
- self.colDialS = QColor(136,200, 0)
- self.colComm = QColor(150,150,150)
- self.colKey = QColor(200, 46, 0)
- self.colVal = QColor(184,200, 0)
+ self.colHead = QColor( 0,155,200)
+ self.colHeadH = QColor( 0,105,135)
+ self.colEmph = QColor(200,120, 0)
+ self.colDialN = QColor(200, 46, 0)
+ self.colDialD = QColor(184,200, 0)
+ self.colDialS = QColor(136,200, 0)
+ self.colComm = QColor(150,150,150)
+ self.colKey = QColor(200, 46, 0)
+ self.colVal = QColor(184,200, 0)
- self.colSpell = QColor(200, 46, 0)
+ self.colSpell = QColor(200, 46, 0)
self.hStyles = {
"header1" : self._makeFormat(self.colHead, "bold",1.8),
@@ -153,7 +154,11 @@ class GuiDocHighlighter(QSyntaxHighlighter):
def setDict(self, theDict):
self.theDict = theDict
- return
+ return True
+
+ def setSpellCheck(self, theMode):
+ self.spellCheck = theMode
+ return True
def _makeFormat(self, fmtCol=None, fmtStyle=None, fmtSize=None):
theFormat = QTextCharFormat()
@@ -189,7 +194,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
self.setCurrentBlockState(0)
- if self.theDict is None:
+ if self.theDict is None or not self.spellCheck:
return
rxSpell = self.spellRx.globalMatch(theText.replace("_"," "), 0)
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index fdd0c3a1..488cf8ff 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -61,6 +61,15 @@ class GuiMainMenu(QMenuBar):
)
return
+ ##
+ # Update Menu on Settings Changed
+ ##
+
+ def updateMenu(self):
+ self.updateRecentProjects()
+ self.updateSpellCheck()
+ return
+
def updateRecentProjects(self):
self.recentMenu.clear()
for n in range(len(self.mainConf.recentList)):
@@ -71,6 +80,11 @@ class GuiMainMenu(QMenuBar):
self.recentMenu.addAction(menuItem)
return
+ def updateSpellCheck(self):
+ self.toolsSpellCheck.setChecked(self.theProject.spellCheck)
+ logger.verbose("Spell check is set to %s" % str(self.theProject.spellCheck))
+ return
+
##
# Menu Action
##
@@ -80,6 +94,12 @@ class GuiMainMenu(QMenuBar):
qApp.quit()
return True
+ def _toggleSpellCheck(self):
+ self.theProject.setSpellCheck(self.toolsSpellCheck.isChecked())
+ self.theParent.docEditor.setSpellCheck(self.toolsSpellCheck.isChecked())
+ logger.verbose("Spell check is set to %s" % str(self.theProject.spellCheck))
+ return
+
def _showAbout(self):
msgBox = QMessageBox()
msgBox.about(self.theParent, "About %s" % nw.__package__, (
@@ -407,8 +427,24 @@ class GuiMainMenu(QMenuBar):
self.toolsMoveDown.triggered.connect(lambda : self._moveTreeItem(1))
self.toolsMenu.addAction(self.toolsMoveDown)
- # # Tools > Separator
- # self.toolsMenu.addSeparator()
+ # Tools > Separator
+ self.toolsMenu.addSeparator()
+
+ # Tools > Toggle Spell Check
+ self.toolsSpellCheck = QAction("Check Spelling", self)
+ self.toolsSpellCheck.setStatusTip("Toggle Check Spelling")
+ self.toolsSpellCheck.setCheckable(True)
+ self.toolsSpellCheck.setChecked(self.theProject.spellCheck)
+ self.toolsSpellCheck.toggled.connect(self._toggleSpellCheck)
+ self.toolsSpellCheck.setShortcut("Ctrl+F7")
+ self.toolsMenu.addAction(self.toolsSpellCheck)
+
+ # Tools > Update Spell Check
+ menuItem = QAction(QIcon.fromTheme("tools-check-spelling"), "Re-Run Spell Check", self)
+ menuItem.setStatusTip("Rus the Spell Checker on Current Document")
+ menuItem.setShortcut("F7")
+ menuItem.triggered.connect(self.theParent.docEditor.updateSpellCheck)
+ self.toolsMenu.addAction(menuItem)
# # Tools > Settings
# menuItem = QAction(QIcon.fromTheme("preferences-system"), "Preferences", self)
diff --git a/nw/gui/winmain.py b/nw/gui/winmain.py
index ab12372f..673fdb27 100644
--- a/nw/gui/winmain.py
+++ b/nw/gui/winmain.py
@@ -172,10 +172,11 @@ class GuiMain(QMainWindow):
self.treeView.clearTree()
self.theProject.openProject(projFile)
self.treeView.buildTree()
- self.mainMenu.updateRecentProjects()
self._setWindowTitle(self.theProject.projName)
self._makeStatusIcons()
self.docEditor.setPwl(path.join(self.theProject.projMeta,"wordlist.txt"))
+ self.docEditor.setSpellCheck(self.theProject.spellCheck)
+ self.mainMenu.updateMenu()
return True
def saveProject(self):
diff --git a/nw/project/item.py b/nw/project/item.py
index 4e2366c4..b41342c9 100644
--- a/nw/project/item.py
+++ b/nw/project/item.py
@@ -13,11 +13,12 @@
import logging
import nw
-from os import path, mkdir
-from lxml import etree
-from datetime import datetime
+from os import path, mkdir
+from lxml import etree
+from datetime import datetime
-from nw.enum import nwItemType, nwItemClass, nwItemLayout
+from nw.enum import nwItemType, nwItemClass, nwItemLayout
+from nw.common import checkInt
logger = logging.getLogger(__name__)
@@ -153,7 +154,7 @@ class NWItem():
return
def setStatus(self, theStatus):
- theStatus = self._checkInt(theStatus,0)
+ theStatus = checkInt(theStatus,0)
self.itemStatus = theStatus
return
@@ -169,31 +170,18 @@ class NWItem():
##
def setCharCount(self, theCount):
- theCount = self._checkInt(theCount,0)
+ theCount = checkInt(theCount,0)
self.charCount = theCount
return
def setWordCount(self, theCount):
- theCount = self._checkInt(theCount,0)
+ theCount = checkInt(theCount,0)
self.wordCount = theCount
return
def setParaCount(self, theCount):
- theCount = self._checkInt(theCount,0)
+ theCount = checkInt(theCount,0)
self.paraCount = theCount
return
- ##
- # Internal Functions
- ##
-
- def _checkInt(self,checkValue,defaultValue,allowNone=False):
- if allowNone:
- if checkValue == None: return None
- if checkValue == "None": return None
- try:
- return int(checkValue)
- except:
- return defaultValue
-
# END Class NWItem
diff --git a/nw/project/project.py b/nw/project/project.py
index 25a96609..23821055 100644
--- a/nw/project/project.py
+++ b/nw/project/project.py
@@ -20,6 +20,7 @@ from datetime import datetime
from time import time
from nw.enum import nwItemType, nwItemClass, nwItemLayout
+from nw.common import checkString, checkBool
from nw.project.item import NWItem
logger = logging.getLogger(__name__)
@@ -36,7 +37,7 @@ class NWProject():
# Debug
self.handleSeed = None
- # Project Settings
+ # Class Settings
self.projTree = None
self.treeOrder = None
self.treeRoots = None
@@ -45,11 +46,17 @@ class NWProject():
self.projMeta = None
self.projCache = None
self.projFile = None
+ self.statusCols = None
+
+ # Project Meta
self.projName = None
self.bookTitle = None
self.bookAuthors = None
- self.statusCols = None
+ # Project Settings
+ self.spellCheck = False
+
+ # Set Defaults
self.clearProject()
return
@@ -130,6 +137,7 @@ class NWProject():
self.projName = ""
self.bookTitle = ""
self.bookAuthors = []
+ self.spellCheck = False
self.statusCols = [
("New", 100,100,100),
("Note", 200, 50, 0),
@@ -185,6 +193,12 @@ class NWProject():
elif xItem.tag == "author":
logger.verbose("Author: '%s'" % xItem.text)
self.bookAuthors.append(xItem.text)
+ elif xChild.tag == "settings":
+ logger.debug("Found project settings")
+ for xItem in xChild:
+ if xItem.text is None: continue
+ if xItem.tag == "spellCheck":
+ self.spellCheck = checkBool(xItem.text,False)
elif xChild.tag == "content":
logger.debug("Found project content")
for xItem in xChild:
@@ -233,15 +247,16 @@ class NWProject():
"appVersion" : str(nw.__version__),
"timeStamp" : datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
})
- xProject = etree.SubElement(nwXML,"project")
- xProjName = etree.SubElement(xProject,"name")
- xProjName.text = self.projName
- xBookTitle = etree.SubElement(xProject,"title")
- xBookTitle.text = self.bookTitle
- for bookAuthor in self.bookAuthors:
- if bookAuthor == "": continue
- xBookAuthor = etree.SubElement(xProject,"author")
- xBookAuthor.text = bookAuthor
+
+ # Save Project Meta
+ xProject = etree.SubElement(nwXML,"project")
+ self._saveProjectValue(xProject,"name", self.projName, True)
+ self._saveProjectValue(xProject,"title", self.bookTitle, True)
+ self._saveProjectValue(xProject,"author",self.bookAuthors)
+
+ # Save Project Settings
+ xSettings = etree.SubElement(nwXML,"settings")
+ self._saveProjectValue(xSettings,"spellCheck",self.spellCheck)
# Save Tree Content
logger.debug("Writing project content")
@@ -298,6 +313,11 @@ class NWProject():
self.setProjectChanged(True)
return True
+ def setSpellCheck(self, theMode):
+ self.spellCheck = theMode
+ self.setProjectChanged(True)
+ return True
+
def setTreeOrder(self, newOrder):
if len(self.treeOrder) != len(newOrder):
logger.warning("Size of new and old tree order does not match")
@@ -351,6 +371,17 @@ class NWProject():
return False
return True
+ def _saveProjectValue(self, xParent, theName, theValue, allowNone=True):
+ if not isinstance(theValue, list):
+ theValue = [theValue]
+ for aValue in theValue:
+ if not isinstance(aValue, str):
+ aValue = str(aValue)
+ if aValue == "" and not allowNone: continue
+ xItem = etree.SubElement(xParent,theName)
+ xItem.text = aValue
+ return
+
def _scanProjectFolder(self):
if self.projPath is None:
@@ -402,8 +433,8 @@ class NWProject():
return
def _appendItem(self, tHandle, pHandle, nwItem):
- tHandle = self._checkString(tHandle,self._makeHandle(),False)
- pHandle = self._checkString(pHandle,None,True)
+ tHandle = checkString(tHandle,self._makeHandle(),False)
+ pHandle = checkString(pHandle,None,True)
logger.verbose("Adding entry %s with parent %s" % (str(tHandle),str(pHandle)))
nwItem.setHandle(tHandle)
@@ -441,20 +472,4 @@ class NWProject():
itemHandle = self._makeHandle(addSeed+"!")
return itemHandle
- def _checkString(self,checkValue,defaultValue,allowNone=False):
- if allowNone:
- if checkValue == None: return None
- if checkValue == "None": return None
- if isinstance(checkValue,str): return str(checkValue)
- return defaultValue
-
- def _checkInt(self,checkValue,defaultValue,allowNone=False):
- if allowNone:
- if checkValue == None: return None
- if checkValue == "None": return None
- try:
- return int(checkValue)
- except:
- return defaultValue
-
# END Class NWProject
diff --git a/tests/reference/new_nwProject.nwx b/tests/reference/new_nwProject.nwx
index 742afe41..e9a65102 100644
--- a/tests/reference/new_nwProject.nwx
+++ b/tests/reference/new_nwProject.nwx
@@ -4,6 +4,9 @@