]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/PythonEditor.py
fixed wxVsnprintf() to write as much as it can if the output buffer is too short
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / PythonEditor.py
1 #----------------------------------------------------------------------------
2 # Name: PythonEditor.py
3 # Purpose: PythonEditor for wx.lib.pydocview tbat uses the Styled Text Control
4 #
5 # Author: Peter Yared
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 CodeEditor
14 import wx
15 import wx.lib.docview
16 import wx.lib.pydocview
17 import string
18 import keyword # So it knows what to hilite
19 import wx.py # For the Python interpreter
20 import wx.stc # For the Python interpreter
21 import cStringIO # For indent
22 import OutlineService
23 import STCTextEditor
24 import keyword # for GetAutoCompleteKeywordList
25 import sys # for GetAutoCompleteKeywordList
26 import MessageService # for OnCheckCode
27 import OutlineService
28 import FindInDirService
29 from UICommon import CaseInsensitiveCompare
30 try:
31 import checker # for pychecker
32 _CHECKER_INSTALLED = True
33 except ImportError:
34 _CHECKER_INSTALLED = False
35 import os.path # for pychecker
36 _ = wx.GetTranslation
37
38 if wx.Platform == '__WXMSW__':
39 _WINDOWS = True
40 else:
41 _WINDOWS = False
42
43
44 VIEW_PYTHON_INTERPRETER_ID = wx.NewId()
45
46
47 class PythonDocument(CodeEditor.CodeDocument):
48 pass
49
50
51 class PythonView(CodeEditor.CodeView):
52
53
54 def GetCtrlClass(self):
55 """ Used in split window to instantiate new instances """
56 return PythonCtrl
57
58
59 def ProcessUpdateUIEvent(self, event):
60 if not self.GetCtrl():
61 return False
62
63 id = event.GetId()
64 if id == CodeEditor.CHECK_CODE_ID:
65 hasText = self.GetCtrl().GetTextLength() > 0
66 event.Enable(hasText)
67 return True
68
69 return CodeEditor.CodeView.ProcessUpdateUIEvent(self, event)
70
71
72 def OnActivateView(self, activate, activeView, deactiveView):
73 STCTextEditor.TextView.OnActivateView(self, activate, activeView, deactiveView)
74 if activate and self.GetCtrl():
75 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
76 self.LoadOutline()
77 else:
78 wx.CallAfter(self.LoadOutline) # need CallAfter because document isn't loaded yet
79
80
81 def OnClose(self, deleteWindow = True):
82 status = STCTextEditor.TextView.OnClose(self, deleteWindow)
83 wx.CallAfter(self.ClearOutline) # need CallAfter because when closing the document, it is Activated and then Close, so need to match OnActivateView's CallAfter
84 return status
85
86
87 def GetAutoCompleteKeywordList(self, context, hint):
88 obj = None
89 try:
90 if context and len(context):
91 obj = eval(context, globals(), locals())
92 except:
93 if not hint or len(hint) == 0: # context isn't valid, maybe it was the hint
94 hint = context
95
96 if obj is None:
97 kw = keyword.kwlist[:]
98 else:
99 symTbl = dir(obj)
100 kw = filter(lambda item: item[0] != '_', symTbl) # remove local variables and methods
101
102 if hint and len(hint):
103 lowerHint = hint.lower()
104 filterkw = filter(lambda item: item.lower().startswith(lowerHint), kw) # remove variables and methods that don't match hint
105 kw = filterkw
106
107 kw.sort(CaseInsensitiveCompare)
108
109 if hint:
110 replaceLen = len(hint)
111 else:
112 replaceLen = 0
113
114 return " ".join(kw), replaceLen
115
116
117 def OnCheckCode(self):
118 if not _CHECKER_INSTALLED:
119 wx.MessageBox(_("pychecker not found. Please install pychecker."), _("Check Code"))
120 return
121
122 filename = os.path.basename(self.GetDocument().GetFilename())
123
124 # pychecker only works on files, doesn't take a stream or string input
125 if self.GetDocument().IsModified():
126 dlg = wx.MessageDialog(self.GetFrame(), _("'%s' has been modfied and must be saved first. Save file and check code?") % filename, _("Check Code"))
127 dlg.CenterOnParent()
128 val = dlg.ShowModal()
129 dlg.Destroy()
130 if val == wx.ID_OK:
131 self.GetDocument().Save()
132 else:
133 return
134
135 messageService = wx.GetApp().GetService(MessageService.MessageService)
136 messageService.ShowWindow()
137 view = messageService.GetView()
138 if not view:
139 return
140
141 view.ClearLines()
142 view.SetCallback(self.OnJumpToFoundLine)
143
144 # Set cursor to Wait cursor
145 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
146
147 try:
148 # This takes a while for involved code
149 checker.checkSyntax(self.GetDocument().GetFilename(), view)
150
151 finally:
152 # Set cursor to Default cursor
153 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
154
155
156 def OnJumpToFoundLine(self, event):
157 messageService = wx.GetApp().GetService(MessageService.MessageService)
158 lineText, pos = messageService.GetView().GetCurrLine()
159
160 lineEnd = lineText.find(".py:")
161 if lineEnd == -1:
162 return
163
164 lineStart = lineEnd + len(".py:")
165 lineEnd = lineText.find(":", lineStart)
166 lineNum = int(lineText[lineStart:lineEnd])
167
168 filename = lineText[0:lineStart - 1]
169
170 foundView = None
171 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
172 for openDoc in openDocs:
173 if openDoc.GetFilename() == filename:
174 foundView = openDoc.GetFirstView()
175 break
176
177 if not foundView:
178 doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
179 foundView = doc.GetFirstView()
180
181 if foundView:
182 foundView.GetFrame().SetFocus()
183 foundView.Activate()
184 foundView.GotoLine(lineNum)
185 startPos = foundView.PositionFromLine(lineNum)
186 endPos = foundView.GetLineEndPosition(lineNum)
187 # wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
188 # Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
189 # time, we don't see the selection, it is scrolled off screen
190 foundView.SetSelection(endPos, startPos)
191 wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
192
193
194
195 class PythonInterpreterView(wx.lib.docview.View):
196
197
198 def OnCreate(self, doc, flags):
199 frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
200 sizer = wx.BoxSizer()
201 self._pyCrust = wx.py.crust.Crust(frame)
202 sizer.Add(self._pyCrust, 1, wx.EXPAND, 0)
203 frame.SetSizer(sizer)
204 frame.Layout()
205 self.Activate()
206 frame.Show()
207 return True
208
209
210 def ProcessEvent(self, event):
211 if not hasattr(self, "_pyCrust") or not self._pyCrust:
212 return wx.lib.docview.View.ProcessEvent(self, event)
213 stcControl = wx.Window_FindFocus()
214 if not isinstance(stcControl, wx.stc.StyledTextCtrl):
215 return wx.lib.docview.View.ProcessEvent(self, event)
216 id = event.GetId()
217 if id == wx.ID_UNDO:
218 stcControl.Undo()
219 return True
220 elif id == wx.ID_REDO:
221 stcControl.Redo()
222 return True
223 elif id == wx.ID_CUT:
224 stcControl.Cut()
225 return True
226 elif id == wx.ID_COPY:
227 stcControl.Copy()
228 return True
229 elif id == wx.ID_PASTE:
230 stcControl.Paste()
231 return True
232 elif id == wx.ID_CLEAR:
233 stcControl.Clear()
234 return True
235 elif id == wx.ID_SELECTALL:
236 stcControl.SetSelection(0, -1)
237 return True
238 else:
239 return wx.lib.docview.View.ProcessEvent(self, event)
240
241
242 def ProcessUpdateUIEvent(self, event):
243 if not hasattr(self, "_pyCrust") or not self._pyCrust:
244 return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
245 stcControl = wx.Window_FindFocus()
246 if not isinstance(stcControl, wx.stc.StyledTextCtrl):
247 return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
248 id = event.GetId()
249 if id == wx.ID_UNDO:
250 event.Enable(stcControl.CanUndo())
251 return True
252 elif id == wx.ID_REDO:
253 event.Enable(stcControl.CanRedo())
254 return True
255 elif id == wx.ID_CUT:
256 event.Enable(stcControl.CanCut())
257 return True
258 elif id == wx.ID_COPY:
259 event.Enable(stcControl.CanCopy())
260 return True
261 elif id == wx.ID_PASTE:
262 event.Enable(stcControl.CanPaste())
263 return True
264 elif id == wx.ID_CLEAR:
265 event.Enable(True) # wxBug: should be stcControl.CanCut()) but disabling clear item means del key doesn't work in control as expected
266 return True
267 elif id == wx.ID_SELECTALL:
268 event.Enable(stcControl.GetTextLength() > 0)
269 return True
270 else:
271 return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
272
273
274 def OnClose(self, deleteWindow=True):
275 if deleteWindow and self.GetFrame():
276 self.GetFrame().Destroy()
277 return True
278
279
280 class PythonInterpreterDocument(wx.lib.docview.Document):
281 """ Generate Unique Doc Type """
282 pass
283
284
285 class PythonService(CodeEditor.CodeService):
286
287
288 def __init__(self):
289 CodeEditor.CodeService.__init__(self)
290 docManager = wx.GetApp().GetDocumentManager()
291 pythonInterpreterTemplate = wx.lib.docview.DocTemplate(docManager,
292 _("Python Interpreter"),
293 "*.Foobar",
294 "Foobar",
295 ".Foobar",
296 _("Python Interpreter Document"),
297 _("Python Interpreter View"),
298 PythonInterpreterDocument,
299 PythonInterpreterView,
300 flags = wx.lib.docview.TEMPLATE_INVISIBLE,
301 icon = getPythonIcon())
302 docManager.AssociateTemplate(pythonInterpreterTemplate)
303
304
305 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
306 CodeEditor.CodeService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
307
308 if document and document.GetDocumentTemplate().GetDocumentType() != PythonDocument:
309 return
310 if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
311 return
312
313 viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
314
315 viewStatusBarItemPos = self.GetMenuItemPos(viewMenu, wx.lib.pydocview.VIEW_STATUSBAR_ID)
316 viewMenu.InsertCheckItem(viewStatusBarItemPos + 1, VIEW_PYTHON_INTERPRETER_ID, _("Python &Interpreter"), _("Shows or hides the Python interactive window"))
317 wx.EVT_MENU(frame, VIEW_PYTHON_INTERPRETER_ID, frame.ProcessEvent)
318 wx.EVT_UPDATE_UI(frame, VIEW_PYTHON_INTERPRETER_ID, frame.ProcessUpdateUIEvent)
319
320
321 def ProcessEvent(self, event):
322 id = event.GetId()
323 if id == VIEW_PYTHON_INTERPRETER_ID:
324 self.OnViewPythonInterpreter(event)
325 return True
326 else:
327 return CodeEditor.CodeService.ProcessEvent(self, event)
328
329
330 def ProcessUpdateUIEvent(self, event):
331 id = event.GetId()
332 if id == VIEW_PYTHON_INTERPRETER_ID:
333 event.Enable(True)
334 docManager = wx.GetApp().GetDocumentManager()
335 event.Check(False)
336 for doc in docManager.GetDocuments():
337 if isinstance(doc, PythonInterpreterDocument):
338 event.Check(True)
339 break
340 return True
341 else:
342 return CodeEditor.CodeService.ProcessUpdateUIEvent(self, event)
343
344
345 def OnViewPythonInterpreter(self, event):
346 for doc in wx.GetApp().GetDocumentManager().GetDocuments():
347 if isinstance(doc, PythonInterpreterDocument):
348 doc.DeleteAllViews()
349 return
350
351 for template in self.GetDocumentManager().GetTemplates():
352 if template.GetDocumentType() == PythonInterpreterDocument:
353 newDoc = template.CreateDocument('', wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
354 if newDoc:
355 newDoc.SetDocumentName(template.GetDocumentName())
356 newDoc.SetDocumentTemplate(template)
357 newDoc.OnNewDocument()
358 newDoc.SetWriteable(False)
359 newDoc.GetFirstView().GetFrame().SetTitle(_("Python Interpreter"))
360 break
361
362
363 class PythonCtrl(CodeEditor.CodeCtrl):
364
365
366 def __init__(self, parent, id=-1, style=wx.NO_FULL_REPAINT_ON_RESIZE):
367 CodeEditor.CodeCtrl.__init__(self, parent, id, style)
368 self.SetProperty("tab.timmy.whinge.level", "1")
369 self.SetProperty("fold.comment.python", "1")
370 self.SetProperty("fold.quotes.python", "1")
371 self.SetLexer(wx.stc.STC_LEX_PYTHON)
372 self.SetKeyWords(0, string.join(keyword.kwlist))
373
374
375 def CreatePopupMenu(self):
376 FINDCLASS_ID = wx.NewId()
377 FINDDEF_ID = wx.NewId()
378
379 menu = CodeEditor.CodeCtrl.CreatePopupMenu(self)
380
381 self.Bind(wx.EVT_MENU, self.OnPopFindDefinition, id=FINDDEF_ID)
382 menu.Insert(1, FINDDEF_ID, _("Find 'def'"))
383
384 self.Bind(wx.EVT_MENU, self.OnPopFindClass, id=FINDCLASS_ID)
385 menu.Insert(2, FINDCLASS_ID, _("Find 'class'"))
386
387 return menu
388
389
390 def OnPopFindDefinition(self, event):
391 view = wx.GetApp().GetDocumentManager().GetCurrentView()
392 if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
393 pattern = view.GetCtrl().GetSelectedText().strip()
394 if pattern:
395 searchPattern = "def\s+%s" % pattern
396 wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
397
398
399 def OnPopFindClass(self, event):
400 view = wx.GetApp().GetDocumentManager().GetCurrentView()
401 if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
402 definition = "class\s+%s"
403 pattern = view.GetCtrl().GetSelectedText().strip()
404 if pattern:
405 searchPattern = definition % pattern
406 wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
407
408
409 def SetViewDefaults(self):
410 CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix="Python", hasWordWrap=True, hasTabs=True, hasFolding=True)
411
412
413 def GetFontAndColorFromConfig(self):
414 return CodeEditor.CodeCtrl.GetFontAndColorFromConfig(self, configPrefix = "Python")
415
416
417 def UpdateStyles(self):
418 CodeEditor.CodeCtrl.UpdateStyles(self)
419
420 if not self.GetFont():
421 return
422
423 faces = { 'font' : self.GetFont().GetFaceName(),
424 'size' : self.GetFont().GetPointSize(),
425 'size2': self.GetFont().GetPointSize() - 2,
426 'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue())
427 }
428
429 # Python styles
430 # White space
431 self.StyleSetSpec(wx.stc.STC_P_DEFAULT, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
432 # Comment
433 self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces)
434 # Number
435 self.StyleSetSpec(wx.stc.STC_P_NUMBER, "face:%(font)s,fore:#007F7F,size:%(size)d" % faces)
436 # String
437 self.StyleSetSpec(wx.stc.STC_P_STRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
438 # Single quoted string
439 self.StyleSetSpec(wx.stc.STC_P_CHARACTER, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
440 # Keyword
441 self.StyleSetSpec(wx.stc.STC_P_WORD, "face:%(font)s,fore:#00007F,bold,size:%(size)d" % faces)
442 # Triple quotes
443 self.StyleSetSpec(wx.stc.STC_P_TRIPLE, "face:%(font)s,fore:#7F0000,size:%(size)d" % faces)
444 # Triple double quotes
445 self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, "face:%(font)s,fore:#7F0000,size:%(size)d" % faces)
446 # Class name definition
447 self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, "face:%(font)s,fore:#0000FF,bold,size:%(size)d" % faces)
448 # Function or method name definition
449 self.StyleSetSpec(wx.stc.STC_P_DEFNAME, "face:%(font)s,fore:#007F7F,bold,size:%(size)d" % faces)
450 # Operators
451 self.StyleSetSpec(wx.stc.STC_P_OPERATOR, "face:%(font)s,size:%(size)d" % faces)
452 # Identifiers
453 self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, "face:%(font)s,fore:#%(color)s,face:%(font)s,size:%(size)d" % faces)
454 # Comment-blocks
455 self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, "face:%(font)s,fore:#7F7F7F,size:%(size)d" % faces)
456 # End of line where string is not closed
457 self.StyleSetSpec(wx.stc.STC_P_STRINGEOL, "face:%(font)s,fore:#000000,face:%(font)s,back:#E0C0E0,eol,size:%(size)d" % faces)
458
459
460 def OnUpdateUI(self, evt):
461 braces = self.GetMatchingBraces()
462
463 # check for matching braces
464 braceAtCaret = -1
465 braceOpposite = -1
466 charBefore = None
467 caretPos = self.GetCurrentPos()
468 if caretPos > 0:
469 charBefore = self.GetCharAt(caretPos - 1)
470 styleBefore = self.GetStyleAt(caretPos - 1)
471
472 # check before
473 if charBefore and chr(charBefore) in braces and styleBefore == wx.stc.STC_P_OPERATOR:
474 braceAtCaret = caretPos - 1
475
476 # check after
477 if braceAtCaret < 0:
478 charAfter = self.GetCharAt(caretPos)
479 styleAfter = self.GetStyleAt(caretPos)
480 if charAfter and chr(charAfter) in braces and styleAfter == wx.stc.STC_P_OPERATOR:
481 braceAtCaret = caretPos
482
483 if braceAtCaret >= 0:
484 braceOpposite = self.BraceMatch(braceAtCaret)
485
486 if braceAtCaret != -1 and braceOpposite == -1:
487 self.BraceBadLight(braceAtCaret)
488 else:
489 self.BraceHighlight(braceAtCaret, braceOpposite)
490
491 evt.Skip()
492
493
494 def DoIndent(self):
495 (text, caretPos) = self.GetCurLine()
496
497 self._tokenizerChars = {} # This is really too much, need to find something more like a C array
498 for i in range(len(text)):
499 self._tokenizerChars[i] = 0
500
501 ctext = cStringIO.StringIO(text)
502 try:
503 tokenize.tokenize(ctext.readline, self)
504 except:
505 pass
506
507 # Left in for debugging purposes:
508 #for i in range(len(text)):
509 # print i, text[i], self._tokenizerChars[i]
510
511 if caretPos == 0 or len(string.strip(text)) == 0: # At beginning of line or within an empty line
512 self.AddText('\n')
513 else:
514 doExtraIndent = False
515 brackets = False
516 commentStart = -1
517 if caretPos > 1:
518 startParenCount = 0
519 endParenCount = 0
520 startSquareBracketCount = 0
521 endSquareBracketCount = 0
522 startCurlyBracketCount = 0
523 endCurlyBracketCount = 0
524 startQuoteCount = 0
525 endQuoteCount = 0
526 for i in range(caretPos - 1, -1, -1): # Go through each character before the caret
527 if i >= len(text): # Sometimes the caret is at the end of the text if there is no LF
528 continue
529 if self._tokenizerChars[i] == 1:
530 continue
531 elif self._tokenizerChars[i] == 2:
532 startQuoteCount = startQuoteCount + 1
533 elif self._tokenizerChars[i] == 3:
534 endQuoteCount = endQuoteCount + 1
535 elif text[i] == '(': # Would be nice to use a dict for this, but the code is much more readable this way
536 startParenCount = startParenCount + 1
537 elif text[i] == ')':
538 endParenCount = endParenCount + 1
539 elif text[i] == "[":
540 startSquareBracketCount = startSquareBracketCount + 1
541 elif text[i] == "]":
542 endSquareBracketCount = endSquareBracketCount + 1
543 elif text[i] == "{":
544 startCurlyBracketCount = startCurlyBracketCount + 1
545 elif text[i] == "}":
546 endCurlyBracketCount = endCurlyBracketCount + 1
547 elif text[i] == "#":
548 commentStart = i
549 break
550 if startQuoteCount > endQuoteCount or startParenCount > endParenCount or startSquareBracketCount > endSquareBracketCount or startCurlyBracketCount > endCurlyBracketCount:
551 if i + 1 >= caretPos: # Caret is right at the open paren, so just do indent as if colon was there
552 doExtraIndent = True
553 break
554 else:
555 spaces = " " * (i + 1)
556 brackets = True
557 break
558 if not brackets:
559 spaces = text[0:len(text) - len(string.lstrip(text))]
560 if caretPos < len(spaces): # If within the opening spaces of a line
561 spaces = spaces[:caretPos]
562
563 # strip comment off
564 if commentStart != -1:
565 text = text[0:commentStart]
566
567 textNoTrailingSpaces = text[0:caretPos].rstrip()
568 if doExtraIndent or len(textNoTrailingSpaces) and textNoTrailingSpaces[-1] == ':':
569 spaces = spaces + ' ' * self.GetIndent()
570 self.AddText('\n' + spaces)
571 self.EnsureCaretVisible()
572
573
574 # Callback for tokenizer in self.DoIndent
575 def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
576 if toktype == tokenize.COMMENT:
577 for i in range(scol, ecol + 1):
578 self._validChars[i] = False
579 elif toktype == token.STRING:
580 self._tokenizerChars[scol] = 2 # Open quote
581 self._tokenizerChars[ecol - 1] = 3 # Close quote
582 for i in range(scol + 1, ecol - 2):
583 self._tokenizerChars[i] = 1 # Part of string, 1 == ignore the char
584
585
586 class PythonOptionsPanel(wx.Panel):
587
588 def __init__(self, parent, id):
589 wx.Panel.__init__(self, parent, id)
590 pathLabel = wx.StaticText(self, -1, _("python.exe Path:"))
591 config = wx.ConfigBase_Get()
592 path = config.Read("ActiveGridPythonLocation")
593 self._pathTextCtrl = wx.TextCtrl(self, -1, path, size = (150, -1))
594 self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue())
595 self._pathTextCtrl.SetInsertionPointEnd()
596 choosePathButton = wx.Button(self, -1, _("Browse..."))
597 pathSizer = wx.BoxSizer(wx.HORIZONTAL)
598 HALF_SPACE = 5
599 SPACE = 10
600 pathSizer.Add(pathLabel, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP, HALF_SPACE)
601 pathSizer.Add(self._pathTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.TOP, HALF_SPACE)
602 pathSizer.Add(choosePathButton, 0, wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT|wx.TOP, HALF_SPACE)
603 wx.EVT_BUTTON(self, choosePathButton.GetId(), self.OnChoosePath)
604 mainSizer = wx.BoxSizer(wx.VERTICAL)
605 mainSizer.Add(pathSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
606
607 self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = True, hasTabs = True, addPage=False, hasFolding=True)
608 mainSizer.Add(self._otherOptions, 0, wx.EXPAND|wx.BOTTOM, SPACE)
609 self.SetSizer(mainSizer)
610 parent.AddPage(self, _("Python"))
611
612
613 def OnChoosePath(self, event):
614 defaultDir = os.path.dirname(self._pathTextCtrl.GetValue().strip())
615 defaultFile = os.path.basename(self._pathTextCtrl.GetValue().strip())
616 if _WINDOWS:
617 wildcard = _("Executable (*.exe)|*.exe|All|*.*")
618 if not defaultFile:
619 defaultFile = "python.exe"
620 else:
621 wildcard = _("*")
622 dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
623 _("Select a File"),
624 defaultDir=defaultDir,
625 defaultFile=defaultFile,
626 wildcard=wildcard,
627 style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY)
628 # dlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
629 if dlg.ShowModal() == wx.ID_OK:
630 path = dlg.GetPath()
631 if path:
632 self._pathTextCtrl.SetValue(path)
633 self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue())
634 self._pathTextCtrl.SetInsertionPointEnd()
635 dlg.Destroy()
636
637
638 def OnOK(self, optionsDialog):
639 config = wx.ConfigBase_Get()
640 config.Write("ActiveGridPythonLocation", self._pathTextCtrl.GetValue().strip())
641
642 self._otherOptions.OnOK(optionsDialog)
643
644
645 def GetIcon(self):
646 return getPythonIcon()
647
648
649 #----------------------------------------------------------------------------
650 # Icon Bitmaps - generated by encode_bitmaps.py
651 #----------------------------------------------------------------------------
652 from wx import ImageFromStream, BitmapFromImage
653 import cStringIO
654
655
656 def getPythonData():
657 return \
658 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
659 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
660 \x00\x01\xe7IDAT8\x8d}\x921h\x13Q\x18\xc7\x7fw\xb9\x0ei\x9d*\xbd\xeb\x10\x8f\
661 ,\x99\x1c*A[\xaa\x19B\xe8\xd0\xb1\x0e%K\x87\x88T2\x88Cqp\tD\x14i\xe9\xe0V\
662 \xdaQ\xb7\xe0P\xa1\x8b\xa0(\x95$z\xd5Q1\x90\xa2\xd7\x9a4^\x87\xa0`\x92!w9\
663 \x87\xf8.\xb9\xa6\xc97\xbd\xef{\xef\xfb\xbd\xff\xfb\xbfO*~;v\xf9\x1f\xad\xba\
664 \x05@\xf9\xd4\x06\xc0::$\xbb\x96\x92\x18\x11\n@(4\xdd\xcdB\xd3\xd4\x1d\x85\
665 \x8b\x97\xe1\xe3;\x83\x99\xe5\x15\xb2\xe0\x8e\x82\xc8\xa3\xe8\x003\xcb+\xac\
666 \xaee\xdda\xfb\xb2\x90\rPw\x14\x00\x9a\xb5\n\xbf\xfflSz\x9d\xa2Y\xdc"zca\xe8\
667 \x05\xb2h\x14\xcd\xd0\xf3B\x9f\x98\xe5\xf9\xde\x13"\xaaB\xc7\xb1\xcfU!\x0b\
668 \xc3D4k\x15\xac\x93\x03\xf4\x89Y\xaf\x96\xffT\x028\x17\xa2\xf4\'\xcdZ\x85\
669 \xf7F\x06{\xaa\x80ev\xc1\x91\xb91>\x18\x0f\xb8\xb7\x95a\xe9\xca\x0b:\x8e\xed\
670 \xca\x01E\x1a\x00\x98\r\x89\x92\x91\xa1\xda\xd8\x87\x06ha\x1f\x1b\x80\xcd\
671 \x9d%\xe0\xa5\x0f"[G\x87\x98\x8d\xde/ia\x05-\xac`\x996\xf9\\\x0b\xcb\xb4)\
672 \x1bmOMn\xf7\xd5\xf0\'\\\x8b\xdces\xe7\x8d\xef\x80h\xd6\xc2\n\xf9\\\x0b]\xf5\
673 \xab\xf2\xcdApR#\xf1kp4b\xc9 \xf9\\\x0b\x80\xe4\xcdE\xaf\xdeqlW\xaeVL\xaf`~\
674 \xd9\x03@W\xd3\x00\xc4\x13\x0b\xc4\x92A\xcf\xd0\xf9\xe8:\x89\xebW\x01(|\xfd\
675 \xe1\xbe-~F\xbas\xff\x91\xf75\x82n\x9d\x1c\xf0}\xfciw\xdd\xe7A<\xd1\x1b\xa8j\
676 c\x9f\xb2\xd1F\x92\xe4\x80O\x12\xc0\xc6\xb3\x14\xf6Ta\xe0)g\x81\xba\x9a\xf6\
677 \x9b(\x07\x14I@\x84lq\xb8?\xe6\xa3\xeb\x00\xdc\xba\x9d\xf4+\x10*~\xfem\xf3\
678 \xf8\xe1\x06\xc7\xa7\xdb\xe8j\x9a\xf8\xdc\xa4\xb7\x1f[\\\xe5\xd2\x851/\xff\
679 \x07\xac\x9b\xd1e\x12\x96\x0f\xfd\x00\x00\x00\x00IEND\xaeB`\x82'
680
681
682 def getPythonBitmap():
683 return BitmapFromImage(getPythonImage())
684
685 def getPythonImage():
686 stream = cStringIO.StringIO(getPythonData())
687 return ImageFromStream(stream)
688
689 def getPythonIcon():
690 return wx.IconFromBitmap(getPythonBitmap())