📄 findservice.py
字号:
#----------------------------------------------------------------------------
# Name: FindService.py
# Purpose: Find Service for pydocview
#
# Author: Peter Yared, Morgan Hua
#
# Created: 8/15/03
# CVS-ID: $Id: FindService.py,v 1.3 2005/12/30 23:01:34 RD Exp $
# Copyright: (c) 2003-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import wx
import wx.lib.docview
import wx.lib.pydocview
import re
_ = wx.GetTranslation
#----------------------------------------------------------------------------
# Constants
#----------------------------------------------------------------------------
FIND_MATCHPATTERN = "FindMatchPattern"
FIND_MATCHREPLACE = "FindMatchReplace"
FIND_MATCHCASE = "FindMatchCase"
FIND_MATCHWHOLEWORD = "FindMatchWholeWordOnly"
FIND_MATCHREGEXPR = "FindMatchRegularExpr"
FIND_MATCHWRAP = "FindMatchWrap"
FIND_MATCHUPDOWN = "FindMatchUpDown"
FIND_SYNTAXERROR = -2
SPACE = 10
HALF_SPACE = 5
#----------------------------------------------------------------------------
# Classes
#----------------------------------------------------------------------------
class FindService(wx.lib.pydocview.DocService):
#----------------------------------------------------------------------------
# Constants
#----------------------------------------------------------------------------
FIND_ID = wx.NewId() # for bringing up Find dialog box
FINDONE_ID = wx.NewId() # for doing Find
FIND_PREVIOUS_ID = wx.NewId() # for doing Find Next
FIND_NEXT_ID = wx.NewId() # for doing Find Prev
REPLACE_ID = wx.NewId() # for bringing up Replace dialog box
REPLACEONE_ID = wx.NewId() # for doing a Replace
REPLACEALL_ID = wx.NewId() # for doing Replace All
GOTO_LINE_ID = wx.NewId() # for bringing up Goto dialog box
# Extending bitmasks: wx.FR_WHOLEWORD, wx.FR_MATCHCASE, and wx.FR_DOWN
FR_REGEXP = max([wx.FR_WHOLEWORD, wx.FR_MATCHCASE, wx.FR_DOWN]) << 1
FR_WRAP = FR_REGEXP << 1
def __init__(self):
self._replaceDialog = None
self._findDialog = None
self._findReplaceData = wx.FindReplaceData()
self._findReplaceData.SetFlags(wx.FR_DOWN)
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
""" Install Find Service Menu Items """
editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
editMenu.AppendSeparator()
editMenu.Append(FindService.FIND_ID, _("&Find...\tCtrl+F"), _("Finds the specified text"))
wx.EVT_MENU(frame, FindService.FIND_ID, frame.ProcessEvent)
wx.EVT_UPDATE_UI(frame, FindService.FIND_ID, frame.ProcessUpdateUIEvent)
editMenu.Append(FindService.FIND_PREVIOUS_ID, _("Find &Previous\tShift+F3"), _("Finds the specified text"))
wx.EVT_MENU(frame, FindService.FIND_PREVIOUS_ID, frame.ProcessEvent)
wx.EVT_UPDATE_UI(frame, FindService.FIND_PREVIOUS_ID, frame.ProcessUpdateUIEvent)
editMenu.Append(FindService.FIND_NEXT_ID, _("Find &Next\tF3"), _("Finds the specified text"))
wx.EVT_MENU(frame, FindService.FIND_NEXT_ID, frame.ProcessEvent)
wx.EVT_UPDATE_UI(frame, FindService.FIND_NEXT_ID, frame.ProcessUpdateUIEvent)
editMenu.Append(FindService.REPLACE_ID, _("R&eplace...\tCtrl+H"), _("Replaces specific text with different text"))
wx.EVT_MENU(frame, FindService.REPLACE_ID, frame.ProcessEvent)
wx.EVT_UPDATE_UI(frame, FindService.REPLACE_ID, frame.ProcessUpdateUIEvent)
editMenu.Append(FindService.GOTO_LINE_ID, _("&Go to Line...\tCtrl+G"), _("Goes to a certain line in the file"))
wx.EVT_MENU(frame, FindService.GOTO_LINE_ID, frame.ProcessEvent)
wx.EVT_UPDATE_UI(frame, FindService.GOTO_LINE_ID, frame.ProcessUpdateUIEvent)
# wxBug: wxToolBar::GetToolPos doesn't exist, need it to find cut tool and then insert find in front of it.
toolBar.InsertTool(6, FindService.FIND_ID, getFindBitmap(), shortHelpString = _("Find"), longHelpString = _("Finds the specified text"))
toolBar.InsertSeparator(6)
toolBar.Realize()
frame.Bind(wx.EVT_FIND, frame.ProcessEvent)
frame.Bind(wx.EVT_FIND_NEXT, frame.ProcessEvent)
frame.Bind(wx.EVT_FIND_REPLACE, frame.ProcessEvent)
frame.Bind(wx.EVT_FIND_REPLACE_ALL, frame.ProcessEvent)
def ProcessUpdateUIEvent(self, event):
id = event.GetId()
if (id == FindService.FIND_ID
or id == FindService.FIND_PREVIOUS_ID
or id == FindService.FIND_NEXT_ID
or id == FindService.REPLACE_ID
or id == FindService.GOTO_LINE_ID):
event.Enable(False)
return True
else:
return False
def ShowFindReplaceDialog(self, findString="", replace = False):
""" Display find/replace dialog box.
Parameters: findString is the default value shown in the find/replace dialog input field.
If replace is True, the replace dialog box is shown, otherwise only the find dialog box is shown.
"""
if replace:
if self._findDialog != None:
# No reason to have both find and replace dialogs up at the same time
self._findDialog.DoClose()
self._findDialog = None
self._replaceDialog = FindReplaceDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Replace"), size=(320,200), findString=findString)
self._replaceDialog.CenterOnParent()
self._replaceDialog.Show(True)
else:
if self._replaceDialog != None:
# No reason to have both find and replace dialogs up at the same time
self._replaceDialog.DoClose()
self._replaceDialog = None
self._findDialog = FindDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Find"), size=(320,200), findString=findString)
self._findDialog.CenterOnParent()
self._findDialog.Show(True)
def OnFindClose(self, event):
""" Cleanup handles when find/replace dialog is closed """
if self._findDialog != None:
self._findDialog = None
elif self._replaceDialog != None:
self._replaceDialog = None
def GetCurrentDialog(self):
""" return handle to either the find or replace dialog """
if self._findDialog != None:
return self._findDialog
return self._replaceDialog
def GetLineNumber(self, parent):
""" Display Goto Line Number dialog box """
line = -1
dialog = wx.TextEntryDialog(parent, _("Enter line number to go to:"), _("Go to Line"))
dialog.CenterOnParent()
if dialog.ShowModal() == wx.ID_OK:
try:
line = int(dialog.GetValue())
if line > 65535:
line = 65535
except:
pass
dialog.Destroy()
# This one is ugly: wx.GetNumberFromUser("", _("Enter line number to go to:"), _("Go to Line"), 1, min = 1, max = 65535, parent = parent)
return line
def DoFind(self, findString, replaceString, text, startLoc, endLoc, down, matchCase, wholeWord, regExpr = False, replace = False, replaceAll = False, wrap = False):
""" Do the actual work of the find/replace.
Returns the tuple (count, start, end, newText).
count = number of string replacements
start = start position of found string
end = end position of found string
newText = new replaced text
"""
flags = 0
if regExpr:
pattern = findString
else:
pattern = re.escape(findString) # Treat the strings as a literal string
if not matchCase:
flags = re.IGNORECASE
if wholeWord:
pattern = r"\b%s\b" % pattern
try:
reg = re.compile(pattern, flags)
except:
# syntax error of some sort
import sys
msgTitle = wx.GetApp().GetAppName()
if not msgTitle:
msgTitle = _("Regular Expression Search")
wx.MessageBox(_("Invalid regular expression \"%s\". %s") % (pattern, sys.exc_value),
msgTitle,
wx.OK | wx.ICON_EXCLAMATION,
self.GetView())
return FIND_SYNTAXERROR, None, None, None
if replaceAll:
newText, count = reg.subn(replaceString, text)
if count == 0:
return -1, None, None, None
else:
return count, None, None, newText
start = -1
if down:
match = reg.search(text, endLoc)
if match == None:
if wrap: # try again, but this time from top of file
match = reg.search(text, 0)
if match == None:
return -1, None, None, None
else:
return -1, None, None, None
start = match.start()
end = match.end()
else:
match = reg.search(text)
if match == None:
return -1, None, None, None
found = None
i, j = match.span()
while i < startLoc and j <= startLoc:
found = match
if i == j:
j = j + 1
match = reg.search(text, j)
if match == None:
break
i, j = match.span()
if found == None:
if wrap: # try again, but this time from bottom of file
match = reg.search(text, startLoc)
if match == None:
return -1, None, None, None
found = None
i, j = match.span()
end = len(text)
while i < end and j <= end:
found = match
if i == j:
j = j + 1
match = reg.search(text, j)
if match == None:
break
i, j = match.span()
if found == None:
return -1, None, None, None
else:
return -1, None, None, None
start = found.start()
end = found.end()
if replace and start != -1:
newText, count = reg.subn(replaceString, text, 1)
return count, start, end, newText
return 0, start, end, None
def SaveFindConfig(self, findString, wholeWord, matchCase, regExpr = None, wrap = None, upDown = None, replaceString = None):
""" Save find/replace patterns and search flags to registry.
findString = search pattern
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -