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 view
= projectService
.GetView()
83 if view
and view
.GetDocument() and view
.GetDocument().GetFiles():
88 elif id == FindInDirService
.FINDDIR_ID
:
91 return FindService
.FindService
.ProcessUpdateUIEvent(self
, event
)
94 def ShowFindDirDialog(self
, findString
=None):
95 config
= wx
.ConfigBase_Get()
97 frame
= wx
.Dialog(None, -1, _("Find in Directory"), size
= (320,200))
98 borderSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
100 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
101 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
102 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Directory:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
103 dirCtrl
= wx
.TextCtrl(frame
, -1, config
.Read(FIND_MATCHDIR
, ""), size
=(200,-1))
104 dirCtrl
.SetToolTipString(dirCtrl
.GetValue())
105 lineSizer
.Add(dirCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
106 findDirButton
= wx
.Button(frame
, -1, _("Browse..."))
107 lineSizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
)
108 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
110 def OnBrowseButton(event
):
111 dlg
= wx
.DirDialog(frame
, _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
)
112 dir = dirCtrl
.GetValue()
115 if dlg
.ShowModal() == wx
.ID_OK
:
116 dirCtrl
.SetValue(dlg
.GetPath())
117 dirCtrl
.SetToolTipString(dirCtrl
.GetValue())
118 dirCtrl
.SetInsertionPointEnd()
121 wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
)
123 subfolderCtrl
= wx
.CheckBox(frame
, -1, _("Search in subdirectories"))
124 subfolderCtrl
.SetValue(config
.ReadInt(FIND_MATCHDIRSUBFOLDERS
, True))
125 contentSizer
.Add(subfolderCtrl
, 0, wx
.BOTTOM
, SPACE
)
127 lineSizer
= wx
.BoxSizer(wx
.VERTICAL
) # let the line expand horizontally without vertical expansion
128 lineSizer
.Add(wx
.StaticLine(frame
, -1, size
= (10,-1)), 0, flag
=wx
.EXPAND
)
129 contentSizer
.Add(lineSizer
, flag
=wx
.EXPAND|wx
.ALIGN_CENTER_VERTICAL|wx
.BOTTOM
, border
=HALF_SPACE
)
131 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
132 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Find what:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
134 findString
= config
.Read(FindService
.FIND_MATCHPATTERN
, "")
135 findCtrl
= wx
.TextCtrl(frame
, -1, findString
, size
=(200,-1))
137 findCtrl
.SetSelection(0,-1)
138 lineSizer
.Add(findCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
139 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
140 wholeWordCtrl
= wx
.CheckBox(frame
, -1, _("Match whole word only"))
141 wholeWordCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHWHOLEWORD
, False))
142 matchCaseCtrl
= wx
.CheckBox(frame
, -1, _("Match case"))
143 matchCaseCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHCASE
, False))
144 regExprCtrl
= wx
.CheckBox(frame
, -1, _("Regular expression"))
145 regExprCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHREGEXPR
, False))
146 contentSizer
.Add(wholeWordCtrl
, 0, wx
.BOTTOM
, SPACE
)
147 contentSizer
.Add(matchCaseCtrl
, 0, wx
.BOTTOM
, SPACE
)
148 contentSizer
.Add(regExprCtrl
, 0, wx
.BOTTOM
, SPACE
)
149 borderSizer
.Add(contentSizer
, 0, wx
.TOP | wx
.BOTTOM | wx
.LEFT
, SPACE
)
151 buttonSizer
= wx
.BoxSizer(wx
.VERTICAL
)
152 findBtn
= wx
.Button(frame
, wx
.ID_OK
, _("Find"))
154 buttonSizer
.Add(findBtn
, 0, wx
.BOTTOM
, HALF_SPACE
)
155 buttonSizer
.Add(wx
.Button(frame
, wx
.ID_CANCEL
), 0)
156 borderSizer
.Add(buttonSizer
, 0, wx
.ALL
, SPACE
)
158 frame
.SetSizer(borderSizer
)
161 status
= frame
.ShowModal()
164 while status
== wx
.ID_OK
and not passedCheck
:
165 if not os
.path
.exists(dirCtrl
.GetValue()):
166 dlg
= wx
.MessageDialog(frame
,
167 _("'%s' does not exist.") % dirCtrl
.GetValue(),
168 _("Find in Directory"),
169 wx
.OK | wx
.ICON_EXCLAMATION
174 status
= frame
.ShowModal()
175 elif len(findCtrl
.GetValue()) == 0:
176 dlg
= wx
.MessageDialog(frame
,
177 _("'Find what:' cannot be empty."),
178 _("Find in Directory"),
179 wx
.OK | wx
.ICON_EXCLAMATION
184 status
= frame
.ShowModal()
189 # save user choice state for this and other Find Dialog Boxes
190 dirString
= dirCtrl
.GetValue()
191 searchSubfolders
= subfolderCtrl
.IsChecked()
192 self
.SaveFindDirConfig(dirString
, searchSubfolders
)
194 findString
= findCtrl
.GetValue()
195 matchCase
= matchCaseCtrl
.IsChecked()
196 wholeWord
= wholeWordCtrl
.IsChecked()
197 regExpr
= regExprCtrl
.IsChecked()
198 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
201 if status
== wx
.ID_OK
:
204 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
205 messageService
.ShowWindow()
207 view
= messageService
.GetView()
209 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
211 view
.SetCallback(self
.OnJumpToFoundLine
)
213 view
.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString
, dirString
))
215 if os
.path
.isfile(dirString
):
217 docFile
= file(dirString
, 'r')
219 needToDisplayFilename
= True
220 line
= docFile
.readline()
222 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
224 if needToDisplayFilename
:
225 view
.AddLines(FILENAME_MARKER
+ dirString
+ "\n")
226 needToDisplayFilename
= False
227 line
= repr(lineNum
).zfill(4) + ":" + line
229 line
= docFile
.readline()
231 if not needToDisplayFilename
:
233 except IOError, (code
, message
):
234 print _("Warning, unable to read file: '%s'. %s") % (dirString
, message
)
236 # do search in files on disk
237 for root
, dirs
, files
in os
.walk(dirString
):
238 if not searchSubfolders
and root
!= dirString
:
242 filename
= os
.path
.join(root
, name
)
244 docFile
= file(filename
, 'r')
245 except IOError, (code
, message
):
246 print _("Warning, unable to read file: '%s'. %s") % (filename
, message
)
250 needToDisplayFilename
= True
251 line
= docFile
.readline()
253 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
255 if needToDisplayFilename
:
256 view
.AddLines(FILENAME_MARKER
+ filename
+ "\n")
257 needToDisplayFilename
= False
258 line
= repr(lineNum
).zfill(4) + ":" + line
260 line
= docFile
.readline()
262 if not needToDisplayFilename
:
265 view
.AddLines(_("Search completed."))
266 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
274 def SaveFindDirConfig(self
, dirString
, searchSubfolders
):
275 """ Save search dir patterns and flags to registry.
277 dirString = search directory
278 searchSubfolders = Search subfolders
280 config
= wx
.ConfigBase_Get()
281 config
.Write(FIND_MATCHDIR
, dirString
)
282 config
.WriteInt(FIND_MATCHDIRSUBFOLDERS
, searchSubfolders
)
285 def ShowFindAllDialog(self
, findString
=None):
286 config
= wx
.ConfigBase_Get()
288 frame
= wx
.Dialog(None, -1, _("Find in Project"), size
= (320,200))
289 borderSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
291 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
292 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
293 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Find what:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
295 findString
= config
.Read(FindService
.FIND_MATCHPATTERN
, "")
296 findCtrl
= wx
.TextCtrl(frame
, -1, findString
, size
=(200,-1))
297 lineSizer
.Add(findCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
298 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
299 wholeWordCtrl
= wx
.CheckBox(frame
, -1, _("Match whole word only"))
300 wholeWordCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHWHOLEWORD
, False))
301 matchCaseCtrl
= wx
.CheckBox(frame
, -1, _("Match case"))
302 matchCaseCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHCASE
, False))
303 regExprCtrl
= wx
.CheckBox(frame
, -1, _("Regular expression"))
304 regExprCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHREGEXPR
, False))
305 contentSizer
.Add(wholeWordCtrl
, 0, wx
.BOTTOM
, SPACE
)
306 contentSizer
.Add(matchCaseCtrl
, 0, wx
.BOTTOM
, SPACE
)
307 contentSizer
.Add(regExprCtrl
, 0, wx
.BOTTOM
, SPACE
)
308 borderSizer
.Add(contentSizer
, 0, wx
.TOP | wx
.BOTTOM | wx
.LEFT
, SPACE
)
310 buttonSizer
= wx
.BoxSizer(wx
.VERTICAL
)
311 findBtn
= wx
.Button(frame
, wx
.ID_OK
, _("Find"))
313 buttonSizer
.Add(findBtn
, 0, wx
.BOTTOM
, HALF_SPACE
)
314 buttonSizer
.Add(wx
.Button(frame
, wx
.ID_CANCEL
), 0)
315 borderSizer
.Add(buttonSizer
, 0, wx
.ALL
, SPACE
)
317 frame
.SetSizer(borderSizer
)
320 status
= frame
.ShowModal()
322 # save user choice state for this and other Find Dialog Boxes
323 findString
= findCtrl
.GetValue()
324 matchCase
= matchCaseCtrl
.IsChecked()
325 wholeWord
= wholeWordCtrl
.IsChecked()
326 regExpr
= regExprCtrl
.IsChecked()
327 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
329 if status
== wx
.ID_OK
:
332 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
333 messageService
.ShowWindow()
335 view
= messageService
.GetView()
338 view
.SetCallback(self
.OnJumpToFoundLine
)
340 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
341 projectFilenames
= projectService
.GetFilesFromCurrentProject()
343 projView
= projectService
.GetView()
345 projName
= wx
.lib
.docview
.FileNameFromPath(projView
.GetDocument().GetFilename())
346 view
.AddLines(PROJECT_MARKER
+ projName
+ "\n\n")
348 # do search in open files first, open files may have been modified and different from disk because it hasn't been saved
349 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
350 openDocsInProject
= filter(lambda openDoc
: openDoc
.GetFilename() in projectFilenames
, openDocs
)
351 for openDoc
in openDocsInProject
:
352 if isinstance(openDoc
, ProjectEditor
.ProjectDocument
): # don't search project model
355 openDocView
= openDoc
.GetFirstView()
356 # some views don't have a in memory text object to search through such as the PM and the DM
357 # even if they do have a non-text searchable object, how do we display it in the message window?
358 if not hasattr(openDocView
, "GetValue"):
360 text
= openDocView
.GetValue()
363 needToDisplayFilename
= True
368 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, text
, start
, end
, True, matchCase
, wholeWord
, regExpr
)
370 if needToDisplayFilename
:
371 view
.AddLines(FILENAME_MARKER
+ openDoc
.GetFilename() + "\n")
372 needToDisplayFilename
= False
374 lineNum
= openDocView
.LineFromPosition(foundStart
)
375 line
= repr(lineNum
).zfill(4) + ":" + openDocView
.GetLine(lineNum
)
378 start
= text
.find("\n", foundStart
)
382 if not needToDisplayFilename
:
384 openDocNames
= map(lambda openDoc
: openDoc
.GetFilename(), openDocs
)
386 # do search in closed files, skipping the open ones we already searched
387 filenames
= filter(lambda filename
: filename
not in openDocNames
, projectFilenames
)
388 for filename
in filenames
:
390 docFile
= file(filename
, 'r')
391 except IOError, (code
, message
):
392 print _("Warning, unable to read file: '%s'. %s") % (filename
, message
)
396 needToDisplayFilename
= True
397 line
= docFile
.readline()
399 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
401 if needToDisplayFilename
:
402 view
.AddLines(FILENAME_MARKER
+ filename
+ "\n")
403 needToDisplayFilename
= False
404 line
= repr(lineNum
).zfill(4) + ":" + line
406 line
= docFile
.readline()
408 if not needToDisplayFilename
:
411 view
.AddLines(_("Search for '%s' completed.") % findString
)
419 def OnJumpToFoundLine(self
, event
):
420 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
421 lineText
, pos
= messageService
.GetView().GetCurrLine()
422 if lineText
== "\n" or lineText
.find(FILENAME_MARKER
) != -1 or lineText
.find(PROJECT_MARKER
) != -1:
424 lineEnd
= lineText
.find(":")
428 lineNum
= int(lineText
[0:lineEnd
])
430 text
= messageService
.GetView().GetText()
431 curPos
= messageService
.GetView().GetCurrentPos()
433 startPos
= text
.rfind(FILENAME_MARKER
, 0, curPos
)
434 endPos
= text
.find("\n", startPos
)
435 filename
= text
[startPos
+ len(FILENAME_MARKER
):endPos
]
438 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
439 for openDoc
in openDocs
:
440 if openDoc
.GetFilename() == filename
:
441 foundView
= openDoc
.GetFirstView()
445 doc
= wx
.GetApp().GetDocumentManager().CreateDocument(filename
, wx
.lib
.docview
.DOC_SILENT
)
446 foundView
= doc
.GetFirstView()
449 foundView
.GetFrame().SetFocus()
451 if hasattr(foundView
, "GotoLine"):
452 foundView
.GotoLine(lineNum
)
453 startPos
= foundView
.PositionFromLine(lineNum
)
454 # wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
455 # Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
456 # time, we don't see the selection, it is scrolled off screen
457 foundView
.SetSelection(startPos
- 1 + len(lineText
[lineEnd
:].rstrip("\n")), startPos
)
458 wx
.GetApp().GetService(OutlineService
.OutlineService
).LoadOutline(foundView
, position
=startPos
)