Fixed merge conflicts
This commit is contained in:
+70
-53
@@ -16,9 +16,9 @@ import enchant
|
||||
|
||||
from time import time
|
||||
|
||||
from PyQt5.QtWidgets import QTextEdit, QAction, QMenu
|
||||
from PyQt5.QtCore import Qt, QTimer, QEvent, pyqtSignal
|
||||
from PyQt5.QtGui import QTextCursor, QTextOption, QMouseEvent
|
||||
from PyQt5.QtWidgets import QTextEdit, QAction, QMenu, QShortcut
|
||||
from PyQt5.QtGui import QTextCursor, QTextOption, QIcon, QKeySequence
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
|
||||
from nw.gui.dochighlight import GuiDocHighlighter
|
||||
from nw.gui.wordcounter import WordCounter
|
||||
@@ -37,6 +37,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theParent = theParent
|
||||
self.docChanged = False
|
||||
self.pwlFile = None
|
||||
|
||||
# Document Variables
|
||||
self.charCount = 0
|
||||
@@ -54,7 +55,12 @@ class GuiDocEditor(QTextEdit):
|
||||
# Core Elements
|
||||
self.theDoc = self.document()
|
||||
self.theDict = enchant.Dict(self.mainConf.spellLanguage)
|
||||
self.hLight = GuiDocHighlighter(self.theDoc, self.theDict)
|
||||
self.hLight = GuiDocHighlighter(self.theDoc)
|
||||
self.hLight.setDict(self.theDict)
|
||||
|
||||
# Context Menu
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.customContextMenuRequested.connect(self._openContextMenu)
|
||||
|
||||
# Editor State
|
||||
self.hasSelection = False
|
||||
@@ -76,6 +82,9 @@ class GuiDocEditor(QTextEdit):
|
||||
self.setAcceptRichText(False)
|
||||
self.setFontPointSize(self.mainConf.textSize)
|
||||
|
||||
# Custom Shortcuts
|
||||
QShortcut(QKeySequence("Ctrl+."), self, context=Qt.WidgetShortcut, activated=self._openSpellContext)
|
||||
|
||||
# Set Up Word Count Thread and Timer
|
||||
self.wcInterval = self.mainConf.wordCountTimer
|
||||
self.wcTimer = QTimer()
|
||||
@@ -106,6 +115,13 @@ class GuiDocEditor(QTextEdit):
|
||||
self.setDocumentChanged(False)
|
||||
return True
|
||||
|
||||
def setPwl(self, pwlFile):
|
||||
if pwlFile is not None:
|
||||
self.pwlFile = pwlFile
|
||||
self.theDict = enchant.DictWithPWL(self.mainConf.spellLanguage,pwlFile)
|
||||
self.hLight.setDict(self.theDict)
|
||||
return
|
||||
|
||||
def getText(self):
|
||||
theText = self.toPlainText()
|
||||
return theText
|
||||
@@ -155,54 +171,63 @@ class GuiDocEditor(QTextEdit):
|
||||
QTextEdit.keyPressEvent(self, keyEvent)
|
||||
return
|
||||
|
||||
def mousePressEvent(self, theEvent):
|
||||
"""Capture right click events and rewrite them to left button event. This moves the cursor
|
||||
to the location of the pointer. Needed to select the word under the right click.
|
||||
Adapted from: https://nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check
|
||||
"""
|
||||
if theEvent.button() == Qt.RightButton:
|
||||
theEvent = QMouseEvent(
|
||||
QEvent.MouseButtonPress, theEvent.pos(), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier
|
||||
)
|
||||
QTextEdit.mousePressEvent(self, theEvent)
|
||||
return
|
||||
|
||||
def contextMenuEvent(self, theEvent):
|
||||
"""Intercept the context menu and insert spelling suggestions, if any.
|
||||
Uses the custom QAction class SpellAction from the same example code.
|
||||
Adapted from: https://nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check
|
||||
"""
|
||||
mnuSpell = self.createStandardContextMenu()
|
||||
theCursor = self.textCursor()
|
||||
theCursor.select(QTextCursor.WordUnderCursor)
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
if self.textCursor().hasSelection():
|
||||
theText = self.textCursor().selectedText()
|
||||
if not self.theDict.check(theText):
|
||||
mnuSuggest = QMenu("Spelling Suggestions")
|
||||
for aWord in self.theDict.suggest(theText):
|
||||
action = SpellAction(aWord, mnuSuggest)
|
||||
action.correct.connect(self._correctWord)
|
||||
mnuSuggest.addAction(action)
|
||||
if len(mnuSuggest.actions()) > 0:
|
||||
theActions = mnuSpell.actions()
|
||||
mnuSpell.insertSeparator(theActions[0])
|
||||
mnuSpell.insertMenu(theActions[0], mnuSuggest)
|
||||
|
||||
mnuSpell.exec_(theEvent.globalPos())
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _correctWord(self, word):
|
||||
theCursor = self.textCursor()
|
||||
def _openSpellContext(self):
|
||||
self._openContextMenu(self.cursorRect().center())
|
||||
return
|
||||
|
||||
def _openContextMenu(self, thePos):
|
||||
|
||||
theCursor = self.cursorForPosition(thePos)
|
||||
theCursor.select(QTextCursor.WordUnderCursor)
|
||||
theWord = theCursor.selectedText()
|
||||
if theWord == "":
|
||||
return
|
||||
if self.theDict.check(theWord):
|
||||
return
|
||||
|
||||
mnuSuggest = QMenu()
|
||||
spIcon = QIcon.fromTheme("tools-check-spelling")
|
||||
mnuHead = QAction(spIcon,"Spelling Suggestion", mnuSuggest)
|
||||
mnuSuggest.addAction(mnuHead)
|
||||
mnuSuggest.addSeparator()
|
||||
theSuggest = self.theDict.suggest(theWord)
|
||||
if len(theSuggest) > 0:
|
||||
for aWord in theSuggest:
|
||||
mnuWord = QAction(aWord, mnuSuggest)
|
||||
mnuWord.triggered.connect(lambda thePos, aWord=aWord : self._correctWord(theCursor, aWord))
|
||||
mnuSuggest.addAction(mnuWord)
|
||||
mnuSuggest.addSeparator()
|
||||
mnuAdd = QAction("Add Word to Dictionary", mnuSuggest)
|
||||
mnuAdd.triggered.connect(lambda thePos : self._addWord(theCursor))
|
||||
mnuSuggest.addAction(mnuAdd)
|
||||
else:
|
||||
mnuHead = QAction("No Suggestions", mnuSuggest)
|
||||
mnuSuggest.addAction(mnuHead)
|
||||
|
||||
mnuSuggest.exec_(self.viewport().mapToGlobal(thePos))
|
||||
|
||||
return
|
||||
|
||||
def _correctWord(self, theCursor, theWord):
|
||||
xPos = theCursor.selectionStart()
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.removeSelectedText()
|
||||
theCursor.insertText(word)
|
||||
theCursor.insertText(theWord)
|
||||
theCursor.endEditBlock()
|
||||
theCursor.setPosition(xPos)
|
||||
self.setTextCursor(theCursor)
|
||||
return
|
||||
|
||||
def _addWord(self, theCursor):
|
||||
theWord = theCursor.selectedText().strip()
|
||||
logger.info("Added '%s' to project dictionary" % theWord)
|
||||
self.theDict.add_to_pwl(theWord)
|
||||
self.hLight.setDict(self.theDict)
|
||||
self.hLight.rehighlightBlock(theCursor.block())
|
||||
return
|
||||
|
||||
def _docChange(self, thePos, charsRemoved, charsAdded):
|
||||
@@ -328,11 +353,3 @@ class GuiDocEditor(QTextEdit):
|
||||
return
|
||||
|
||||
# END Class GuiDocEditor
|
||||
|
||||
class SpellAction(QAction):
|
||||
correct = pyqtSignal(str)
|
||||
def __init__(self, *args):
|
||||
QAction.__init__(self, *args)
|
||||
self.triggered.connect(lambda x: self.correct.emit(self.text()))
|
||||
|
||||
# END Class SpellAction
|
||||
|
||||
@@ -20,13 +20,13 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
|
||||
def __init__(self, theDoc, theDict):
|
||||
def __init__(self, theDoc):
|
||||
QSyntaxHighlighter.__init__(self, theDoc)
|
||||
|
||||
logger.debug("Initialising DocHighlighter ...")
|
||||
self.mainConf = nw.CONFIG
|
||||
self.theDoc = theDoc
|
||||
self.theDict = theDict
|
||||
self.theDict = None
|
||||
self.hRules = []
|
||||
|
||||
self.colHead = QColor( 0,155,200)
|
||||
@@ -145,12 +145,16 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
|
||||
# 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"[\w\'{:s}]+".format(self.mainConf.fmtApostrophe))
|
||||
self.spellRx = QRegularExpression(r"\b[^\s]+\b")
|
||||
|
||||
logger.debug("DocHighlighter initialisation complete")
|
||||
|
||||
return
|
||||
|
||||
def setDict(self, theDict):
|
||||
self.theDict = theDict
|
||||
return
|
||||
|
||||
def _makeFormat(self, fmtCol=None, fmtStyle=None, fmtSize=None):
|
||||
theFormat = QTextCharFormat()
|
||||
|
||||
@@ -188,7 +192,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
if self.theDict is None:
|
||||
return
|
||||
|
||||
rxSpell = self.spellRx.globalMatch(theText, 0)
|
||||
rxSpell = self.spellRx.globalMatch(theText.replace("_"," "), 0)
|
||||
while rxSpell.hasNext():
|
||||
rxMatch = rxSpell.next()
|
||||
if not self.theDict.check(rxMatch.captured(0)):
|
||||
|
||||
+4
-2
@@ -159,8 +159,9 @@ class GuiMain(QMainWindow):
|
||||
def newProject(self):
|
||||
logger.info("Creating new project")
|
||||
self.treeView.clearTree()
|
||||
self.theProject.newProject()
|
||||
self.treeView.buildTree()
|
||||
if self.saveProject():
|
||||
self.theProject.newProject()
|
||||
self.treeView.buildTree()
|
||||
return
|
||||
|
||||
def openProject(self, projFile=None):
|
||||
@@ -174,6 +175,7 @@ class GuiMain(QMainWindow):
|
||||
self.mainMenu.updateRecentProjects()
|
||||
self._setWindowTitle(self.theProject.projName)
|
||||
self._makeStatusIcons()
|
||||
self.docEditor.setPwl(path.join(self.theProject.projMeta,"wordlist.txt"))
|
||||
return True
|
||||
|
||||
def saveProject(self):
|
||||
|
||||
+26
-7
@@ -39,6 +39,8 @@ class NWProject():
|
||||
self.treeRoots = None
|
||||
self.trashRoot = None
|
||||
self.projPath = None
|
||||
self.projMeta = None
|
||||
self.projCache = None
|
||||
self.projFile = None
|
||||
self.projName = None
|
||||
self.bookTitle = None
|
||||
@@ -116,6 +118,8 @@ class NWProject():
|
||||
self.treeRoots = []
|
||||
self.trashRoot = None
|
||||
self.projPath = None
|
||||
self.projMeta = None
|
||||
self.projCache = None
|
||||
self.projFile = "nwProject.nwx"
|
||||
self.projName = ""
|
||||
self.bookTitle = ""
|
||||
@@ -141,6 +145,12 @@ class NWProject():
|
||||
self.projPath = path.dirname(fileName)
|
||||
logger.debug("Opening project: %s" % self.projPath)
|
||||
|
||||
self.projMeta = path.join(self.projPath,"meta")
|
||||
self.projCache = path.join(self.projPath,"cache")
|
||||
|
||||
if not self._checkFolder(self.projMeta): return
|
||||
if not self._checkFolder(self.projCache): return
|
||||
|
||||
nwXML = etree.parse(fileName)
|
||||
xRoot = nwXML.getroot()
|
||||
|
||||
@@ -201,13 +211,12 @@ class NWProject():
|
||||
self.theParent.makeAlert("Project path not set, cannot save.",2)
|
||||
return False
|
||||
|
||||
if not path.isdir(self.projPath):
|
||||
try:
|
||||
mkdir(self.projPath)
|
||||
logger.info("Created folder %s" % self.projPath)
|
||||
except Exception as e:
|
||||
self.theParent.makeAlert(["Could not create folder.",str(e)],2)
|
||||
return False
|
||||
self.projMeta = path.join(self.projPath,"meta")
|
||||
self.projCache = path.join(self.projPath,"cache")
|
||||
|
||||
if not self._checkFolder(self.projPath): return
|
||||
if not self._checkFolder(self.projMeta): return
|
||||
if not self._checkFolder(self.projCache): return
|
||||
|
||||
logger.debug("Saving project: %s" % self.projPath)
|
||||
|
||||
@@ -326,6 +335,16 @@ class NWProject():
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _checkFolder(self, thePath):
|
||||
if not path.isdir(thePath):
|
||||
try:
|
||||
mkdir(thePath)
|
||||
logger.info("Created folder %s" % thePath)
|
||||
except Exception as e:
|
||||
self.theParent.makeAlert(["Could not create folder.",str(e)],2)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _scanProjectFolder(self):
|
||||
|
||||
if self.projPath is None:
|
||||
|
||||
Reference in New Issue
Block a user