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 self
.ShowFindAllDialog()
63 elif id == FindInDirService
.FINDDIR_ID
:
64 self
.ShowFindDirDialog()
67 return FindService
.FindService
.ProcessEvent(self
, event
)
70 def ProcessUpdateUIEvent(self
, event
):
72 if id == FindInDirService
.FINDALL_ID
:
73 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
74 view
= projectService
.GetView()
75 if view
and view
.GetDocument() and view
.GetDocument().GetFiles():
80 elif id == FindInDirService
.FINDDIR_ID
:
83 return FindService
.FindService
.ProcessUpdateUIEvent(self
, event
)
86 def ShowFindDirDialog(self
):
87 config
= wx
.ConfigBase_Get()
89 frame
= wx
.Dialog(None, -1, _("Find in Directory"), size
= (320,200))
90 borderSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
92 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
93 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
94 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Directory:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
95 dirCtrl
= wx
.TextCtrl(frame
, -1, config
.Read(FIND_MATCHDIR
, ""), size
=(200,-1))
96 dirCtrl
.SetToolTipString(dirCtrl
.GetValue())
97 lineSizer
.Add(dirCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
98 findDirButton
= wx
.Button(frame
, -1, "Browse...")
99 lineSizer
.Add(findDirButton
, 0, wx
.LEFT
, HALF_SPACE
)
100 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
102 def OnBrowseButton(event
):
103 dlg
= wx
.DirDialog(frame
, _("Choose a directory:"), style
=wx
.DD_DEFAULT_STYLE
)
104 dir = dirCtrl
.GetValue()
107 if dlg
.ShowModal() == wx
.ID_OK
:
108 dirCtrl
.SetValue(dlg
.GetPath())
109 dirCtrl
.SetToolTipString(dirCtrl
.GetValue())
110 dirCtrl
.SetInsertionPointEnd()
113 wx
.EVT_BUTTON(findDirButton
, -1, OnBrowseButton
)
115 subfolderCtrl
= wx
.CheckBox(frame
, -1, _("Search in subfolders"))
116 subfolderCtrl
.SetValue(config
.ReadInt(FIND_MATCHDIRSUBFOLDERS
, True))
117 contentSizer
.Add(subfolderCtrl
, 0, wx
.BOTTOM
, SPACE
)
119 lineSizer
= wx
.BoxSizer(wx
.VERTICAL
) # let the line expand horizontally without vertical expansion
120 lineSizer
.Add(wx
.StaticLine(frame
, -1, size
= (10,-1)), 0, flag
=wx
.EXPAND
)
121 contentSizer
.Add(lineSizer
, flag
=wx
.EXPAND|wx
.ALIGN_CENTER_VERTICAL|wx
.BOTTOM
, border
=HALF_SPACE
)
123 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
124 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Find what:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
125 findCtrl
= wx
.TextCtrl(frame
, -1, config
.Read(FindService
.FIND_MATCHPATTERN
, ""), size
=(200,-1))
126 lineSizer
.Add(findCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
127 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
128 wholeWordCtrl
= wx
.CheckBox(frame
, -1, _("Match whole word only"))
129 wholeWordCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHWHOLEWORD
, False))
130 matchCaseCtrl
= wx
.CheckBox(frame
, -1, _("Match case"))
131 matchCaseCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHCASE
, False))
132 regExprCtrl
= wx
.CheckBox(frame
, -1, _("Regular expression"))
133 regExprCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHREGEXPR
, False))
134 contentSizer
.Add(wholeWordCtrl
, 0, wx
.BOTTOM
, SPACE
)
135 contentSizer
.Add(matchCaseCtrl
, 0, wx
.BOTTOM
, SPACE
)
136 contentSizer
.Add(regExprCtrl
, 0, wx
.BOTTOM
, SPACE
)
137 borderSizer
.Add(contentSizer
, 0, wx
.TOP | wx
.BOTTOM | wx
.LEFT
, SPACE
)
139 buttonSizer
= wx
.BoxSizer(wx
.VERTICAL
)
140 findBtn
= wx
.Button(frame
, wx
.ID_OK
, _("Find"))
142 buttonSizer
.Add(findBtn
, 0, wx
.BOTTOM
, HALF_SPACE
)
143 buttonSizer
.Add(wx
.Button(frame
, wx
.ID_CANCEL
, _("Cancel")), 0)
144 borderSizer
.Add(buttonSizer
, 0, wx
.ALL
, SPACE
)
146 frame
.SetSizer(borderSizer
)
149 status
= frame
.ShowModal()
152 while status
== wx
.ID_OK
and not passedCheck
:
153 if not os
.path
.exists(dirCtrl
.GetValue()):
154 dlg
= wx
.MessageDialog(frame
,
155 _("'%s' does not exist.") % dirCtrl
.GetValue(),
156 _("Find in Directory"),
157 wx
.OK | wx
.ICON_EXCLAMATION
162 status
= frame
.ShowModal()
163 elif len(findCtrl
.GetValue()) == 0:
164 dlg
= wx
.MessageDialog(frame
,
165 _("'Find what:' cannot be empty."),
166 _("Find in Directory"),
167 wx
.OK | wx
.ICON_EXCLAMATION
172 status
= frame
.ShowModal()
177 # save user choice state for this and other Find Dialog Boxes
178 dirString
= dirCtrl
.GetValue()
179 searchSubfolders
= subfolderCtrl
.IsChecked()
180 self
.SaveFindDirConfig(dirString
, searchSubfolders
)
182 findString
= findCtrl
.GetValue()
183 matchCase
= matchCaseCtrl
.IsChecked()
184 wholeWord
= wholeWordCtrl
.IsChecked()
185 regExpr
= regExprCtrl
.IsChecked()
186 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
189 if status
== wx
.ID_OK
:
192 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
193 messageService
.ShowWindow()
195 view
= messageService
.GetView()
197 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
199 view
.SetCallback(self
.OnJumpToFoundLine
)
201 view
.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString
, dirString
))
203 if os
.path
.isfile(dirString
):
205 docFile
= file(dirString
, 'r')
207 needToDisplayFilename
= True
208 line
= docFile
.readline()
210 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
212 if needToDisplayFilename
:
213 view
.AddLines(FILENAME_MARKER
+ dirString
+ "\n")
214 needToDisplayFilename
= False
215 line
= repr(lineNum
).zfill(4) + ":" + line
217 line
= docFile
.readline()
219 if not needToDisplayFilename
:
221 except IOError, (code
, message
):
222 print _("Warning, unable to read file: '%s'. %s") % (dirString
, message
)
224 # do search in files on disk
225 for root
, dirs
, files
in os
.walk(dirString
):
226 if not searchSubfolders
and root
!= dirString
:
230 filename
= os
.path
.join(root
, name
)
232 docFile
= file(filename
, 'r')
233 except IOError, (code
, message
):
234 print _("Warning, unable to read file: '%s'. %s") % (filename
, message
)
238 needToDisplayFilename
= True
239 line
= docFile
.readline()
241 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
243 if needToDisplayFilename
:
244 view
.AddLines(FILENAME_MARKER
+ filename
+ "\n")
245 needToDisplayFilename
= False
246 line
= repr(lineNum
).zfill(4) + ":" + line
248 line
= docFile
.readline()
250 if not needToDisplayFilename
:
253 view
.AddLines(_("Search completed."))
254 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
262 def SaveFindDirConfig(self
, dirString
, searchSubfolders
):
263 """ Save search dir patterns and flags to registry.
265 dirString = search directory
266 searchSubfolders = Search subfolders
268 config
= wx
.ConfigBase_Get()
269 config
.Write(FIND_MATCHDIR
, dirString
)
270 config
.WriteInt(FIND_MATCHDIRSUBFOLDERS
, searchSubfolders
)
273 def ShowFindAllDialog(self
):
274 config
= wx
.ConfigBase_Get()
276 frame
= wx
.Dialog(None, -1, _("Find in Project"), size
= (320,200))
277 borderSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
279 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
280 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
281 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Find what:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
282 findCtrl
= wx
.TextCtrl(frame
, -1, config
.Read(FindService
.FIND_MATCHPATTERN
, ""), size
=(200,-1))
283 lineSizer
.Add(findCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
284 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
285 wholeWordCtrl
= wx
.CheckBox(frame
, -1, _("Match whole word only"))
286 wholeWordCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHWHOLEWORD
, False))
287 matchCaseCtrl
= wx
.CheckBox(frame
, -1, _("Match case"))
288 matchCaseCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHCASE
, False))
289 regExprCtrl
= wx
.CheckBox(frame
, -1, _("Regular expression"))
290 regExprCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHREGEXPR
, False))
291 contentSizer
.Add(wholeWordCtrl
, 0, wx
.BOTTOM
, SPACE
)
292 contentSizer
.Add(matchCaseCtrl
, 0, wx
.BOTTOM
, SPACE
)
293 contentSizer
.Add(regExprCtrl
, 0, wx
.BOTTOM
, SPACE
)
294 borderSizer
.Add(contentSizer
, 0, wx
.TOP | wx
.BOTTOM | wx
.LEFT
, SPACE
)
296 buttonSizer
= wx
.BoxSizer(wx
.VERTICAL
)
297 findBtn
= wx
.Button(frame
, wx
.ID_OK
, _("Find"))
299 buttonSizer
.Add(findBtn
, 0, wx
.BOTTOM
, HALF_SPACE
)
300 buttonSizer
.Add(wx
.Button(frame
, wx
.ID_CANCEL
, _("Cancel")), 0)
301 borderSizer
.Add(buttonSizer
, 0, wx
.ALL
, SPACE
)
303 frame
.SetSizer(borderSizer
)
306 status
= frame
.ShowModal()
308 # save user choice state for this and other Find Dialog Boxes
309 findString
= findCtrl
.GetValue()
310 matchCase
= matchCaseCtrl
.IsChecked()
311 wholeWord
= wholeWordCtrl
.IsChecked()
312 regExpr
= regExprCtrl
.IsChecked()
313 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
315 if status
== wx
.ID_OK
:
318 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
319 messageService
.ShowWindow()
321 view
= messageService
.GetView()
324 view
.SetCallback(self
.OnJumpToFoundLine
)
326 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
327 projectFilenames
= projectService
.GetFilesFromCurrentProject()
329 projView
= projectService
.GetView()
331 projName
= wx
.lib
.docview
.FileNameFromPath(projView
.GetDocument().GetFilename())
332 view
.AddLines(PROJECT_MARKER
+ projName
+ "\n\n")
334 # do search in open files first, open files may have been modified and different from disk because it hasn't been saved
335 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
336 openDocsInProject
= filter(lambda openDoc
: openDoc
.GetFilename() in projectFilenames
, openDocs
)
337 for openDoc
in openDocsInProject
:
338 if isinstance(openDoc
, ProjectEditor
.ProjectDocument
): # don't search project model
341 openDocView
= openDoc
.GetFirstView()
342 # some views don't have a in memory text object to search through such as the PM and the DM
343 # even if they do have a non-text searchable object, how do we display it in the message window?
344 if not hasattr(openDocView
, "GetValue"):
346 text
= openDocView
.GetValue()
349 needToDisplayFilename
= True
354 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, text
, start
, end
, True, matchCase
, wholeWord
, regExpr
)
356 if needToDisplayFilename
:
357 view
.AddLines(FILENAME_MARKER
+ openDoc
.GetFilename() + "\n")
358 needToDisplayFilename
= False
360 lineNum
= openDocView
.LineFromPosition(foundStart
)
361 line
= repr(lineNum
).zfill(4) + ":" + openDocView
.GetLine(lineNum
)
364 start
= text
.find("\n", foundStart
)
368 if not needToDisplayFilename
:
370 openDocNames
= map(lambda openDoc
: openDoc
.GetFilename(), openDocs
)
372 # do search in closed files, skipping the open ones we already searched
373 filenames
= filter(lambda filename
: filename
not in openDocNames
, projectFilenames
)
374 for filename
in filenames
:
376 docFile
= file(filename
, 'r')
377 except IOError, (code
, message
):
378 print _("Warning, unable to read file: '%s'. %s") % (filename
, message
)
382 needToDisplayFilename
= True
383 line
= docFile
.readline()
385 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
387 if needToDisplayFilename
:
388 view
.AddLines(FILENAME_MARKER
+ filename
+ "\n")
389 needToDisplayFilename
= False
390 line
= repr(lineNum
).zfill(4) + ":" + line
392 line
= docFile
.readline()
394 if not needToDisplayFilename
:
397 view
.AddLines(_("Search for '%s' completed.") % findString
)
405 def OnJumpToFoundLine(self
, event
):
406 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
407 lineText
, pos
= messageService
.GetView().GetCurrLine()
408 if lineText
== "\n" or lineText
.find(FILENAME_MARKER
) != -1 or lineText
.find(PROJECT_MARKER
) != -1:
410 lineEnd
= lineText
.find(":")
414 lineNum
= int(lineText
[0:lineEnd
])
416 text
= messageService
.GetView().GetText()
417 curPos
= messageService
.GetView().GetCurrentPos()
419 startPos
= text
.rfind(FILENAME_MARKER
, 0, curPos
)
420 endPos
= text
.find("\n", startPos
)
421 filename
= text
[startPos
+ len(FILENAME_MARKER
):endPos
]
424 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
425 for openDoc
in openDocs
:
426 if openDoc
.GetFilename() == filename
:
427 foundView
= openDoc
.GetFirstView()
431 doc
= wx
.GetApp().GetDocumentManager().CreateDocument(filename
, wx
.lib
.docview
.DOC_SILENT
)
432 foundView
= doc
.GetFirstView()
435 foundView
.GetFrame().SetFocus()
437 if hasattr(foundView
, "GotoLine"):
438 foundView
.GotoLine(lineNum
)
439 startPos
= foundView
.PositionFromLine(lineNum
)
440 # wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
441 # Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
442 # time, we don't see the selection, it is scrolled off screen
443 foundView
.SetSelection(startPos
- 1 + len(lineText
[lineEnd
:].rstrip("\n")), startPos
)
444 wx
.GetApp().GetService(OutlineService
.OutlineService
).LoadOutline(foundView
, position
=startPos
)