232 lines
6.3 KiB
Python
232 lines
6.3 KiB
Python
import misc
|
|
import spellcheck
|
|
import undo
|
|
import util
|
|
|
|
import wx
|
|
|
|
class SpellCheckDlg(wx.Dialog):
|
|
def __init__(self, parent, ctrl, sc, gScDict):
|
|
wx.Dialog.__init__(self, parent, -1, "Spell checker",
|
|
style = wx.DEFAULT_DIALOG_STYLE | wx.WANTS_CHARS)
|
|
|
|
self.ctrl = ctrl
|
|
|
|
# spellcheck.SpellCheck
|
|
self.sc = sc
|
|
|
|
# user's global spell checker dictionary
|
|
self.gScDict = gScDict
|
|
|
|
# have we added any words to global dictionary
|
|
self.changedGlobalDict = False
|
|
|
|
vsizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
hsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
hsizer.Add(wx.StaticText(self, -1, "Word:"), 0,
|
|
wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
|
|
self.replaceEntry = wx.TextCtrl(self, -1, style = wx.TE_PROCESS_ENTER)
|
|
hsizer.Add(self.replaceEntry, 1, wx.EXPAND)
|
|
|
|
vsizer.Add(hsizer, 1, wx.EXPAND | wx.BOTTOM, 15)
|
|
|
|
gsizer = wx.FlexGridSizer(2, 2, 10, 10)
|
|
gsizer.AddGrowableCol(1)
|
|
|
|
replaceBtn = wx.Button(self, -1, "&Replace")
|
|
gsizer.Add(replaceBtn)
|
|
|
|
addScriptBtn = wx.Button(self, -1, "Add to &script dictionary")
|
|
gsizer.Add(addScriptBtn, 0, wx.EXPAND)
|
|
|
|
skipBtn = wx.Button(self, -1, "S&kip")
|
|
gsizer.Add(skipBtn)
|
|
|
|
addGlobalBtn = wx.Button(self, -1, "Add to &global dictionary")
|
|
gsizer.Add(addGlobalBtn, 0, wx.EXPAND)
|
|
|
|
vsizer.Add(gsizer, 0, wx.EXPAND, 0)
|
|
|
|
suggestBtn = wx.Button(self, -1, "S&uggest replacement")
|
|
vsizer.Add(suggestBtn, 0, wx.EXPAND | wx.TOP, 10)
|
|
|
|
self.Bind(wx.EVT_TEXT_ENTER, self.OnReplace, id=self.replaceEntry.GetId())
|
|
|
|
self.Bind(wx.EVT_BUTTON, self.OnReplace, id=replaceBtn.GetId())
|
|
self.Bind(wx.EVT_BUTTON, self.OnAddScript, id=addScriptBtn.GetId())
|
|
self.Bind(wx.EVT_BUTTON, self.OnAddGlobal, id=addGlobalBtn.GetId())
|
|
self.Bind(wx.EVT_BUTTON, self.OnSkip, id=skipBtn.GetId())
|
|
self.Bind(wx.EVT_BUTTON, self.OnSuggest, id=suggestBtn.GetId())
|
|
|
|
self.Bind(wx.EVT_CHAR, self.OnChar)
|
|
self.replaceEntry.Bind(wx.EVT_CHAR, self.OnChar)
|
|
replaceBtn.Bind(wx.EVT_CHAR, self.OnChar)
|
|
addScriptBtn.Bind(wx.EVT_CHAR, self.OnChar)
|
|
skipBtn.Bind(wx.EVT_CHAR, self.OnChar)
|
|
addGlobalBtn.Bind(wx.EVT_CHAR, self.OnChar)
|
|
suggestBtn.Bind(wx.EVT_CHAR, self.OnChar)
|
|
|
|
util.finishWindow(self, vsizer)
|
|
|
|
self.showWord()
|
|
|
|
def showWord(self):
|
|
self.ctrl.sp.line = self.sc.line
|
|
self.ctrl.sp.column = self.sc.col
|
|
self.ctrl.sp.setMark(self.sc.line, self.sc.col + len(self.sc.word) - 1)
|
|
|
|
self.replaceEntry.SetValue(self.sc.word)
|
|
|
|
self.ctrl.makeLineVisible(self.sc.line)
|
|
self.ctrl.updateScreen()
|
|
|
|
def gotoNext(self, incCol = True):
|
|
if incCol:
|
|
self.sc.col += len(self.sc.word)
|
|
|
|
if not self.sc.findNext():
|
|
wx.MessageBox("No more incorrect words found.", "Results",
|
|
wx.OK, self)
|
|
|
|
self.EndModal(wx.ID_OK)
|
|
|
|
return
|
|
|
|
self.showWord()
|
|
|
|
def OnChar(self, event):
|
|
kc = event.GetKeyCode()
|
|
|
|
if kc == wx.WXK_ESCAPE:
|
|
self.EndModal(wx.ID_OK)
|
|
|
|
return
|
|
|
|
event.Skip()
|
|
|
|
def OnReplace(self, event):
|
|
if not self.sc.word:
|
|
return
|
|
|
|
sp = self.ctrl.sp
|
|
u = undo.SinglePara(sp, undo.CMD_MISC, self.sc.line)
|
|
|
|
word = util.toInputStr(misc.fromGUI(self.replaceEntry.GetValue()))
|
|
ls = sp.lines
|
|
|
|
sp.gotoPos(self.sc.line, self.sc.col)
|
|
|
|
ls[self.sc.line].text = util.replace(
|
|
ls[self.sc.line].text, word,
|
|
self.sc.col, len(self.sc.word))
|
|
|
|
sp.rewrapPara(sp.getParaFirstIndexFromLine(self.sc.line))
|
|
|
|
# rewrapping a paragraph can have moved the cursor, so get the new
|
|
# location of it, and then advance past the just-changed word
|
|
self.sc.line = sp.line
|
|
self.sc.col = sp.column + len(word)
|
|
|
|
sp.clearMark()
|
|
sp.markChanged()
|
|
|
|
u.setAfter(sp)
|
|
sp.addUndo(u)
|
|
|
|
self.gotoNext(False)
|
|
|
|
def OnSkip(self, event = None, autoFind = False):
|
|
if not self.sc.word:
|
|
return
|
|
|
|
self.gotoNext()
|
|
|
|
def OnAddScript(self, event):
|
|
if not self.sc.word:
|
|
return
|
|
|
|
self.ctrl.sp.scDict.add(self.sc.word)
|
|
self.ctrl.sp.markChanged()
|
|
self.gotoNext()
|
|
|
|
def OnAddGlobal(self, event):
|
|
if not self.sc.word:
|
|
return
|
|
|
|
self.gScDict.add(self.sc.word)
|
|
self.changedGlobalDict = True
|
|
|
|
self.gotoNext()
|
|
|
|
def OnSuggest(self, event):
|
|
if not self.sc.word:
|
|
return
|
|
|
|
isAllCaps = self.sc.word == util.upper(self.sc.word)
|
|
isCapitalized = self.sc.word[:1] == util.upper(self.sc.word[:1])
|
|
|
|
word = util.lower(self.sc.word)
|
|
|
|
wl = len(word)
|
|
wstart = word[:2]
|
|
d = 500
|
|
fifo = util.FIFO(5)
|
|
wx.BeginBusyCursor()
|
|
|
|
for w in spellcheck.prefixDict[util.getWordPrefix(word)]:
|
|
if w.startswith(wstart):
|
|
d = self.tryWord(word, wl, w, d, fifo)
|
|
|
|
for w in self.gScDict.words.keys():
|
|
if w.startswith(wstart):
|
|
d = self.tryWord(word, wl, w, d, fifo)
|
|
|
|
for w in self.ctrl.sp.scDict.words.keys():
|
|
if w.startswith(wstart):
|
|
d = self.tryWord(word, wl, w, d, fifo)
|
|
|
|
items = fifo.get()
|
|
|
|
wx.EndBusyCursor()
|
|
|
|
if len(items) == 0:
|
|
wx.MessageBox("No similar words found.", "Results",
|
|
wx.OK, self)
|
|
|
|
return
|
|
|
|
dlg = wx.SingleChoiceDialog(
|
|
self, "Most similar words:", "Suggestions", items)
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
sel = dlg.GetSelection()
|
|
|
|
newWord = items[sel]
|
|
|
|
if isAllCaps:
|
|
newWord = util.upper(newWord)
|
|
elif isCapitalized:
|
|
newWord = util.capitalize(newWord)
|
|
|
|
self.replaceEntry.SetValue(newWord)
|
|
|
|
dlg.Destroy()
|
|
|
|
# if w2 is closer to w1 in Levenshtein distance than d, add it to
|
|
# fifo. return min(d, new_distance).
|
|
def tryWord(self, w1, w1len, w2, d, fifo):
|
|
if abs(w1len - len(w2)) > 3:
|
|
return d
|
|
|
|
d2 = spellcheck.lev(w1, w2)
|
|
|
|
if d2 <= d:
|
|
fifo.add(w2)
|
|
|
|
return d2
|
|
|
|
return d
|
|
|