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 IDEFindService(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
, IDEFindService
.FINDALL_ID
, self
.ProcessEvent
)
51 wx
.EVT_UPDATE_UI(frame
, IDEFindService
.FINDALL_ID
, self
.ProcessUpdateUIEvent
)
52 editMenu
.Append(IDEFindService
.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
, IDEFindService
.FINDDIR_ID
, self
.ProcessEvent
)
54 wx
.EVT_UPDATE_UI(frame
, IDEFindService
.FINDDIR_ID
, self
.ProcessUpdateUIEvent
)
55 editMenu
.Append(IDEFindService
.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 == IDEFindService
.FINDALL_ID
:
61 self
.ShowFindAllDialog()
63 elif id == IDEFindService
.FINDDIR_ID
:
64 self
.ShowFindDirDialog()
67 return FindService
.FindService
.ProcessEvent(self
, event
)
70 def ProcessUpdateUIEvent(self
, event
):
72 if id == IDEFindService
.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 == IDEFindService
.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()
151 # save user choice state for this and other Find Dialog Boxes
152 dirString
= dirCtrl
.GetValue()
153 searchSubfolders
= subfolderCtrl
.IsChecked()
154 self
.SaveFindDirConfig(dirString
, searchSubfolders
)
156 findString
= findCtrl
.GetValue()
157 matchCase
= matchCaseCtrl
.IsChecked()
158 wholeWord
= wholeWordCtrl
.IsChecked()
159 regExpr
= regExprCtrl
.IsChecked()
160 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
162 while not os
.path
.exists(dirString
):
163 dlg
= wx
.MessageDialog(frame
,
164 _("'%s' does not exist.") % dirString
,
165 _("Find in Directory"),
166 wx
.OK | wx
.ICON_EXCLAMATION
171 status
= frame
.ShowModal()
173 # save user choice state for this and other Find Dialog Boxes
174 dirString
= dirCtrl
.GetValue()
175 searchSubfolders
= subfolderCtrl
.IsChecked()
176 self
.SaveFindDirConfig(dirString
, searchSubfolders
)
178 findString
= findCtrl
.GetValue()
179 matchCase
= matchCaseCtrl
.IsChecked()
180 wholeWord
= wholeWordCtrl
.IsChecked()
181 regExpr
= regExprCtrl
.IsChecked()
182 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
184 if status
== wx
.ID_CANCEL
:
188 if status
== wx
.ID_OK
:
191 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
192 messageService
.ShowWindow()
194 view
= messageService
.GetView()
196 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_WAIT
))
198 view
.SetCallback(self
.OnJumpToFoundLine
)
200 view
.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString
, dirString
))
202 if os
.path
.isfile(dirString
):
204 docFile
= file(dirString
, 'r')
206 needToDisplayFilename
= True
207 line
= docFile
.readline()
209 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
211 if needToDisplayFilename
:
212 view
.AddLines(FILENAME_MARKER
+ dirString
+ "\n")
213 needToDisplayFilename
= False
214 line
= repr(lineNum
).zfill(4) + ":" + line
216 line
= docFile
.readline()
218 if not needToDisplayFilename
:
220 except IOError, (code
, message
):
221 print _("Warning, unable to read file: '%s'. %s") % (dirString
, message
)
223 # do search in files on disk
224 for root
, dirs
, files
in os
.walk(dirString
):
225 if not searchSubfolders
and root
!= dirString
:
229 filename
= os
.path
.join(root
, name
)
231 docFile
= file(filename
, 'r')
232 except IOError, (code
, message
):
233 print _("Warning, unable to read file: '%s'. %s") % (filename
, message
)
237 needToDisplayFilename
= True
238 line
= docFile
.readline()
240 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
242 if needToDisplayFilename
:
243 view
.AddLines(FILENAME_MARKER
+ filename
+ "\n")
244 needToDisplayFilename
= False
245 line
= repr(lineNum
).zfill(4) + ":" + line
247 line
= docFile
.readline()
249 if not needToDisplayFilename
:
252 view
.AddLines(_("Search completed."))
253 wx
.GetApp().GetTopWindow().SetCursor(wx
.StockCursor(wx
.CURSOR_DEFAULT
))
261 def SaveFindDirConfig(self
, dirString
, searchSubfolders
):
262 """ Save search dir patterns and flags to registry.
264 dirString = search directory
265 searchSubfolders = Search subfolders
267 config
= wx
.ConfigBase_Get()
268 config
.Write(FIND_MATCHDIR
, dirString
)
269 config
.WriteInt(FIND_MATCHDIRSUBFOLDERS
, searchSubfolders
)
272 def ShowFindAllDialog(self
):
273 config
= wx
.ConfigBase_Get()
275 frame
= wx
.Dialog(None, -1, _("Find in Project"), size
= (320,200))
276 borderSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
278 contentSizer
= wx
.BoxSizer(wx
.VERTICAL
)
279 lineSizer
= wx
.BoxSizer(wx
.HORIZONTAL
)
280 lineSizer
.Add(wx
.StaticText(frame
, -1, _("Find what:")), 0, wx
.ALIGN_CENTER | wx
.RIGHT
, HALF_SPACE
)
281 findCtrl
= wx
.TextCtrl(frame
, -1, config
.Read(FindService
.FIND_MATCHPATTERN
, ""), size
=(200,-1))
282 lineSizer
.Add(findCtrl
, 0, wx
.LEFT
, HALF_SPACE
)
283 contentSizer
.Add(lineSizer
, 0, wx
.BOTTOM
, SPACE
)
284 wholeWordCtrl
= wx
.CheckBox(frame
, -1, _("Match whole word only"))
285 wholeWordCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHWHOLEWORD
, False))
286 matchCaseCtrl
= wx
.CheckBox(frame
, -1, _("Match case"))
287 matchCaseCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHCASE
, False))
288 regExprCtrl
= wx
.CheckBox(frame
, -1, _("Regular expression"))
289 regExprCtrl
.SetValue(config
.ReadInt(FindService
.FIND_MATCHREGEXPR
, False))
290 contentSizer
.Add(wholeWordCtrl
, 0, wx
.BOTTOM
, SPACE
)
291 contentSizer
.Add(matchCaseCtrl
, 0, wx
.BOTTOM
, SPACE
)
292 contentSizer
.Add(regExprCtrl
, 0, wx
.BOTTOM
, SPACE
)
293 borderSizer
.Add(contentSizer
, 0, wx
.TOP | wx
.BOTTOM | wx
.LEFT
, SPACE
)
295 buttonSizer
= wx
.BoxSizer(wx
.VERTICAL
)
296 findBtn
= wx
.Button(frame
, wx
.ID_OK
, _("Find"))
298 buttonSizer
.Add(findBtn
, 0, wx
.BOTTOM
, HALF_SPACE
)
299 buttonSizer
.Add(wx
.Button(frame
, wx
.ID_CANCEL
, _("Cancel")), 0)
300 borderSizer
.Add(buttonSizer
, 0, wx
.ALL
, SPACE
)
302 frame
.SetSizer(borderSizer
)
305 status
= frame
.ShowModal()
307 # save user choice state for this and other Find Dialog Boxes
308 findString
= findCtrl
.GetValue()
309 matchCase
= matchCaseCtrl
.IsChecked()
310 wholeWord
= wholeWordCtrl
.IsChecked()
311 regExpr
= regExprCtrl
.IsChecked()
312 self
.SaveFindConfig(findString
, wholeWord
, matchCase
, regExpr
)
314 if status
== wx
.ID_OK
:
317 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
318 messageService
.ShowWindow()
320 view
= messageService
.GetView()
323 view
.SetCallback(self
.OnJumpToFoundLine
)
325 projectService
= wx
.GetApp().GetService(ProjectEditor
.ProjectService
)
326 projectFilenames
= projectService
.GetFilesFromCurrentProject()
328 projView
= projectService
.GetView()
330 projName
= wx
.lib
.docview
.FileNameFromPath(projView
.GetDocument().GetFilename())
331 view
.AddLines(PROJECT_MARKER
+ projName
+ "\n\n")
333 # do search in open files first, open files may have been modified and different from disk because it hasn't been saved
334 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
335 openDocsInProject
= filter(lambda openDoc
: openDoc
.GetFilename() in projectFilenames
, openDocs
)
336 for openDoc
in openDocsInProject
:
337 if isinstance(openDoc
, ProjectEditor
.ProjectDocument
): # don't search project model
340 openDocView
= openDoc
.GetFirstView()
341 # some views don't have a in memory text object to search through such as the PM and the DM
342 # even if they do have a non-text searchable object, how do we display it in the message window?
343 if not hasattr(openDocView
, "GetValue"):
345 text
= openDocView
.GetValue()
348 needToDisplayFilename
= True
353 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, text
, start
, end
, True, matchCase
, wholeWord
, regExpr
)
355 if needToDisplayFilename
:
356 view
.AddLines(FILENAME_MARKER
+ openDoc
.GetFilename() + "\n")
357 needToDisplayFilename
= False
359 lineNum
= openDocView
.LineFromPosition(foundStart
)
360 line
= repr(lineNum
).zfill(4) + ":" + openDocView
.GetLine(lineNum
)
363 start
= text
.find("\n", foundStart
)
367 if not needToDisplayFilename
:
369 openDocNames
= map(lambda openDoc
: openDoc
.GetFilename(), openDocs
)
371 # do search in closed files, skipping the open ones we already searched
372 filenames
= filter(lambda filename
: filename
not in openDocNames
, projectFilenames
)
373 for filename
in filenames
:
375 docFile
= file(filename
, 'r')
376 except IOError, (code
, message
):
377 print _("Warning, unable to read file: '%s'. %s") % (filename
, message
)
381 needToDisplayFilename
= True
382 line
= docFile
.readline()
384 count
, foundStart
, foundEnd
, newText
= self
.DoFind(findString
, None, line
, 0, 0, True, matchCase
, wholeWord
, regExpr
)
386 if needToDisplayFilename
:
387 view
.AddLines(FILENAME_MARKER
+ filename
+ "\n")
388 needToDisplayFilename
= False
389 line
= repr(lineNum
).zfill(4) + ":" + line
391 line
= docFile
.readline()
393 if not needToDisplayFilename
:
396 view
.AddLines(_("Search for '%s' completed.") % findString
)
404 def OnJumpToFoundLine(self
, event
):
405 messageService
= wx
.GetApp().GetService(MessageService
.MessageService
)
406 lineText
, pos
= messageService
.GetView().GetCurrLine()
407 if lineText
== "\n" or lineText
.find(FILENAME_MARKER
) != -1 or lineText
.find(PROJECT_MARKER
) != -1:
409 lineEnd
= lineText
.find(":")
413 lineNum
= int(lineText
[0:lineEnd
])
415 text
= messageService
.GetView().GetText()
416 curPos
= messageService
.GetView().GetCurrentPos()
418 startPos
= text
.rfind(FILENAME_MARKER
, 0, curPos
)
419 endPos
= text
.find("\n", startPos
)
420 filename
= text
[startPos
+ len(FILENAME_MARKER
):endPos
]
423 openDocs
= wx
.GetApp().GetDocumentManager().GetDocuments()
424 for openDoc
in openDocs
:
425 if openDoc
.GetFilename() == filename
:
426 foundView
= openDoc
.GetFirstView()
430 doc
= wx
.GetApp().GetDocumentManager().CreateDocument(filename
, wx
.lib
.docview
.DOC_SILENT
)
431 foundView
= doc
.GetFirstView()
434 foundView
.GetFrame().SetFocus()
436 if hasattr(foundView
, "GotoLine"):
437 foundView
.GotoLine(lineNum
)
438 startPos
= foundView
.PositionFromLine(lineNum
)
439 # wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
440 # Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
441 # time, we don't see the selection, it is scrolled off screen
442 foundView
.SetSelection(startPos
- 1 + len(lineText
[lineEnd
:].rstrip("\n")), startPos
)
443 wx
.GetApp().GetService(OutlineService
.OutlineService
).LoadOutline(foundView
, position
=startPos
)