--- a/plugins/findbar.py +++ b/plugins/findbar.py @@ -1,5 +1,6 @@ import wx +import wx.stc import compiler class OnCloseBar: #embedded callback to destroy the findbar on removal @@ -11,6 +12,9 @@ self.c.Destroy() del self.c +word = dict.fromkeys(map(ord, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')) +non_word = dict.fromkeys([chr(i) for i in xrange(256) if i not in word]) + class ReplaceBar(wx.Panel): def __init__(self, parent, root): #init the object @@ -24,6 +28,8 @@ self.loop = 0 self.shiftdown = 0 self.buttons = [] + self.replacing = 0 + self.wholeword = 0 prefs = self.readPreferences() self.setup() @@ -142,7 +148,8 @@ matchcase = self.case.GetValue() and wx.FR_MATCHCASE if not findTxt: if not which: - evt.Skip() + if evt: + evt.Skip() raise cancelled return "", None, None @@ -156,7 +163,8 @@ #nothing to find! if not findTxt: if not which: - evt.Skip() + if evt: + evt.Skip() raise cancelled return "", None, None @@ -171,25 +179,48 @@ self.status(msg) line = win.LineFromPosition(posns) win.GotoLine(line) + posns, posne = self.getRange(win, posns, posne-posns) win.SetSelection(posns, posne) win.EnsureVisible(line) win.EnsureCaretVisible() + + def getRange(self, win, start, chars): + end = start + for i in xrange(chars): + end = win.PositionAfter(end) + return start, end + + def isWholeWord(self, start, chars, win): + #we may need to use this for unicode whole word checks + start, end = self.getRange(win, start, chars) + if start != 0: + if win.GetCharAt(win.PositionBefore(start)) not in non_word: + return 0 + + if end != win.GetTextLength(): + if win.GetCharAt(win.PositionAfter(end)) not in non_word: + return 0 + + return 1 def OnFindN(self, evt, incr=0): self._lastcall = self.OnFindN self.incr = incr findTxt, matchcase, win = self.getFinds(evt) + flags = wx.FR_DOWN|matchcase + if self.wholeword: + flags |= wx.stc.STC_FIND_WHOLEWORD #handle finding next item, handling wrap-arounds as necessary st = win.GetSelection()[1-incr] - posn = win.FindText(st, win.GetTextLength(), findTxt, wx.FR_DOWN|matchcase) + posn = win.FindText(st, win.GetTextLength(), findTxt, flags) if posn != -1: self.sel(posn, posn+len(findTxt), '', win) self.loop = 0 return if self.wrap.GetValue() and st != 0: - posn = win.FindText(0, win.GetTextLength(), findTxt, wx.FR_DOWN|matchcase) + posn = win.FindText(0, win.GetTextLength(), findTxt, flags) self.loop = 1 if posn != -1: @@ -202,17 +233,21 @@ self._lastcall = self.OnFindP self.incr = 0 findTxt, matchcase, win = self.getFinds(evt) + flags = matchcase + if self.wholeword: + flags |= wx.stc.STC_FIND_WHOLEWORD + #handle finding previous item, handling wrap-arounds as necessary - st = max(win.GetSelection()[0]-len(findTxt), 0) - posn = win.FindText(st, 0, findTxt, matchcase) + st = max(win.GetSelection()[0]-len(ft), 0) + posn = win.FindText(st, 0, findTxt, flags) if posn != -1: self.sel(posn, posn+len(findTxt), '', win) self.loop = 0 return if self.wrap.GetValue() and st != win.GetTextLength(): - posn = win.FindText(win.GetTextLength(), 0, findTxt, matchcase) + posn = win.FindText(win.GetTextLength(), 0, findTxt, flags) self.loop = 1 if posn != -1: @@ -222,6 +257,8 @@ self.OnNotFound() def OnKeyDown(self, evt): + if self.FindFocus() == self.box1: + self.wholeword = 0 kc = evt.GetKeyCode() if kc == wx.WXK_ESCAPE: p = self.parent.GetWindow1() @@ -236,12 +273,14 @@ return self.buttons[0].SetFocus() elif len(self.buttons) == 2 and foc == self.box2: return self.buttons[1].SetFocus() - evt.Skip() + if evt: + evt.Skip() def OnKeyUp(self, evt): if evt.GetKeyCode() == wx.WXK_SHIFT: self.shiftdown = 0 - evt.Skip() + if evt: + evt.Skip() def OnEnter(self, evt): if self.shiftdown: @@ -250,11 +289,13 @@ def OnKillFocus(self, evt): self.shiftdown = 0 - evt.Skip() + if evt: + evt.Skip() def OnSetFocus(self, evt): self.box1.SetMark(0, self.box1.GetLastPosition()) - evt.Skip() + if evt: + evt.Skip() def OnReplace(self, evt): findTxt, matchcase, win = self.getFinds(evt) @@ -299,14 +340,17 @@ replaceTxt = replaceTxt[:1].lower() + replaceTxt[1:] sel = win.GetSelection() - win.ReplaceSelection(replaceTxt) - win.SetSelection(sel[0], sel[0]+len(replaceTxt)) - self.OnFindN(evt) - - return sel, len(findTxt)-len(replaceTxt) - - def replaceAll(self, evt, ris): - + if win.CanEdit(): + win.ReplaceSelection(replaceTxt) + #fix for unicode... + win.SetSelection(min(sel), min(sel)+len(replaceTxt)) + self.OnFindN(evt) + + return sel, len(findTxt)-len(replaceTxt) + else: + return (max(sel), max(sel)), 0 + + def _replaceAll(self, evt, ris): win = self.parent.GetWindow1() ostart, oend = win.GetSelection() @@ -340,17 +384,85 @@ elif rsel < ostart: ostart -= delta oend -= delta - + try: + wx.Yield() + except: + pass ## print self.loop, failed self.sel(ostart, oend, '', win) win.SetFocus() win.EndUndoAction() + + def replaceAll(self, evt, ris): + win = self.parent.GetWindow1() + ostart, oend = win.GetSelection() + + #ris = self.replinsel.GetValue() + win.BeginUndoAction() + if ris: + win.SetSelection(ostart, ostart) + else: + win.SetSelection(0, 0) + + self.replacing = 1 + wx.CallAfter(self.ReentrantReplace, (ostart, oend, ris, win)) + + def ReentrantReplace(self, state): + ostart, oend, ris, win = state + + try: + self.loop + except: + return + + cont = 1 + count = 0 + while not self.loop: + sel = win.GetSelection() + if ris and (sel[0] < ostart or sel[1] > oend): + cont = 0 + break + #find/replace next item + (lsel, rsel), delta = self.OnReplace(None) + ## print (lsel, rsel), delta, repr(win.GetTextRange(lsel, rsel)), self.loop + #if nothing found, continue + if lsel == -1: + continue + #if change was entirely in the old selection... + elif ostart <= lsel and rsel <= oend: + oend -= delta + #original selection is encompassed by the replacement. + elif lsel <= ostart and rsel >= oend: + ostart = oend = lsel + #replaced portion is to the left of the original selection + elif rsel < ostart: + ostart -= delta + oend -= delta + count += 1 + if count >= 10 and not self.loop: + break + else: + cont = 0 + + if cont: + wx.FutureCall(1, self.ReentrantReplace, (ostart, oend, ris, win)) + else: + self.replacing = 0 + self.sel(ostart, oend, '', win) + if self.root.control.GetPageCount() and \ + self.root.control.GetCurrentPage() == self.parent: + win.SetFocus() + win.EndUndoAction() def OnReplaceAll(self, evt): + if self.replacing: + return self.replaceAll(evt, 0) def OnReplaceSel(self, evt): + if self.replacing: + return self.replaceAll(evt, 1) #----------------------------------