02b800ce7c
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@36607 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
469 lines
21 KiB
Python
469 lines
21 KiB
Python
#----------------------------------------------------------------------------
|
|
# Name: IDEFindService.py
|
|
# Purpose: Find Service for pydocview
|
|
#
|
|
# Author: Morgan Hua
|
|
#
|
|
# Created: 8/15/03
|
|
# CVS-ID: $Id$
|
|
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
|
# License: wxWindows License
|
|
#----------------------------------------------------------------------------
|
|
|
|
import wx
|
|
import wx.lib.docview
|
|
import os
|
|
from os.path import join
|
|
import re
|
|
import ProjectEditor
|
|
import MessageService
|
|
import FindService
|
|
import OutlineService
|
|
_ = wx.GetTranslation
|
|
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Constants
|
|
#----------------------------------------------------------------------------
|
|
FILENAME_MARKER = _("Found in file: ")
|
|
PROJECT_MARKER = _("Searching project: ")
|
|
FIND_MATCHDIR = "FindMatchDir"
|
|
FIND_MATCHDIRSUBFOLDERS = "FindMatchDirSubfolders"
|
|
|
|
SPACE = 10
|
|
HALF_SPACE = 5
|
|
|
|
|
|
class FindInDirService(FindService.FindService):
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Constants
|
|
#----------------------------------------------------------------------------
|
|
FINDALL_ID = wx.NewId() # for bringing up Find All dialog box
|
|
FINDDIR_ID = wx.NewId() # for bringing up Find Dir dialog box
|
|
|
|
|
|
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
|
|
FindService.FindService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
|
|
|
|
editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
|
|
wx.EVT_MENU(frame, FindInDirService.FINDALL_ID, self.ProcessEvent)
|
|
wx.EVT_UPDATE_UI(frame, FindInDirService.FINDALL_ID, self.ProcessUpdateUIEvent)
|
|
editMenu.Append(FindInDirService.FINDALL_ID, _("Find in Project...\tCtrl+Shift+F"), _("Searches for the specified text in all the files in the project"))
|
|
wx.EVT_MENU(frame, FindInDirService.FINDDIR_ID, self.ProcessEvent)
|
|
wx.EVT_UPDATE_UI(frame, FindInDirService.FINDDIR_ID, self.ProcessUpdateUIEvent)
|
|
editMenu.Append(FindInDirService.FINDDIR_ID, _("Find in Directory..."), _("Searches for the specified text in all the files in the directory"))
|
|
|
|
|
|
def ProcessEvent(self, event):
|
|
id = event.GetId()
|
|
if id == FindInDirService.FINDALL_ID:
|
|
view = wx.GetApp().GetDocumentManager().GetCurrentView()
|
|
if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
|
|
self.ShowFindAllDialog(view.GetCtrl().GetSelectedText())
|
|
else:
|
|
self.ShowFindAllDialog()
|
|
return True
|
|
elif id == FindInDirService.FINDDIR_ID:
|
|
view = wx.GetApp().GetDocumentManager().GetCurrentView()
|
|
if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
|
|
self.ShowFindDirDialog(view.GetCtrl().GetSelectedText())
|
|
else:
|
|
self.ShowFindDirDialog()
|
|
return True
|
|
else:
|
|
return FindService.FindService.ProcessEvent(self, event)
|
|
|
|
|
|
def ProcessUpdateUIEvent(self, event):
|
|
id = event.GetId()
|
|
if id == FindInDirService.FINDALL_ID:
|
|
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
|
|
if projectService.GetFilesFromCurrentProject():
|
|
event.Enable(True)
|
|
else:
|
|
event.Enable(False)
|
|
return True
|
|
elif id == FindInDirService.FINDDIR_ID:
|
|
event.Enable(True)
|
|
else:
|
|
return FindService.FindService.ProcessUpdateUIEvent(self, event)
|
|
|
|
|
|
def ShowFindDirDialog(self, findString=None):
|
|
config = wx.ConfigBase_Get()
|
|
|
|
frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Directory"), size= (320,200))
|
|
borderSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
contentSizer = wx.BoxSizer(wx.VERTICAL)
|
|
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
lineSizer.Add(wx.StaticText(frame, -1, _("Directory:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
|
dirCtrl = wx.TextCtrl(frame, -1, config.Read(FIND_MATCHDIR, ""), size=(200,-1))
|
|
dirCtrl.SetToolTipString(dirCtrl.GetValue())
|
|
lineSizer.Add(dirCtrl, 0, wx.LEFT, HALF_SPACE)
|
|
findDirButton = wx.Button(frame, -1, _("Browse..."))
|
|
lineSizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
|
|
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
|
|
|
def OnBrowseButton(event):
|
|
dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
|
|
dir = dirCtrl.GetValue()
|
|
if len(dir):
|
|
dlg.SetPath(dir)
|
|
dlg.CenterOnParent()
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
dirCtrl.SetValue(dlg.GetPath())
|
|
dirCtrl.SetToolTipString(dirCtrl.GetValue())
|
|
dirCtrl.SetInsertionPointEnd()
|
|
dlg.Destroy()
|
|
wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
|
|
|
|
subfolderCtrl = wx.CheckBox(frame, -1, _("Search in subdirectories"))
|
|
subfolderCtrl.SetValue(config.ReadInt(FIND_MATCHDIRSUBFOLDERS, True))
|
|
contentSizer.Add(subfolderCtrl, 0, wx.BOTTOM, SPACE)
|
|
|
|
lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion
|
|
lineSizer.Add(wx.StaticLine(frame, -1, size = (10,-1)), 0, flag=wx.EXPAND)
|
|
contentSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM, border=HALF_SPACE)
|
|
|
|
if wx.Platform == "__WXMAC__":
|
|
contentSizer.Add((-1, 10), 0, wx.EXPAND)
|
|
|
|
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
|
if not findString:
|
|
findString = config.Read(FindService.FIND_MATCHPATTERN, "")
|
|
findCtrl = wx.TextCtrl(frame, -1, findString, size=(200,-1))
|
|
findCtrl.SetFocus()
|
|
findCtrl.SetSelection(0,-1)
|
|
lineSizer.Add(findCtrl, 0, wx.LEFT, HALF_SPACE)
|
|
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
|
wholeWordCtrl = wx.CheckBox(frame, -1, _("Match whole word only"))
|
|
wholeWordCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHWHOLEWORD, False))
|
|
matchCaseCtrl = wx.CheckBox(frame, -1, _("Match case"))
|
|
matchCaseCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHCASE, False))
|
|
regExprCtrl = wx.CheckBox(frame, -1, _("Regular expression"))
|
|
regExprCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHREGEXPR, False))
|
|
contentSizer.Add(wholeWordCtrl, 0, wx.BOTTOM, SPACE)
|
|
contentSizer.Add(matchCaseCtrl, 0, wx.BOTTOM, SPACE)
|
|
contentSizer.Add(regExprCtrl, 0, wx.BOTTOM, SPACE)
|
|
borderSizer.Add(contentSizer, 0, wx.TOP | wx.BOTTOM | wx.LEFT, SPACE)
|
|
|
|
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
|
findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
|
|
findBtn.SetDefault()
|
|
BTM_SPACE = HALF_SPACE
|
|
if wx.Platform == "__WXMAC__":
|
|
BTM_SPACE = SPACE
|
|
buttonSizer.Add(findBtn, 0, wx.BOTTOM, BTM_SPACE)
|
|
buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
|
|
borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
|
|
|
|
frame.SetSizer(borderSizer)
|
|
frame.Fit()
|
|
|
|
frame.CenterOnParent()
|
|
status = frame.ShowModal()
|
|
|
|
passedCheck = False
|
|
while status == wx.ID_OK and not passedCheck:
|
|
if not os.path.exists(dirCtrl.GetValue()):
|
|
dlg = wx.MessageDialog(frame,
|
|
_("'%s' does not exist.") % dirCtrl.GetValue(),
|
|
_("Find in Directory"),
|
|
wx.OK | wx.ICON_EXCLAMATION
|
|
)
|
|
dlg.CenterOnParent()
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
|
|
status = frame.ShowModal()
|
|
elif len(findCtrl.GetValue()) == 0:
|
|
dlg = wx.MessageDialog(frame,
|
|
_("'Find what:' cannot be empty."),
|
|
_("Find in Directory"),
|
|
wx.OK | wx.ICON_EXCLAMATION
|
|
)
|
|
dlg.CenterOnParent()
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
|
|
status = frame.ShowModal()
|
|
else:
|
|
passedCheck = True
|
|
|
|
|
|
# save user choice state for this and other Find Dialog Boxes
|
|
dirString = dirCtrl.GetValue()
|
|
searchSubfolders = subfolderCtrl.IsChecked()
|
|
self.SaveFindDirConfig(dirString, searchSubfolders)
|
|
|
|
findString = findCtrl.GetValue()
|
|
matchCase = matchCaseCtrl.IsChecked()
|
|
wholeWord = wholeWordCtrl.IsChecked()
|
|
regExpr = regExprCtrl.IsChecked()
|
|
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
|
|
|
|
frame.Destroy()
|
|
if status == wx.ID_OK:
|
|
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
|
messageService.ShowWindow()
|
|
|
|
view = messageService.GetView()
|
|
if view:
|
|
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
|
|
view.ClearLines()
|
|
view.SetCallback(self.OnJumpToFoundLine)
|
|
|
|
view.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString, dirString))
|
|
|
|
if os.path.isfile(dirString):
|
|
try:
|
|
docFile = file(dirString, 'r')
|
|
lineNum = 1
|
|
needToDisplayFilename = True
|
|
line = docFile.readline()
|
|
while line:
|
|
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
|
if count != -1:
|
|
if needToDisplayFilename:
|
|
view.AddLines(FILENAME_MARKER + dirString + "\n")
|
|
needToDisplayFilename = False
|
|
line = repr(lineNum).zfill(4) + ":" + line
|
|
view.AddLines(line)
|
|
line = docFile.readline()
|
|
lineNum += 1
|
|
if not needToDisplayFilename:
|
|
view.AddLines("\n")
|
|
except IOError, (code, message):
|
|
print _("Warning, unable to read file: '%s'. %s") % (dirString, message)
|
|
else:
|
|
# do search in files on disk
|
|
for root, dirs, files in os.walk(dirString):
|
|
if not searchSubfolders and root != dirString:
|
|
break
|
|
|
|
for name in files:
|
|
filename = os.path.join(root, name)
|
|
try:
|
|
docFile = file(filename, 'r')
|
|
except IOError, (code, message):
|
|
print _("Warning, unable to read file: '%s'. %s") % (filename, message)
|
|
continue
|
|
|
|
lineNum = 1
|
|
needToDisplayFilename = True
|
|
line = docFile.readline()
|
|
while line:
|
|
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
|
if count != -1:
|
|
if needToDisplayFilename:
|
|
view.AddLines(FILENAME_MARKER + filename + "\n")
|
|
needToDisplayFilename = False
|
|
line = repr(lineNum).zfill(4) + ":" + line
|
|
view.AddLines(line)
|
|
line = docFile.readline()
|
|
lineNum += 1
|
|
if not needToDisplayFilename:
|
|
view.AddLines("\n")
|
|
|
|
view.AddLines(_("Search completed."))
|
|
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
|
|
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def SaveFindDirConfig(self, dirString, searchSubfolders):
|
|
""" Save search dir patterns and flags to registry.
|
|
|
|
dirString = search directory
|
|
searchSubfolders = Search subfolders
|
|
"""
|
|
config = wx.ConfigBase_Get()
|
|
config.Write(FIND_MATCHDIR, dirString)
|
|
config.WriteInt(FIND_MATCHDIRSUBFOLDERS, searchSubfolders)
|
|
|
|
|
|
def ShowFindAllDialog(self, findString=None):
|
|
config = wx.ConfigBase_Get()
|
|
|
|
frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Project"), size= (320,200))
|
|
borderSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
contentSizer = wx.BoxSizer(wx.VERTICAL)
|
|
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
|
if not findString:
|
|
findString = config.Read(FindService.FIND_MATCHPATTERN, "")
|
|
findCtrl = wx.TextCtrl(frame, -1, findString, size=(200,-1))
|
|
lineSizer.Add(findCtrl, 0, wx.LEFT, HALF_SPACE)
|
|
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
|
wholeWordCtrl = wx.CheckBox(frame, -1, _("Match whole word only"))
|
|
wholeWordCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHWHOLEWORD, False))
|
|
matchCaseCtrl = wx.CheckBox(frame, -1, _("Match case"))
|
|
matchCaseCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHCASE, False))
|
|
regExprCtrl = wx.CheckBox(frame, -1, _("Regular expression"))
|
|
regExprCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHREGEXPR, False))
|
|
contentSizer.Add(wholeWordCtrl, 0, wx.BOTTOM, SPACE)
|
|
contentSizer.Add(matchCaseCtrl, 0, wx.BOTTOM, SPACE)
|
|
contentSizer.Add(regExprCtrl, 0, wx.BOTTOM, SPACE)
|
|
borderSizer.Add(contentSizer, 0, wx.TOP | wx.BOTTOM | wx.LEFT, SPACE)
|
|
|
|
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
|
findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
|
|
findBtn.SetDefault()
|
|
BTM_SPACE = HALF_SPACE
|
|
if wx.Platform == "__WXMAC__":
|
|
BTM_SPACE = SPACE
|
|
buttonSizer.Add(findBtn, 0, wx.BOTTOM, BTM_SPACE)
|
|
buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
|
|
borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
|
|
|
|
frame.SetSizer(borderSizer)
|
|
frame.Fit()
|
|
|
|
frame.CenterOnParent()
|
|
status = frame.ShowModal()
|
|
|
|
# save user choice state for this and other Find Dialog Boxes
|
|
findString = findCtrl.GetValue()
|
|
matchCase = matchCaseCtrl.IsChecked()
|
|
wholeWord = wholeWordCtrl.IsChecked()
|
|
regExpr = regExprCtrl.IsChecked()
|
|
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
|
|
|
|
frame.Destroy()
|
|
if status == wx.ID_OK:
|
|
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
|
messageService.ShowWindow()
|
|
|
|
view = messageService.GetView()
|
|
if view:
|
|
view.ClearLines()
|
|
view.SetCallback(self.OnJumpToFoundLine)
|
|
|
|
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
|
|
projectFilenames = projectService.GetFilesFromCurrentProject()
|
|
|
|
projView = projectService.GetView()
|
|
if projView:
|
|
projName = wx.lib.docview.FileNameFromPath(projView.GetDocument().GetFilename())
|
|
view.AddLines(PROJECT_MARKER + projName + "\n\n")
|
|
|
|
# do search in open files first, open files may have been modified and different from disk because it hasn't been saved
|
|
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
|
|
openDocsInProject = filter(lambda openDoc: openDoc.GetFilename() in projectFilenames, openDocs)
|
|
for openDoc in openDocsInProject:
|
|
if isinstance(openDoc, ProjectEditor.ProjectDocument): # don't search project model
|
|
continue
|
|
|
|
openDocView = openDoc.GetFirstView()
|
|
# some views don't have a in memory text object to search through such as the PM and the DM
|
|
# even if they do have a non-text searchable object, how do we display it in the message window?
|
|
if not hasattr(openDocView, "GetValue"):
|
|
continue
|
|
text = openDocView.GetValue()
|
|
|
|
lineNum = 1
|
|
needToDisplayFilename = True
|
|
start = 0
|
|
end = 0
|
|
count = 0
|
|
while count != -1:
|
|
count, foundStart, foundEnd, newText = self.DoFind(findString, None, text, start, end, True, matchCase, wholeWord, regExpr)
|
|
if count != -1:
|
|
if needToDisplayFilename:
|
|
view.AddLines(FILENAME_MARKER + openDoc.GetFilename() + "\n")
|
|
needToDisplayFilename = False
|
|
|
|
lineNum = openDocView.LineFromPosition(foundStart)
|
|
line = repr(lineNum).zfill(4) + ":" + openDocView.GetLine(lineNum)
|
|
view.AddLines(line)
|
|
|
|
start = text.find("\n", foundStart)
|
|
if start == -1:
|
|
break
|
|
end = start
|
|
if not needToDisplayFilename:
|
|
view.AddLines("\n")
|
|
openDocNames = map(lambda openDoc: openDoc.GetFilename(), openDocs)
|
|
|
|
# do search in closed files, skipping the open ones we already searched
|
|
filenames = filter(lambda filename: filename not in openDocNames, projectFilenames)
|
|
for filename in filenames:
|
|
try:
|
|
docFile = file(filename, 'r')
|
|
except IOError, (code, message):
|
|
print _("Warning, unable to read file: '%s'. %s") % (filename, message)
|
|
continue
|
|
|
|
lineNum = 1
|
|
needToDisplayFilename = True
|
|
line = docFile.readline()
|
|
while line:
|
|
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
|
if count != -1:
|
|
if needToDisplayFilename:
|
|
view.AddLines(FILENAME_MARKER + filename + "\n")
|
|
needToDisplayFilename = False
|
|
line = repr(lineNum).zfill(4) + ":" + line
|
|
view.AddLines(line)
|
|
line = docFile.readline()
|
|
lineNum += 1
|
|
if not needToDisplayFilename:
|
|
view.AddLines("\n")
|
|
|
|
view.AddLines(_("Search for '%s' completed.") % findString)
|
|
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def OnJumpToFoundLine(self, event):
|
|
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
|
lineText, pos = messageService.GetView().GetCurrLine()
|
|
if lineText == "\n" or lineText.find(FILENAME_MARKER) != -1 or lineText.find(PROJECT_MARKER) != -1:
|
|
return
|
|
lineEnd = lineText.find(":")
|
|
if lineEnd == -1:
|
|
return
|
|
else:
|
|
lineNum = int(lineText[0:lineEnd])
|
|
|
|
text = messageService.GetView().GetText()
|
|
curPos = messageService.GetView().GetCurrentPos()
|
|
|
|
startPos = text.rfind(FILENAME_MARKER, 0, curPos)
|
|
endPos = text.find("\n", startPos)
|
|
filename = text[startPos + len(FILENAME_MARKER):endPos]
|
|
|
|
foundView = None
|
|
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
|
|
for openDoc in openDocs:
|
|
if openDoc.GetFilename() == filename:
|
|
foundView = openDoc.GetFirstView()
|
|
break
|
|
|
|
if not foundView:
|
|
doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
|
|
if doc:
|
|
foundView = doc.GetFirstView()
|
|
|
|
if foundView:
|
|
foundView.GetFrame().SetFocus()
|
|
foundView.Activate()
|
|
if hasattr(foundView, "GotoLine"):
|
|
foundView.GotoLine(lineNum)
|
|
startPos = foundView.PositionFromLine(lineNum)
|
|
# wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
|
|
# Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
|
|
# time, we don't see the selection, it is scrolled off screen
|
|
foundView.SetSelection(startPos - 1 + len(lineText[lineEnd:].rstrip("\n")), startPos)
|
|
wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
|
|
|
|
|