1 #----------------------------------------------------------------------------
2 # Name: IDEFindService.py
3 # Purpose: Find Service for pydocview
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
16 from os
.path
import join
25 #----------------------------------------------------------------------------
27 #----------------------------------------------------------------------------
28 FILENAME_MARKER
= _("Found in file: ")
29 PROJECT_MARKER
= _("Searching project: ")
30 FIND_MATCHDIR
= "FindMatchDir"
31 FIND_MATCHDIRSUBFOLDERS
= "FindMatchDirSubfolders"
37 class FindInDirService(FindService
.FindService
):
39 #----------------------------------------------------------------------------
41 #----------------------------------------------------------------------------
42 FINDALL_ID
= wx
.NewId() # for bringing up Find All dialog box
43 FINDDIR_ID
= wx
.NewId() # for bringing up Find Dir dialog box
46 def InstallControls(self
, frame
, menuBar
= None, toolBar
= None, statusBar
= None, document
= None):
47 FindService
.FindService
.InstallControls(self
, frame
, menuBar
, toolBar
, statusBar
, document
)
49 editMenu
= menuBar
.GetMenu(menuBar
.FindMenu(_("&Edit")))
50 wx
.EVT_MENU(frame
, FindInDirService
.FINDALL_ID
, self
.ProcessEvent
)
51 wx
.EVT_UPDATE_UI(frame
, FindInDirService
.FINDALL_ID
, self
.ProcessUpdateUIEvent
)
52 editMenu
.Append(FindInDirService
.FINDALL_ID
, _("Find in Project...\tCtrl+Shift+F"), _("Searches for the specified text in all the files in the project"))
53 wx
.EVT_MENU(frame
, FindInDirService
.FINDDIR_ID
, self
.ProcessEvent
)
54 wx
.EVT_UPDATE_UI(frame
, FindInDirService
.FINDDIR_ID
, self
.ProcessUpdateUIEvent
)
55 editMenu
.Append(FindInDirService
.FINDDIR_ID
, _("Find in Directory..."), _("Searches for the specified text in all the files in the directory"))
58 def ProcessEvent(self
, event
):
60 if id == FindInDirService
.FINDALL_ID
:
61 view
= wx
.GetApp().GetDocumentManager().GetCurrentView()
62 if hasattr(view
, "GetCtrl") and view
.GetCtrl() and hasattr(view
.GetCtrl(), "GetSelectedText"):
63 self
.ShowFindAllDialog(view
.GetCtrl().GetSelectedText())
65 self
.ShowFindAllDialog()
67 elif id == FindInDirService
.FINDDIR_ID
:
68 view
= wx
.GetApp().GetDocumentManager().GetCurrentView()
69 if hasattr(view
, "GetCtrl") and view
.GetCtrl() and hasattr(view
.GetCtrl(), "GetSelectedText"):
70 self
.ShowFindDirDialog(view
.GetCtrl().GetSelectedText())
72 self
.ShowFindDirDialog()
75 return FindService
.FindService
.ProcessEvent(self
, event
)
78 def ProcessUpdateUIEvent(self
, event
):
80 if id == FindInDirService
.FINDALL_ID
:
81 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
82 if projectService
.GetFilesFromCurrentProject():
87 elif id == FindInDirService
.FINDDIR_ID
:
90 return FindService
.FindService
.ProcessUpdateUIEvent(self
, event
)
93 def ShowFindDirDialog(self
, findString
=None):
94 config
= wx
.ConfigBase_Get()
96 frame
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("Find in Directory"), size
= (320,200))
97 borderSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
99 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
100 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
101 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Directory:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
102 dirCtrl
= wx
.TextCtrl(frame
, -1, config
.Read(FIND_MATCHDIR
, ""), size
=(200,-1))
103 dirCtrl
.SetToolTipString(dirCtrl
.GetValue())
104 lineSizer
.Add(dirCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
105 findDirButton
= wx
.Button(frame
, -1, _("Browse..."))
106 lineSizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
)
107 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
109 def OnBrowseButton(event
):
110 dlg
= wx
.DirDialog(frame
, _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
)
111 dir = dirCtrl
.GetValue()
115 if dlg
.ShowModal() == wx
.ID_OK
:
116 dirCtrl
.SetValue(dlg
.GetPath())
117 dirCtrl
.SetToolTipString(dirCtrl
.GetValue())
118 dirCtrl
.SetInsertionPointEnd()
120 wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
)
122 subfolderCtrl
= wx
.CheckBox(frame
, -1, _("Search in subdirectories"))
123 subfolderCtrl
.SetValue(config
.ReadInt(FIND_MATCHDIRSUBFOLDERS
, True))
124 contentSizer
.Add(subfolderCtrl
, 0, wx
.BOTTOM
, SPACE
)
126 lineSizer
= wx
.BoxSizer(wx
.VERTICAL
) # let the line expand horizontally without vertical expansion
127 lineSizer
.Add(wx
.StaticLine(frame
, -1, size
= (10,-1)), 0, flag
=wx
.EXPAND
)
128 contentSizer
.Add(lineSizer
, flag
=wx
.EXPAND|wx
.ALIGN_CENTER_VERTICAL|wx
.BOTTOM
, border
=HALF_SPACE
)
130 if wx
.Platform
== "__WXMAC__":
131 contentSizer
.Add((-1, 10), 0, wx
.EXPAND
)
133 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
134 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Find what:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
136 findString
= config
.Read(FindService
.FIND_MATCHPATTERN
, "")
137 findCtrl
= wx
.TextCtrl(frame
, -1, findString
, size
=(200,-1))
139 findCtrl
.SetSelection(0,-1)
140 lineSizer
.Add(findCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
141 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
142 wholeWordCtrl
= wx
.CheckBox(frame
, -1, _("Match whole word only"))
143 wholeWordCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHWHOLEWORD
, False))
144 matchCaseCtrl
= wx
.CheckBox(frame
, -1, _("Match case"))
145 matchCaseCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHCASE
, False))
146 regExprCtrl
= wx
.CheckBox(frame
, -1, _("Regular expression"))
147 regExprCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHREGEXPR
, False))
148 contentSizer
.Add(wholeWordCtrl
, 0, wx
.BOTTOM
, SPACE
)
149 contentSizer
.Add(matchCaseCtrl
, 0, wx
.BOTTOM
, SPACE
)
150 contentSizer
.Add(regExprCtrl
, 0, wx
.BOTTOM
, SPACE
)
151 borderSizer
.Add(contentSizer
, 0, wx
.TOP | wx
.BOTTOM | wx
.LEFT
, SPACE
)
153 buttonSizer
= wx
.BoxSizer(wx
.VERTICAL
)
154 findBtn
= wx
.Button(frame
, wx
.ID_OK
, _("Find"))
156 BTM_SPACE
= HALF_SPACE
157 if wx
.Platform
== "__WXMAC__":
159 buttonSizer
.Add(findBtn
, 0, wx
.BOTTOM
, BTM_SPACE
)
160 buttonSizer
.Add(wx
.Button(frame
, wx
.ID_CANCEL
), 0)
161 borderSizer
.Add(buttonSizer
, 0, wx
.ALL
, SPACE
)
163 frame
.SetSizer(borderSizer
)
166 frame
.CenterOnParent()
167 status
= frame
.ShowModal()
170 while status
== wx
.ID_OK
and not passedCheck
:
171 if not os
.path
.exists(dirCtrl
.GetValue()):
172 dlg
= wx
.MessageDialog(frame
,
173 _("'%s' does not exist.") % dirCtrl
.GetValue(),
174 _("Find in Directory"),
175 wx
.OK | wx
.ICON_EXCLAMATION
181 status
= frame
.ShowModal()
182 elif len(findCtrl
.GetValue()) == 0:
183 dlg
= wx
.MessageDialog(frame
,
184 _("'Find what:' cannot be empty."),
185 _("Find in Directory"),
186 wx
.OK | wx
.ICON_EXCLAMATION
192 status
= frame
.ShowModal()
197 # save user choice state for this and other Find Dialog Boxes
198 dirString
= dirCtrl
.GetValue()
199 searchSubfolders
= subfolderCtrl
.IsChecked()
200 self
.SaveFindDirConfig(dirString
, searchSubfolders
)
202 findString
= findCtrl
.GetValue()
203 matchCase
= matchCaseCtrl
.IsChecked()
204 wholeWord
= wholeWordCtrl
.IsChecked()
205 regExpr
= regExprCtrl
.IsChecked()
206 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
209 if status
== wx
.ID_OK
:
210 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
211 messageService
.ShowWindow()
213 view
= messageService
.GetView()
215 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
217 view
.SetCallback(self
.OnJumpToFoundLine
)
219 view
.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString
, dirString
))
221 if os
.path
.isfile(dirString
):
223 docFile
= file(dirString
, 'r')
225 needToDisplayFilename
= True
226 line
= docFile
.readline()
228 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
230 if needToDisplayFilename
:
231 view
.AddLines(FILENAME_MARKER
+ dirString
+ "\n")
232 needToDisplayFilename
= False
233 line
= repr(lineNum
).zfill(4) + ":" + line
235 line
= docFile
.readline()
237 if not needToDisplayFilename
:
239 except IOError, (code
, message
):
240 print _("Warning, unable to read file: '%s'. %s") % (dirString
, message
)
242 # do search in files on disk
243 for root
, dirs
, files
in os
.walk(dirString
):
244 if not searchSubfolders
and root
!= dirString
:
248 filename
= os
.path
.join(root
, name
)
250 docFile
= file(filename
, 'r')
251 except IOError, (code
, message
):
252 print _("Warning, unable to read file: '%s'. %s") % (filename
, message
)
256 needToDisplayFilename
= True
257 line
= docFile
.readline()
259 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
261 if needToDisplayFilename
:
262 view
.AddLines(FILENAME_MARKER
+ filename
+ "\n")
263 needToDisplayFilename
= False
264 line
= repr(lineNum
).zfill(4) + ":" + line
266 line
= docFile
.readline()
268 if not needToDisplayFilename
:
271 view
.AddLines(_("Search completed."))
272 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
279 def SaveFindDirConfig(self
, dirString
, searchSubfolders
):
280 """ Save search dir patterns and flags to registry.
282 dirString = search directory
283 searchSubfolders = Search subfolders
285 config
= wx
.ConfigBase_Get()
286 config
.Write(FIND_MATCHDIR
, dirString
)
287 config
.WriteInt(FIND_MATCHDIRSUBFOLDERS
, searchSubfolders
)
290 def ShowFindAllDialog(self
, findString
=None):
291 config
= wx
.ConfigBase_Get()
293 frame
= wx
.Dialog(wx
.GetApp().GetTopWindow(), -1, _("Find in Project"), size
= (320,200))
294 borderSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
296 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
297 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
298 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Find what:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
300 findString
= config
.Read(FindService
.FIND_MATCHPATTERN
, "")
301 findCtrl
= wx
.TextCtrl(frame
, -1, findString
, size
=(200,-1))
302 lineSizer
.Add(findCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
303 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
304 wholeWordCtrl
= wx
.CheckBox(frame
, -1, _("Match whole word only"))
305 wholeWordCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHWHOLEWORD
, False))
306 matchCaseCtrl
= wx
.CheckBox(frame
, -1, _("Match case"))
307 matchCaseCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHCASE
, False))
308 regExprCtrl
= wx
.CheckBox(frame
, -1, _("Regular expression"))
309 regExprCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHREGEXPR
, False))
310 contentSizer
.Add(wholeWordCtrl
, 0, wx
.BOTTOM
, SPACE
)
311 contentSizer
.Add(matchCaseCtrl
, 0, wx
.BOTTOM
, SPACE
)
312 contentSizer
.Add(regExprCtrl
, 0, wx
.BOTTOM
, SPACE
)
313 borderSizer
.Add(contentSizer
, 0, wx
.TOP | wx
.BOTTOM | wx
.LEFT
, SPACE
)
315 buttonSizer
= wx
.BoxSizer(wx
.VERTICAL
)
316 findBtn
= wx
.Button(frame
, wx
.ID_OK
, _("Find"))
318 BTM_SPACE
= HALF_SPACE
319 if wx
.Platform
== "__WXMAC__":
321 buttonSizer
.Add(findBtn
, 0, wx
.BOTTOM
, BTM_SPACE
)
322 buttonSizer
.Add(wx
.Button(frame
, wx
.ID_CANCEL
), 0)
323 borderSizer
.Add(buttonSizer
, 0, wx
.ALL
, SPACE
)
325 frame
.SetSizer(borderSizer
)
328 frame
.CenterOnParent()
329 status
= frame
.ShowModal()
331 # save user choice state for this and other Find Dialog Boxes
332 findString
= findCtrl
.GetValue()
333 matchCase
= matchCaseCtrl
.IsChecked()
334 wholeWord
= wholeWordCtrl
.IsChecked()
335 regExpr
= regExprCtrl
.IsChecked()
336 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
339 if status
== wx
.ID_OK
:
340 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
341 messageService
.ShowWindow()
343 view
= messageService
.GetView()
346 view
.SetCallback(self
.OnJumpToFoundLine
)
348 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
349 projectFilenames
= projectService
.GetFilesFromCurrentProject()
351 projView
= projectService
.GetView()
353 projName
= wx
.lib
.docview
.FileNameFromPath(projView
.GetDocument().GetFilename())
354 view
.AddLines(PROJECT_MARKER
+ projName
+ "\n\n")
356 # do search in open files first, open files may have been modified and different from disk because it hasn't been saved
357 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
358 openDocsInProject
= filter(lambda openDoc
: openDoc
.GetFilename() in projectFilenames
, openDocs
)
359 for openDoc
in openDocsInProject
:
360 if isinstance(openDoc
, ProjectEditor
.ProjectDocument
): # don't search project model
363 openDocView
= openDoc
.GetFirstView()
364 # some views don't have a in memory text object to search through such as the PM and the DM
365 # even if they do have a non-text searchable object, how do we display it in the message window?
366 if not hasattr(openDocView
, "GetValue"):
368 text
= openDocView
.GetValue()
371 needToDisplayFilename
= True
376 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, text
, start
, end
, True, matchCase
, wholeWord
, regExpr
)
378 if needToDisplayFilename
:
379 view
.AddLines(FILENAME_MARKER
+ openDoc
.GetFilename() + "\n")
380 needToDisplayFilename
= False
382 lineNum
= openDocView
.LineFromPosition(foundStart
)
383 line
= repr(lineNum
).zfill(4) + ":" + openDocView
.GetLine(lineNum
)
386 start
= text
.find("\n", foundStart
)
390 if not needToDisplayFilename
:
392 openDocNames
= map(lambda openDoc
: openDoc
.GetFilename(), openDocs
)
394 # do search in closed files, skipping the open ones we already searched
395 filenames
= filter(lambda filename
: filename
not in openDocNames
, projectFilenames
)
396 for filename
in filenames
:
398 docFile
= file(filename
, 'r')
399 except IOError, (code
, message
):
400 print _("Warning, unable to read file: '%s'. %s") % (filename
, message
)
404 needToDisplayFilename
= True
405 line
= docFile
.readline()
407 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
409 if needToDisplayFilename
:
410 view
.AddLines(FILENAME_MARKER
+ filename
+ "\n")
411 needToDisplayFilename
= False
412 line
= repr(lineNum
).zfill(4) + ":" + line
414 line
= docFile
.readline()
416 if not needToDisplayFilename
:
419 view
.AddLines(_("Search for '%s' completed.") % findString
)
426 def OnJumpToFoundLine(self
, event
):
427 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
428 lineText
, pos
= messageService
.GetView().GetCurrLine()
429 if lineText
== "\n" or lineText
.find(FILENAME_MARKER
) != -1 or lineText
.find(PROJECT_MARKER
) != -1:
431 lineEnd
= lineText
.find(":")
435 lineNum
= int(lineText
[0:lineEnd
])
437 text
= messageService
.GetView().GetText()
438 curPos
= messageService
.GetView().GetCurrentPos()
440 startPos
= text
.rfind(FILENAME_MARKER
, 0, curPos
)
441 endPos
= text
.find("\n", startPos
)
442 filename
= text
[startPos
+ len(FILENAME_MARKER
):endPos
]
445 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
446 for openDoc
in openDocs
:
447 if openDoc
.GetFilename() == filename
:
448 foundView
= openDoc
.GetFirstView()
452 doc
= wx
.GetApp().GetDocumentManager().CreateDocument(filename
, wx
.lib
.docview
.DOC_SILENT|wx
.lib
.docview
.DOC_OPEN_ONCE
)
454 foundView
= doc
.GetFirstView()
457 foundView
.GetFrame().SetFocus()
459 if hasattr(foundView
, "GotoLine"):
460 foundView
.GotoLine(lineNum
)
461 startPos
= foundView
.PositionFromLine(lineNum
)
462 # wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
463 # Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
464 # time, we don't see the selection, it is scrolled off screen
465 foundView
.SetSelection(startPos
- 1 + len(lineText
[lineEnd
:].rstrip("\n")), startPos
)
466 wx
.GetApp().GetService(OutlineService
.OutlineService
).LoadOutline(foundView
, position
=startPos
)