]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/FindInDirService.py
Patch from Davide Salomoni that adds an optional point
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / FindInDirService.py
1 #----------------------------------------------------------------------------
2 # Name: IDEFindService.py
3 # Purpose: Find Service for pydocview
4 #
5 # Author: Morgan Hua
6 #
7 # Created: 8/15/03
8 # CVS-ID: $Id$
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13 import wx
14 import wx.lib.docview
15 import os
16 from os.path import join
17 import re
18 import ProjectEditor
19 import MessageService
20 import FindService
21 import OutlineService
22 _ = wx.GetTranslation
23
24
25 #----------------------------------------------------------------------------
26 # Constants
27 #----------------------------------------------------------------------------
28 FILENAME_MARKER = _("Found in file: ")
29 PROJECT_MARKER = _("Searching project: ")
30 FIND_MATCHDIR = "FindMatchDir"
31 FIND_MATCHDIRSUBFOLDERS = "FindMatchDirSubfolders"
32
33 SPACE = 10
34 HALF_SPACE = 5
35
36
37 class FindInDirService(FindService.FindService):
38
39 #----------------------------------------------------------------------------
40 # Constants
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
44
45
46 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
47 FindService.FindService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
48
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"))
56
57
58 def ProcessEvent(self, event):
59 id = event.GetId()
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())
64 else:
65 self.ShowFindAllDialog()
66 return True
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())
71 else:
72 self.ShowFindDirDialog()
73 return True
74 else:
75 return FindService.FindService.ProcessEvent(self, event)
76
77
78 def ProcessUpdateUIEvent(self, event):
79 id = event.GetId()
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():
84 event.Enable(True)
85 else:
86 event.Enable(False)
87 return True
88 elif id == FindInDirService.FINDDIR_ID:
89 event.Enable(True)
90 else:
91 return FindService.FindService.ProcessUpdateUIEvent(self, event)
92
93
94 def ShowFindDirDialog(self, findString=None):
95 config = wx.ConfigBase_Get()
96
97 frame = wx.Dialog(None, -1, _("Find in Directory"), size= (320,200))
98 borderSizer = wx.BoxSizer(wx.HORIZONTAL)
99
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)
109
110 def OnBrowseButton(event):
111 dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
112 dir = dirCtrl.GetValue()
113 if len(dir):
114 dlg.SetPath(dir)
115 if dlg.ShowModal() == wx.ID_OK:
116 dirCtrl.SetValue(dlg.GetPath())
117 dirCtrl.SetToolTipString(dirCtrl.GetValue())
118 dirCtrl.SetInsertionPointEnd()
119
120 dlg.Destroy()
121 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
122
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)
126
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)
130
131 lineSizer = wx.BoxSizer(wx.HORIZONTAL)
132 lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
133 if not findString:
134 findString = config.Read(FindService.FIND_MATCHPATTERN, "")
135 findCtrl = wx.TextCtrl(frame, -1, findString, size=(200,-1))
136 findCtrl.SetFocus()
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)
150
151 buttonSizer = wx.BoxSizer(wx.VERTICAL)
152 findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
153 findBtn.SetDefault()
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)
157
158 frame.SetSizer(borderSizer)
159 frame.Fit()
160
161 status = frame.ShowModal()
162
163 passedCheck = False
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
170 )
171 dlg.ShowModal()
172 dlg.Destroy()
173
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
180 )
181 dlg.ShowModal()
182 dlg.Destroy()
183
184 status = frame.ShowModal()
185 else:
186 passedCheck = True
187
188
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)
193
194 findString = findCtrl.GetValue()
195 matchCase = matchCaseCtrl.IsChecked()
196 wholeWord = wholeWordCtrl.IsChecked()
197 regExpr = regExprCtrl.IsChecked()
198 self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
199
200
201 if status == wx.ID_OK:
202 frame.Destroy()
203
204 messageService = wx.GetApp().GetService(MessageService.MessageService)
205 messageService.ShowWindow()
206
207 view = messageService.GetView()
208 if view:
209 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
210 view.ClearLines()
211 view.SetCallback(self.OnJumpToFoundLine)
212
213 view.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString, dirString))
214
215 if os.path.isfile(dirString):
216 try:
217 docFile = file(dirString, 'r')
218 lineNum = 1
219 needToDisplayFilename = True
220 line = docFile.readline()
221 while line:
222 count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
223 if count != -1:
224 if needToDisplayFilename:
225 view.AddLines(FILENAME_MARKER + dirString + "\n")
226 needToDisplayFilename = False
227 line = repr(lineNum).zfill(4) + ":" + line
228 view.AddLines(line)
229 line = docFile.readline()
230 lineNum += 1
231 if not needToDisplayFilename:
232 view.AddLines("\n")
233 except IOError, (code, message):
234 print _("Warning, unable to read file: '%s'. %s") % (dirString, message)
235 else:
236 # do search in files on disk
237 for root, dirs, files in os.walk(dirString):
238 if not searchSubfolders and root != dirString:
239 break
240
241 for name in files:
242 filename = os.path.join(root, name)
243 try:
244 docFile = file(filename, 'r')
245 except IOError, (code, message):
246 print _("Warning, unable to read file: '%s'. %s") % (filename, message)
247 continue
248
249 lineNum = 1
250 needToDisplayFilename = True
251 line = docFile.readline()
252 while line:
253 count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
254 if count != -1:
255 if needToDisplayFilename:
256 view.AddLines(FILENAME_MARKER + filename + "\n")
257 needToDisplayFilename = False
258 line = repr(lineNum).zfill(4) + ":" + line
259 view.AddLines(line)
260 line = docFile.readline()
261 lineNum += 1
262 if not needToDisplayFilename:
263 view.AddLines("\n")
264
265 view.AddLines(_("Search completed."))
266 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
267
268 return True
269 else:
270 frame.Destroy()
271 return False
272
273
274 def SaveFindDirConfig(self, dirString, searchSubfolders):
275 """ Save search dir patterns and flags to registry.
276
277 dirString = search directory
278 searchSubfolders = Search subfolders
279 """
280 config = wx.ConfigBase_Get()
281 config.Write(FIND_MATCHDIR, dirString)
282 config.WriteInt(FIND_MATCHDIRSUBFOLDERS, searchSubfolders)
283
284
285 def ShowFindAllDialog(self, findString=None):
286 config = wx.ConfigBase_Get()
287
288 frame = wx.Dialog(None, -1, _("Find in Project"), size= (320,200))
289 borderSizer = wx.BoxSizer(wx.HORIZONTAL)
290
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)
294 if not findString:
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)
309
310 buttonSizer = wx.BoxSizer(wx.VERTICAL)
311 findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
312 findBtn.SetDefault()
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)
316
317 frame.SetSizer(borderSizer)
318 frame.Fit()
319
320 status = frame.ShowModal()
321
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)
328
329 if status == wx.ID_OK:
330 frame.Destroy()
331
332 messageService = wx.GetApp().GetService(MessageService.MessageService)
333 messageService.ShowWindow()
334
335 view = messageService.GetView()
336 if view:
337 view.ClearLines()
338 view.SetCallback(self.OnJumpToFoundLine)
339
340 projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
341 projectFilenames = projectService.GetFilesFromCurrentProject()
342
343 projView = projectService.GetView()
344 if projView:
345 projName = wx.lib.docview.FileNameFromPath(projView.GetDocument().GetFilename())
346 view.AddLines(PROJECT_MARKER + projName + "\n\n")
347
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
353 continue
354
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"):
359 continue
360 text = openDocView.GetValue()
361
362 lineNum = 1
363 needToDisplayFilename = True
364 start = 0
365 end = 0
366 count = 0
367 while count != -1:
368 count, foundStart, foundEnd, newText = self.DoFind(findString, None, text, start, end, True, matchCase, wholeWord, regExpr)
369 if count != -1:
370 if needToDisplayFilename:
371 view.AddLines(FILENAME_MARKER + openDoc.GetFilename() + "\n")
372 needToDisplayFilename = False
373
374 lineNum = openDocView.LineFromPosition(foundStart)
375 line = repr(lineNum).zfill(4) + ":" + openDocView.GetLine(lineNum)
376 view.AddLines(line)
377
378 start = text.find("\n", foundStart)
379 if start == -1:
380 break
381 end = start
382 if not needToDisplayFilename:
383 view.AddLines("\n")
384 openDocNames = map(lambda openDoc: openDoc.GetFilename(), openDocs)
385
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:
389 try:
390 docFile = file(filename, 'r')
391 except IOError, (code, message):
392 print _("Warning, unable to read file: '%s'. %s") % (filename, message)
393 continue
394
395 lineNum = 1
396 needToDisplayFilename = True
397 line = docFile.readline()
398 while line:
399 count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
400 if count != -1:
401 if needToDisplayFilename:
402 view.AddLines(FILENAME_MARKER + filename + "\n")
403 needToDisplayFilename = False
404 line = repr(lineNum).zfill(4) + ":" + line
405 view.AddLines(line)
406 line = docFile.readline()
407 lineNum += 1
408 if not needToDisplayFilename:
409 view.AddLines("\n")
410
411 view.AddLines(_("Search for '%s' completed.") % findString)
412
413 return True
414 else:
415 frame.Destroy()
416 return False
417
418
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:
423 return
424 lineEnd = lineText.find(":")
425 if lineEnd == -1:
426 return
427 else:
428 lineNum = int(lineText[0:lineEnd])
429
430 text = messageService.GetView().GetText()
431 curPos = messageService.GetView().GetCurrentPos()
432
433 startPos = text.rfind(FILENAME_MARKER, 0, curPos)
434 endPos = text.find("\n", startPos)
435 filename = text[startPos + len(FILENAME_MARKER):endPos]
436
437 foundView = None
438 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
439 for openDoc in openDocs:
440 if openDoc.GetFilename() == filename:
441 foundView = openDoc.GetFirstView()
442 break
443
444 if not foundView:
445 doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
446 foundView = doc.GetFirstView()
447
448 if foundView:
449 foundView.GetFrame().SetFocus()
450 foundView.Activate()
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)
459
460