Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

Diff of /plugins/spellcheck.py [000000] .. [6fe32e] Maximize Restore

  Switch to side-by-side view

--- a
+++ b/plugins/spellcheck.py
@@ -0,0 +1,514 @@
+
+import wx
+import wx.stc
+import wx.lib.scrolledpanel as scrolled
+import os
+import time
+import traceback
+import sys
+
+try:
+    UNICODE = sys.modules['__main__'].UNICODE
+except:
+    UNICODE = 0
+
+class defdict(dict):
+    def __getitem__(self, key):
+        try:
+            return dict.__getitem__(self, key)
+        except KeyError:
+            return key
+
+goodch = dict.fromkeys('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
+transtable = ''.join([(' ', i.lower())[i in goodch] for i in [chr(j) for j in xrange(256)]])
+
+
+non_word = defdict([(chr(i), ' ') for i in xrange(256) if chr(i) not in goodch])
+
+fil = os.path.join(os.path.normpath(os.path.join(os.getcwd(),
+                                                 os.path.split(__file__)[0])),
+                   'dictionary.txt')
+
+dictionary = {}
+alphabet = 'abcdefghijklmnopqrstuvwxyz'
+
+def _dri(word, alphabet):
+    for i in xrange(len(word)):
+        x = word[:i]
+        y = word[i+1:]
+        #will attempt to delete every individual character from the word
+        yield x + y
+        z = word[i:]
+        for j in alphabet:
+            #will attempt to replace every individual character from the word
+            #with another character
+            yield x + j + y
+            #will attempt to insert a new character into every position in the
+            #word except for the last
+            yield x + j + z
+        #will attempt to swap every internal pair of characters
+        yield x + word[i+1:i+2] + word[i:i+1] + word[i+2:]
+    for j in alphabet:
+        #inserts a new character at the end of the word
+        yield word + j
+
+
+def suggest(word, dcts):
+    _a = alphabet
+    x = {}
+    for i in _dri(word, _a):
+        if i not in x:
+            for j in dcts:
+                if i in j:
+                    x[i] = None
+                    break
+    x = x.keys()
+    x.sort()
+    return x
+
+class SpellCheck(scrolled.ScrolledPanel):
+    def __init__(self, parent, root):
+        scrolled.ScrolledPanel.__init__(self, parent, -1)
+        
+        self.document = None
+        self.root = root
+        self.funcdefs = {}
+        self.dcts = {}
+        self.start = 0
+        
+        ws = wx.BoxSizer(wx.HORIZONTAL)
+        
+#-------------------------- Left button/pref column --------------------------
+        
+        s = wx.BoxSizer(wx.VERTICAL)
+        
+        s2 = wx.BoxSizer(wx.HORIZONTAL)
+        
+        go = wx.Button(self, -1, "Check!")
+        bs = go.GetDefaultSize()[1]
+        go.SetToolTipString("Check spelling in the current document")
+        s2.Add(go, 0, wx.RIGHT, 4)
+        self.Bind(wx.EVT_BUTTON, self.OnSpellCheck, go)
+
+        checks = wx.Button(self, -1, "Check Sel")
+        checks.SetToolTipString("Check spelling in the selection of the current document")
+        s2.Add(checks, 0)
+        self.Bind(wx.EVT_BUTTON, self.OnSpellCheckSel, checks)
+
+        s.Add(s2, 0, wx.ALL, 2)
+        
+        s2 = wx.BoxSizer(wx.HORIZONTAL)
+        
+        self.ignore_funcdefs = wx.CheckBox(self, -1, "Ignore Defs")
+        self.ignore_funcdefs.SetToolTipString("If checked, will ignore all function definitions")
+        s2.Add(self.ignore_funcdefs, 0, wx.TOP|wx.BOTTOM, 6)
+        
+        clear = wx.Button(self, -1, "Clear")
+        clear.SetToolTipString("Clear the results of the previous spell check")
+        s2.Add(clear, 0, wx.LEFT, 4)
+        self.Bind(wx.EVT_BUTTON, self.OnClear, clear)
+
+        s.Add(s2, 0, wx.ALL|wx.EXPAND, 2)
+        
+        ## ws.Add(s, 0, wx.ALL|wx.EXPAND, 2)
+        
+#------------------------ Custom dictionaries column -------------------------
+
+        s.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 4)
+
+        ## s = wx.BoxSizer(wx.VERTICAL)
+        
+        s2 = wx.BoxSizer(wx.HORIZONTAL)
+        
+        s2.Add(wx.StaticText(self, -1, "Custom Dictionaries:"), 1, wx.RIGHT|wx.ALIGN_LEFT|wx.EXPAND, 4)
+        
+        s2.Add(wx.StaticText(self, -1, ""), 0, wx.EXPAND)
+        
+        addd = wx.Button(self, -1, "+", style=wx.BU_EXACTFIT)
+        addd.SetToolTipString("Add a new custom dictionary")
+        s2.Add(addd, 0, wx.RIGHT|wx.ALIGN_RIGHT, 4)
+        self.Bind(wx.EVT_BUTTON, self.OnAddD, addd)
+        
+        deld = wx.Button(self, -1, "-", style=wx.BU_EXACTFIT)
+        deld.SetToolTipString("Remove a custom dictionary")
+        s2.Add(deld, 0, wx.ALIGN_RIGHT)
+        self.Bind(wx.EVT_BUTTON, self.OnDelD, deld)
+        
+        s.Add(s2, 0, wx.ALL, 2)
+        
+        s.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 2)
+        
+        _ = list(self.root.config['DICTIONARIES'])
+        _.sort()
+        self.dictionaries = wx.CheckListBox(self, -1, choices=_)
+        s.Add(self.dictionaries, 1, wx.EXPAND)
+        
+        ws.Add(s, 0, wx.ALL|wx.EXPAND, 2)
+        
+#---------------------------- Misspellings column ----------------------------
+        
+        s = wx.BoxSizer(wx.VERTICAL)
+        
+        s2 = wx.BoxSizer(wx.HORIZONTAL)
+        s2.Add(wx.StaticText(self, -1, "Possible Misspellings:"), 1, wx.ALIGN_LEFT|wx.EXPAND, 4)
+        
+        cha = wx.Button(self, -1, "./ All", style=wx.BU_EXACTFIT)
+        cha.SetToolTipString("Make sure all of the 'Possible Misspellings' are checked")
+        s2.Add(cha, 0, wx.RIGHT, 4)
+        self.Bind(wx.EVT_BUTTON, self.OnCheckAll, cha)
+        
+        add = wx.Button(self, -1, "+./", style=wx.BU_EXACTFIT)
+        add.SetToolTipString("Add the checked words to a custom dictionary")
+        s2.Add(add, 0)
+        self.Bind(wx.EVT_BUTTON, self.OnAdd, add)
+
+        ignore = wx.Button(self, -1, "- ./", style=wx.BU_EXACTFIT)
+        ignore.SetToolTipString("Ignore the checked words while this document stays open")
+        s2.Add(ignore, 0, wx.RIGHT, 4)
+        self.Bind(wx.EVT_BUTTON, self.OnIgnore, ignore)
+        
+        s.Add(s2, 0, wx.ALL|wx.EXPAND, 2)
+        s.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 2)
+        
+        self.badsp = wx.CheckListBox(self, -1, choices=[], style=wx.LB_SINGLE)
+        s.Add(self.badsp, 1, wx.EXPAND)
+        self.badsp.Bind(wx.EVT_LISTBOX, self.OnClick)
+        
+        ws.Add(s, 1, wx.ALL|wx.EXPAND, 2)
+        
+#---------------------------- Corrections column -----------------------------
+        
+        s = wx.BoxSizer(wx.VERTICAL)
+        
+        s.Add(wx.StaticText(self, -1, "Possible Corrections:", size=(-1, bs)), 0, wx.ALL|wx.ALIGN_CENTER|wx.EXPAND, 2)
+        
+        s.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 2)
+        
+        self.replsp = FindPrefixListBox(self, -1, choices=[], style=wx.LB_SINGLE)
+        s.Add(self.replsp, 1, wx.EXPAND)
+        self.replsp.Bind(wx.EVT_LISTBOX, self.OnClickRepl)
+        
+        ws.Add(s, 1, wx.ALL|wx.EXPAND, 2)
+
+        self.SetSizer(ws)
+        self.SetAutoLayout(1)
+        self.SetupScrolling()
+    
+        wx.CallAfter(self.load_d)
+        
+    def load_d(self):
+        rp = self.root.getglobal('runpath')
+        df = os.path.join(rp, 'dictionary.txt')
+        while os.path.exists(df):
+            try:
+                dct = open(df, 'rb').read()
+            except Exception, why:
+                self.root.SetStatusText("Dictionary load failed! %s"%why)
+                break
+            
+            if dct.startswith('\xef\xbb\xbf'):
+                dct = dct[3:]
+            
+            try:
+                dct = dct.decode('utf-8')
+            except:
+                self.root.SetStatusText("Dictionary load failed!")
+                traceback.print_exc()
+                break
+            
+            x = dict.fromkeys(map(str, dct.split()))
+            _ = x.pop('', None)
+            
+            if not UNICODE:
+                for i in x:
+                    if type(i) is unicode:
+                        self.root.SetStatusText("Dictionary load failed!")
+                        self.root.SetStatusText("You must use a unicode-enabled PyPE/wxPython for the given dictionary")
+                        break
+            dictionary.update(x)
+                    
+        af = os.path.join(rp, 'alphabet.txt')
+        while os.path.exists(af):
+            try:
+                ab = open(af, 'rb').read()
+            except Exception, why:
+                self.root.SetStatusText("Alphabet load failed! %s"%why)
+                break
+            
+            if ab.startswith('\xef\xbb\xbf'):
+                ab = ab[3:]
+            
+            try:
+                ab = ab.decode('utf-8')
+            except:
+                self.root.SetStatusText("Alphabet load failed!")
+                traceback.print_exc()
+                break
+            
+            x = ''.join(ab.split())
+            x = dict.fromkeys([str(i).lower() for i in x.split(',') if i]).keys()
+            if not UNICODE:
+                for i in x:
+                    if type(i) is unicode:
+                        self.root.SetStatusText("Alphabet load failed!")
+                        self.root.SetStatusText("You must use a unicode-enabled PyPE/wxPython for the given alphabet")
+                        break
+            
+            global alphabet
+            alphabet = tuple(x)
+            break
+    
+    def OnCheckAll(self, evt):
+        for i in xrange(self.badsp.GetCount()):
+            self.badsp.Check(i)
+    
+    def OnSpellCheckSel(self, evt):
+        self.OnSpellCheck(evt, 1)
+    
+    def OnSpellCheck(self, evt, sel=0):
+        num, win = self.root.getNumWin(evt)
+        t = time.time()
+        
+        if win != self.document:
+            self.document = win
+            self.funcdefs.clear()
+        
+        if self.ignore_funcdefs.GetValue():
+            self.funcdefs = dict.fromkeys([i.lower() for i in win.tooltips])
+        else:
+            self.funcdefs.clear()
+        
+        fd = self.funcdefs
+        _dict = dictionary
+        self.dcts = dcts = {}
+        d = self.root.config["DICTIONARIES"]
+        for i in xrange(self.dictionaries.GetCount()):
+            if self.dictionaries.IsChecked(i):
+                dcts.update(d[self.dictionaries.GetString(i)])
+        
+        words = {}
+        if not sel:
+            doc = wx.stc.StyledTextCtrl.GetText(win)
+            self.start = 0
+        else:
+            doc = wx.stc.StyledTextCtrl.GetSelectedText(win)
+            self.start = min(win.GetSelection())
+        tt = transtable
+        try:
+            doc = doc.encode('ascii')
+        except:
+            tt = non_word
+        
+        wrds = doc.translate(tt).split()
+        wc = len(wrds)
+        for i in wrds:
+            if i not in _dict and i not in fd and i not in win.ignore and i not in dcts:
+                words[i] = words.get(i, 0) + 1
+        del wrds
+        words = words.items()
+        words.sort()
+        
+        self.OnClear(evt)
+        
+        cnt = 0
+        for i,j in words:
+            if j > 1:
+                i = "%s    (%i times)"%(i,j)
+            cnt += j
+            _ = self.badsp.Append(i)
+            ## self.badsp.Check(self.badsp.GetCount()-1)
+        self.root.SetStatusText("Found %i misspellings of %i words in %i words in %.1f seconds"%(cnt, len(words), wc, time.time()-t))
+    
+    def verify_and_select(self):
+        #select the document...
+        try:
+            self.document.ignore
+            for i,j in enumerate(self.root.control):
+                if j == self.document:
+                    self.root.control.SetSelection(i)
+                    break
+        except Exception, why:
+            print why
+            self.badsp.Clear()
+            self.replsp.Clear()
+            self.document = None
+            self.funcdefs.clear()
+            return 0
+        return 1
+    
+    def verify_findbox(self, evt):
+        self.root.OnShowReplacebar(evt)
+        fb = self.document.GetParent().GetWindow2()
+        fb.case.SetValue(0)
+        fb.wrap.SetValue(1)
+        x = self.badsp.GetStringSelection().split()[0]
+        if fb.box1.GetValue() != x:
+            fb.box1.SetValue(x)
+        fb.box2.SetValue('')
+        fb.wholeword = 1
+        return fb
+    
+    def OnClick(self, evt):
+        if not self.verify_and_select():
+            return
+        
+        self.replsp.Clear()
+        x = evt.GetString().split()[0]
+        lst = suggest(x, (dictionary, self.funcdefs, self.dcts))
+        for i in lst:
+            _ = self.replsp.Append(i)
+        
+        fb = self.verify_findbox(evt)
+        self.document.SetSelection(self.start, self.start)
+        fb.OnFindN(evt)
+        wx.CallAfter(self.badsp.SetFocus)
+    
+    def OnIgnore(self, evt):
+        for i in xrange(self.badsp.GetCount()-1, -1, -1):
+            if self.badsp.IsChecked(i):
+                self.document.ignore[self.badsp.GetString(i).split()[0]] = None
+                self.badsp.Delete(i)
+    
+    def OnClickRepl(self, evt):
+        if not self.verify_and_select():
+            return
+        
+        fb = self.verify_findbox(evt)
+        fb.smartcase.SetValue(1)
+        fb.box2.SetValue(evt.GetString().split()[0])
+    
+    def OnClear(self, evt):
+        self.badsp.Clear()
+        self.replsp.Clear()
+
+    def OnAdd(self, evt):
+        dct = self.root.config['DICTIONARIES']
+        k = list(dct)
+        k.sort()
+        
+        dlg = wx.SingleChoiceDialog(self,
+            'Which dictionary do you want to add words to?', 'Which Dictionary?',
+            k, wx.CHOICEDLG_STYLE)
+        
+        add = None
+        if dlg.ShowModal() == wx.ID_OK:
+            add = dlg.GetStringSelection()
+
+        dlg.Destroy()
+        
+        if not add:
+            return
+        
+        add = dct[add]
+        i = 0
+        for i in xrange(self.badsp.GetCount()-1, -1, -1):
+            if self.badsp.IsChecked(i):
+                add[self.badsp.GetString(i).split()[0]] = None
+                self.badsp.Delete(i)
+
+    def OnAddD(self, evt):
+        dlg = wx.TextEntryDialog(self, "Dictionary Name?", "What would you like your new dictionary to be named?", "")
+        resp = dlg.ShowModal()
+        valu = dlg.GetValue()
+        dlg.Destroy()
+        if resp != wx.ID_OK:
+            raise cancelled
+        
+        dct = self.root.config['DICTIONARIES']
+        
+        if valu in dct:
+            self.root.SetStatusText('Dictionary adding failed, dictionary %s already exists'%valu)
+            return
+        elif not valu.strip():
+            self.root.SetStatusText('Dictionary adding failed, cannot create unnamed dictionary')
+            return
+        
+        dct[valu] = {}
+        #refresh the dictionary list...
+        
+        insp = len([i for i in dct if i < valu])
+        self.dictionaries.Insert(valu, insp)
+        
+        
+    def OnDelD(self, evt):
+        k = list(self.root.config['DICTIONARIES'])
+        k.sort()
+        
+        dlg = wx.SingleChoiceDialog(self,
+            'Which dictionary would\nyou like to delete?', 'Delete Dictionary?',
+            k, wx.CHOICEDLG_STYLE)
+        
+        dele = None
+        if dlg.ShowModal() == wx.ID_OK:
+            dele = dlg.GetStringSelection()
+
+        dlg.Destroy()
+        if not dele:
+            return
+        
+        dct = self.root.config['DICTIONARIES']
+        
+        if dct[dele]:
+            #are you sure?
+            dlg = wx.MessageDialog(self, '''\
+                Are you sure you want to delete the custom dictionary: '%s'?
+                It has %i words.'''.replace(16*' ', '')%(dele, len(dct[dele])),
+                "Are you sure?", wx.OK|wx.CANCEL)
+            retr = dlg.ShowModal()
+            dlg.Destroy()
+            if retr != wx.ID_OK:
+                self.root.SetStatusText('Dictionary deletion cancelled')
+                return
+        
+        del dct[dele]
+        dsp = len([i for i in dct if i < dele])
+        self.dictionaries.Delete(dsp)
+        
+#FindPrefixListBox derived from the wxPython demo
+class FindPrefixListBox(wx.ListBox):
+    def __init__(self, parent, id=-1, choices=[], style=wx.LB_SINGLE):
+        wx.ListBox.__init__(self, parent, id, choices=choices, style=wx.LB_SINGLE)
+        self.typedText = ''
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
+
+    def FindPrefix(self, prefix):
+        self.log.WriteText('Looking for prefix: %s\n' % prefix)
+
+        if prefix:
+            prefix = prefix.lower()
+            length = len(prefix)
+
+            # Changed in 2.5 because ListBox.Number() is no longer supported.
+            # ListBox.GetCount() is now the appropriate way to go.
+            for x in xrange(self.GetCount()):
+                text = self.GetString(x)
+                text = text.lower()
+
+                if text[:length] == prefix:
+                    return x
+
+        return -1
+
+    def OnKey(self, evt):
+        key = evt.GetKeyCode()
+
+        if key >= 32 and key <= 127:
+            self.typedText = self.typedText + chr(key)
+            item = self.FindPrefix(self.typedText)
+
+            if item != -1:
+                self.SetSelection(item)
+
+        elif key == wx.WXK_BACK:   # backspace removes one character and backs up
+            self.typedText = self.typedText[:-1]
+
+            if not self.typedText:
+                self.SetSelection(0)
+            else:
+                item = self.FindPrefix(self.typedText)
+
+                if item != -1:
+                    self.SetSelection(item)
+        else:
+            self.typedText = ''
+            evt.Skip()