]>
Commit | Line | Data |
---|---|---|
1f780e48 RD |
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 | try: | |
29 | import checker # for pychecker | |
30 | _CHECKER_INSTALLED = True | |
31 | except ImportError: | |
32 | _CHECKER_INSTALLED = False | |
33 | import os.path # for pychecker | |
34 | _ = wx.GetTranslation | |
35 | ||
36 | if wx.Platform == '__WXMSW__': | |
37 | _WINDOWS = True | |
38 | else: | |
39 | _WINDOWS = False | |
40 | ||
41 | ||
42 | VIEW_PYTHON_INTERPRETER_ID = wx.NewId() | |
43 | ||
44 | ||
45 | class PythonDocument(CodeEditor.CodeDocument): | |
46 | pass | |
47 | ||
48 | ||
49 | class PythonView(CodeEditor.CodeView): | |
50 | ||
51 | ||
52 | def ProcessUpdateUIEvent(self, event): | |
53 | if not self.GetCtrl(): | |
54 | return False | |
55 | ||
56 | id = event.GetId() | |
57 | if id == CodeEditor.CHECK_CODE_ID: | |
58 | hasText = self.GetCtrl().GetTextLength() > 0 | |
59 | event.Enable(hasText) | |
60 | return True | |
61 | ||
62 | return CodeEditor.CodeView.ProcessUpdateUIEvent(self, event) | |
63 | ||
64 | ||
65 | def GetCtrlClass(self): | |
66 | """ Used in split window to instantiate new instances """ | |
67 | return PythonCtrl | |
68 | ||
69 | ||
70 | def OnActivateView(self, activate, activeView, deactiveView): | |
71 | STCTextEditor.TextView.OnActivateView(self, activate, activeView, deactiveView) | |
72 | if activate: | |
73 | wx.CallAfter(self.LoadOutline) # need CallAfter because document isn't loaded yet | |
74 | ||
75 | ||
76 | def OnClose(self, deleteWindow = True): | |
77 | status = STCTextEditor.TextView.OnClose(self, deleteWindow) | |
78 | wx.CallAfter(self.ClearOutline) # need CallAfter because when closing the document, it is Activated and then Close, so need to match OnActivateView's CallAfter | |
79 | return status | |
80 | ||
81 | ||
82 | def GetAutoCompleteKeywordList(self, context, hint): | |
83 | obj = None | |
84 | try: | |
85 | if context and len(context): | |
86 | obj = eval(context, globals(), locals()) | |
87 | except: | |
88 | if not hint or len(hint) == 0: # context isn't valid, maybe it was the hint | |
89 | hint = context | |
90 | ||
91 | if obj is None: | |
92 | kw = keyword.kwlist[:] | |
93 | else: | |
94 | symTbl = dir(obj) | |
95 | kw = filter(lambda item: item[0] != '_', symTbl) # remove local variables and methods | |
96 | ||
97 | if hint and len(hint): | |
98 | lowerHint = hint.lower() | |
99 | filterkw = filter(lambda item: item.lower().startswith(lowerHint), kw) # remove variables and methods that don't match hint | |
100 | kw = filterkw | |
101 | ||
102 | kw.sort(self.CaseInsensitiveCompare) | |
103 | ||
104 | if hint: | |
105 | replaceLen = len(hint) | |
106 | else: | |
107 | replaceLen = 0 | |
108 | ||
109 | return " ".join(kw), replaceLen | |
110 | ||
111 | ||
112 | def OnCheckCode(self): | |
113 | if not _CHECKER_INSTALLED: | |
114 | wx.MessageBox(_("pychecker not found. Please install pychecker."), _("Check Code")) | |
115 | return | |
116 | ||
117 | filename = os.path.basename(self.GetDocument().GetFilename()) | |
118 | ||
119 | # pychecker only works on files, doesn't take a stream or string input | |
120 | if self.GetDocument().IsModified(): | |
121 | dlg = wx.MessageDialog(self.GetFrame(), _("'%s' has been modfied and must be saved first. Save file and check code?") % filename, _("Check Code")) | |
122 | val = dlg.ShowModal() | |
123 | dlg.Destroy() | |
124 | if val == wx.ID_OK: | |
125 | self.GetDocument().Save() | |
126 | else: | |
127 | return | |
128 | ||
129 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
130 | messageService.ShowWindow() | |
131 | view = messageService.GetView() | |
132 | if not view: | |
133 | return | |
134 | ||
135 | view.ClearLines() | |
136 | view.SetCallback(self.OnJumpToFoundLine) | |
137 | ||
138 | # Set cursor to Wait cursor | |
139 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) | |
140 | ||
141 | # This takes a while for involved code | |
142 | checker.checkSyntax(self.GetDocument().GetFilename(), view) | |
143 | ||
144 | # Set cursor to Default cursor | |
145 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | |
146 | ||
147 | ||
148 | def OnJumpToFoundLine(self, event): | |
149 | messageService = wx.GetApp().GetService(MessageService.MessageService) | |
150 | lineText, pos = messageService.GetView().GetCurrLine() | |
151 | ||
152 | lineEnd = lineText.find(".py:") | |
153 | if lineEnd == -1: | |
154 | return | |
155 | ||
156 | lineStart = lineEnd + len(".py:") | |
157 | lineEnd = lineText.find(":", lineStart) | |
158 | lineNum = int(lineText[lineStart:lineEnd]) | |
159 | ||
160 | filename = lineText[0:lineStart - 1] | |
161 | ||
162 | foundView = None | |
163 | openDocs = wx.GetApp().GetDocumentManager().GetDocuments() | |
164 | for openDoc in openDocs: | |
165 | if openDoc.GetFilename() == filename: | |
166 | foundView = openDoc.GetFirstView() | |
167 | break | |
168 | ||
169 | if not foundView: | |
170 | doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT) | |
171 | foundView = doc.GetFirstView() | |
172 | ||
173 | if foundView: | |
174 | foundView.GetFrame().SetFocus() | |
175 | foundView.Activate() | |
176 | foundView.GotoLine(lineNum) | |
177 | startPos = foundView.PositionFromLine(lineNum) | |
178 | endPos = foundView.GetLineEndPosition(lineNum) | |
179 | # wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct | |
180 | # Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first | |
181 | # time, we don't see the selection, it is scrolled off screen | |
182 | foundView.SetSelection(endPos, startPos) | |
183 | wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos) | |
184 | ||
185 | ||
186 | ||
187 | class PythonInterpreterView(wx.lib.docview.View): | |
188 | ||
189 | ||
190 | def OnCreate(self, doc, flags): | |
191 | frame = wx.GetApp().CreateDocumentFrame(self, doc, flags) | |
192 | sizer = wx.BoxSizer() | |
193 | self._pyCrust = wx.py.crust.Crust(frame) | |
194 | sizer.Add(self._pyCrust, 1, wx.EXPAND, 0) | |
195 | frame.SetSizer(sizer) | |
196 | frame.Layout() | |
197 | self.Activate() | |
198 | frame.Show() | |
199 | return True | |
200 | ||
201 | ||
202 | def ProcessEvent(self, event): | |
203 | if not hasattr(self, "_pyCrust") or not self._pyCrust: | |
204 | return wx.lib.docview.View.ProcessEvent(self, event) | |
205 | stcControl = wx.Window_FindFocus() | |
206 | if not isinstance(stcControl, wx.stc.StyledTextCtrl): | |
207 | return wx.lib.docview.View.ProcessEvent(self, event) | |
208 | id = event.GetId() | |
209 | if id == wx.ID_UNDO: | |
210 | stcControl.Undo() | |
211 | return True | |
212 | elif id == wx.ID_REDO: | |
213 | stcControl.Redo() | |
214 | return True | |
215 | elif id == wx.ID_CUT: | |
216 | stcControl.Cut() | |
217 | return True | |
218 | elif id == wx.ID_COPY: | |
219 | stcControl.Copy() | |
220 | return True | |
221 | elif id == wx.ID_PASTE: | |
222 | stcControl.Paste() | |
223 | return True | |
224 | elif id == wx.ID_CLEAR: | |
225 | stcControl.Clear() | |
226 | return True | |
227 | elif id == wx.ID_SELECTALL: | |
228 | stcControl.SetSelection(0, -1) | |
229 | return True | |
230 | else: | |
231 | return wx.lib.docview.View.ProcessEvent(self, event) | |
232 | ||
233 | ||
234 | def ProcessUpdateUIEvent(self, event): | |
235 | if not hasattr(self, "_pyCrust") or not self._pyCrust: | |
236 | return wx.lib.docview.View.ProcessUpdateUIEvent(self, event) | |
237 | stcControl = wx.Window_FindFocus() | |
238 | if not isinstance(stcControl, wx.stc.StyledTextCtrl): | |
239 | return wx.lib.docview.View.ProcessUpdateUIEvent(self, event) | |
240 | id = event.GetId() | |
241 | if id == wx.ID_UNDO: | |
242 | event.Enable(stcControl.CanUndo()) | |
243 | return True | |
244 | elif id == wx.ID_REDO: | |
245 | event.Enable(stcControl.CanRedo()) | |
246 | return True | |
247 | elif id == wx.ID_CUT: | |
248 | event.Enable(stcControl.CanCut()) | |
249 | return True | |
250 | elif id == wx.ID_COPY: | |
251 | event.Enable(stcControl.CanCopy()) | |
252 | return True | |
253 | elif id == wx.ID_PASTE: | |
254 | event.Enable(stcControl.CanPaste()) | |
255 | return True | |
256 | elif id == wx.ID_CLEAR: | |
257 | event.Enable(True) # wxBug: should be stcControl.CanCut()) but disabling clear item means del key doesn't work in control as expected | |
258 | return True | |
259 | elif id == wx.ID_SELECTALL: | |
260 | event.Enable(stcControl.GetTextLength() > 0) | |
261 | return True | |
262 | else: | |
263 | return wx.lib.docview.View.ProcessUpdateUIEvent(self, event) | |
264 | ||
265 | ||
266 | def OnClose(self, deleteWindow=True): | |
267 | if deleteWindow and self.GetFrame(): | |
268 | self.GetFrame().Destroy() | |
269 | return True | |
270 | ||
271 | ||
272 | class PythonService(CodeEditor.CodeService): | |
273 | ||
274 | ||
275 | def __init__(self): | |
276 | CodeEditor.CodeService.__init__(self) | |
277 | ||
278 | ||
279 | def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None): | |
280 | CodeEditor.CodeService.InstallControls(self, frame, menuBar, toolBar, statusBar, document) | |
281 | ||
282 | if document and document.GetDocumentTemplate().GetDocumentType() != PythonDocument: | |
283 | return | |
284 | if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI: | |
285 | return | |
286 | ||
287 | viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View"))) | |
288 | ||
289 | viewStatusBarItemPos = self.GetMenuItemPos(viewMenu, wx.lib.pydocview.VIEW_STATUSBAR_ID) | |
290 | viewMenu.InsertCheckItem(viewStatusBarItemPos + 1, VIEW_PYTHON_INTERPRETER_ID, _("Python &Interpreter"), _("Shows or hides the Python interactive window")) | |
291 | wx.EVT_MENU(frame, VIEW_PYTHON_INTERPRETER_ID, frame.ProcessEvent) | |
292 | wx.EVT_UPDATE_UI(frame, VIEW_PYTHON_INTERPRETER_ID, frame.ProcessUpdateUIEvent) | |
293 | ||
294 | ||
295 | def ProcessEvent(self, event): | |
296 | id = event.GetId() | |
297 | if id == VIEW_PYTHON_INTERPRETER_ID: | |
298 | self.OnViewPythonInterpreter(event) | |
299 | return True | |
300 | else: | |
301 | return CodeEditor.CodeService.ProcessEvent(self, event) | |
302 | ||
303 | ||
304 | def ProcessUpdateUIEvent(self, event): | |
305 | id = event.GetId() | |
306 | if id == VIEW_PYTHON_INTERPRETER_ID: | |
307 | event.Enable(True) | |
308 | docManager = wx.GetApp().GetDocumentManager() | |
309 | event.Check(False) | |
310 | for doc in docManager.GetDocuments(): | |
311 | if isinstance(doc.GetFirstView(), PythonInterpreterView): | |
312 | event.Check(True) | |
313 | break | |
314 | return True | |
315 | else: | |
316 | return CodeEditor.CodeService.ProcessUpdateUIEvent(self, event) | |
317 | ||
318 | ||
319 | def OnViewPythonInterpreter(self, event): | |
320 | for doc in wx.GetApp().GetDocumentManager().GetDocuments(): | |
321 | if isinstance(doc.GetFirstView(), PythonInterpreterView): | |
322 | doc.GetFirstView().GetDocument().DeleteAllViews() | |
323 | return | |
324 | ||
325 | docManager = self.GetDocumentManager() | |
326 | template = wx.lib.docview.DocTemplate(docManager, | |
327 | _("Python Interpreter"), | |
328 | "*.Foobar", | |
329 | "Foobar", | |
330 | ".Foobar", | |
331 | _("Python Interpreter Document"), | |
332 | _("Python Interpreter View"), | |
333 | wx.lib.docview.Document, | |
334 | PythonInterpreterView, | |
335 | flags = wx.lib.docview.TEMPLATE_INVISIBLE) | |
336 | newDoc = template.CreateDocument('', wx.lib.docview.DOC_SILENT) | |
337 | if newDoc: | |
338 | newDoc.SetDocumentName(template.GetDocumentName()) | |
339 | newDoc.SetDocumentTemplate(template) | |
340 | newDoc.OnNewDocument() | |
341 | newDoc.SetWriteable(False) | |
342 | newDoc.GetFirstView().GetFrame().SetTitle(_("Python Interpreter")) | |
343 | ||
344 | ||
345 | class PythonCtrl(CodeEditor.CodeCtrl): | |
346 | ||
347 | ||
348 | def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE): | |
349 | CodeEditor.CodeCtrl.__init__(self, parent, ID, style) | |
350 | self.SetProperty("tab.timmy.whinge.level", "1") | |
351 | self.SetProperty("fold.comment.python", "1") | |
352 | self.SetProperty("fold.quotes.python", "1") | |
353 | self.SetLexer(wx.stc.STC_LEX_PYTHON) | |
354 | self.SetKeyWords(0, string.join(keyword.kwlist)) | |
355 | ||
356 | ||
357 | def SetViewDefaults(self): | |
358 | CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Python", hasWordWrap = False, hasTabs = True) | |
359 | ||
360 | ||
361 | def GetFontAndColorFromConfig(self): | |
362 | return CodeEditor.CodeCtrl.GetFontAndColorFromConfig(self, configPrefix = "Python") | |
363 | ||
364 | ||
365 | def UpdateStyles(self): | |
366 | CodeEditor.CodeCtrl.UpdateStyles(self) | |
367 | ||
368 | if not self.GetFont(): | |
369 | return | |
370 | ||
371 | faces = { 'font' : self.GetFont().GetFaceName(), | |
372 | 'size' : self.GetFont().GetPointSize(), | |
373 | 'size2': self.GetFont().GetPointSize() - 2, | |
374 | 'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue()) | |
375 | } | |
376 | ||
377 | # Python styles | |
378 | # White space | |
379 | self.StyleSetSpec(wx.stc.STC_P_DEFAULT, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces) | |
380 | # Comment | |
381 | self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces) | |
382 | # Number | |
383 | self.StyleSetSpec(wx.stc.STC_P_NUMBER, "face:%(font)s,fore:#007F7F,size:%(size)d" % faces) | |
384 | # String | |
385 | self.StyleSetSpec(wx.stc.STC_P_STRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces) | |
386 | # Single quoted string | |
387 | self.StyleSetSpec(wx.stc.STC_P_CHARACTER, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces) | |
388 | # Keyword | |
389 | self.StyleSetSpec(wx.stc.STC_P_WORD, "face:%(font)s,fore:#00007F,bold,size:%(size)d" % faces) | |
390 | # Triple quotes | |
391 | self.StyleSetSpec(wx.stc.STC_P_TRIPLE, "face:%(font)s,fore:#7F0000,size:%(size)d" % faces) | |
392 | # Triple double quotes | |
393 | self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, "face:%(font)s,fore:#7F0000,size:%(size)d" % faces) | |
394 | # Class name definition | |
395 | self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, "face:%(font)s,fore:#0000FF,bold,size:%(size)d" % faces) | |
396 | # Function or method name definition | |
397 | self.StyleSetSpec(wx.stc.STC_P_DEFNAME, "face:%(font)s,fore:#007F7F,bold,size:%(size)d" % faces) | |
398 | # Operators | |
399 | self.StyleSetSpec(wx.stc.STC_P_OPERATOR, "face:%(font)s,size:%(size)d" % faces) | |
400 | # Identifiers | |
401 | self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, "face:%(font)s,fore:#%(color)s,face:%(font)s,size:%(size)d" % faces) | |
402 | # Comment-blocks | |
403 | self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, "face:%(font)s,fore:#7F7F7F,size:%(size)d" % faces) | |
404 | # End of line where string is not closed | |
405 | self.StyleSetSpec(wx.stc.STC_P_STRINGEOL, "face:%(font)s,fore:#000000,face:%(font)s,back:#E0C0E0,eol,size:%(size)d" % faces) | |
406 | ||
407 | ||
408 | def OnUpdateUI(self, evt): | |
409 | braces = self.GetMatchingBraces() | |
410 | ||
411 | # check for matching braces | |
412 | braceAtCaret = -1 | |
413 | braceOpposite = -1 | |
414 | charBefore = None | |
415 | caretPos = self.GetCurrentPos() | |
416 | if caretPos > 0: | |
417 | charBefore = self.GetCharAt(caretPos - 1) | |
418 | styleBefore = self.GetStyleAt(caretPos - 1) | |
419 | ||
420 | # check before | |
421 | if charBefore and chr(charBefore) in braces and styleBefore == wx.stc.STC_P_OPERATOR: | |
422 | braceAtCaret = caretPos - 1 | |
423 | ||
424 | # check after | |
425 | if braceAtCaret < 0: | |
426 | charAfter = self.GetCharAt(caretPos) | |
427 | styleAfter = self.GetStyleAt(caretPos) | |
428 | if charAfter and chr(charAfter) in braces and styleAfter == wx.stc.STC_P_OPERATOR: | |
429 | braceAtCaret = caretPos | |
430 | ||
431 | if braceAtCaret >= 0: | |
432 | braceOpposite = self.BraceMatch(braceAtCaret) | |
433 | ||
434 | if braceAtCaret != -1 and braceOpposite == -1: | |
435 | self.BraceBadLight(braceAtCaret) | |
436 | else: | |
437 | self.BraceHighlight(braceAtCaret, braceOpposite) | |
438 | ||
439 | evt.Skip() | |
440 | ||
441 | ||
442 | def DoIndent(self): | |
443 | (text, caretPos) = self.GetCurLine() | |
444 | ||
445 | self._tokenizerChars = {} # This is really too much, need to find something more like a C array | |
446 | for i in range(len(text)): | |
447 | self._tokenizerChars[i] = 0 | |
448 | ||
449 | ctext = cStringIO.StringIO(text) | |
450 | try: | |
451 | tokenize.tokenize(ctext.readline, self) | |
452 | except: | |
453 | pass | |
454 | ||
455 | # Left in for debugging purposes: | |
456 | #for i in range(len(text)): | |
457 | # print i, text[i], self._tokenizerChars[i] | |
458 | ||
459 | if caretPos == 0 or len(string.strip(text)) == 0: # At beginning of line or within an empty line | |
460 | self.AddText('\n') | |
461 | else: | |
462 | doExtraIndent = False | |
463 | brackets = False | |
464 | commentStart = -1 | |
465 | if caretPos > 1: | |
466 | startParenCount = 0 | |
467 | endParenCount = 0 | |
468 | startSquareBracketCount = 0 | |
469 | endSquareBracketCount = 0 | |
470 | startCurlyBracketCount = 0 | |
471 | endCurlyBracketCount = 0 | |
472 | startQuoteCount = 0 | |
473 | endQuoteCount = 0 | |
474 | for i in range(caretPos - 1, -1, -1): # Go through each character before the caret | |
475 | if i >= len(text): # Sometimes the caret is at the end of the text if there is no LF | |
476 | continue | |
477 | if self._tokenizerChars[i] == 1: | |
478 | continue | |
479 | elif self._tokenizerChars[i] == 2: | |
480 | startQuoteCount = startQuoteCount + 1 | |
481 | elif self._tokenizerChars[i] == 3: | |
482 | endQuoteCount = endQuoteCount + 1 | |
483 | elif text[i] == '(': # Would be nice to use a dict for this, but the code is much more readable this way | |
484 | startParenCount = startParenCount + 1 | |
485 | elif text[i] == ')': | |
486 | endParenCount = endParenCount + 1 | |
487 | elif text[i] == "[": | |
488 | startSquareBracketCount = startSquareBracketCount + 1 | |
489 | elif text[i] == "]": | |
490 | endSquareBracketCount = endSquareBracketCount + 1 | |
491 | elif text[i] == "{": | |
492 | startCurlyBracketCount = startCurlyBracketCount + 1 | |
493 | elif text[i] == "}": | |
494 | endCurlyBracketCount = endCurlyBracketCount + 1 | |
495 | elif text[i] == "#": | |
496 | commentStart = i | |
497 | break | |
498 | if startQuoteCount > endQuoteCount or startParenCount > endParenCount or startSquareBracketCount > endSquareBracketCount or startCurlyBracketCount > endCurlyBracketCount: | |
499 | if i + 1 >= caretPos: # Caret is right at the open paren, so just do indent as if colon was there | |
500 | doExtraIndent = True | |
501 | break | |
502 | else: | |
503 | spaces = " " * (i + 1) | |
504 | brackets = True | |
505 | break | |
506 | if not brackets: | |
507 | spaces = text[0:len(text) - len(string.lstrip(text))] | |
508 | if caretPos < len(spaces): # If within the opening spaces of a line | |
509 | spaces = spaces[:caretPos] | |
510 | ||
511 | # strip comment off | |
512 | if commentStart != -1: | |
513 | text = text[0:commentStart] | |
514 | ||
515 | textNoTrailingSpaces = text[0:caretPos].rstrip() | |
516 | if doExtraIndent or len(textNoTrailingSpaces) and textNoTrailingSpaces[-1] == ':': | |
517 | spaces = spaces + ' ' * self.GetIndent() | |
518 | self.AddText('\n' + spaces) | |
519 | ||
520 | ||
521 | # Callback for tokenizer in self.DoIndent | |
522 | def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line): | |
523 | if toktype == tokenize.COMMENT: | |
524 | for i in range(scol, ecol + 1): | |
525 | self._validChars[i] = False | |
526 | elif toktype == token.STRING: | |
527 | self._tokenizerChars[scol] = 2 # Open quote | |
528 | self._tokenizerChars[ecol - 1] = 3 # Close quote | |
529 | for i in range(scol + 1, ecol - 2): | |
530 | self._tokenizerChars[i] = 1 # Part of string, 1 == ignore the char | |
531 | ||
532 | ||
533 | class PythonOptionsPanel(wx.Panel): | |
534 | ||
535 | def __init__(self, parent, id): | |
536 | wx.Panel.__init__(self, parent, id) | |
537 | pathLabel = wx.StaticText(self, -1, _("python.exe Path:")) | |
538 | config = wx.ConfigBase_Get() | |
539 | path = config.Read("ActiveGridPythonLocation") | |
540 | self._pathTextCtrl = wx.TextCtrl(self, -1, path, size = (150, -1)) | |
541 | self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue()) | |
542 | self._pathTextCtrl.SetInsertionPointEnd() | |
543 | choosePathButton = wx.Button(self, -1, _("Browse...")) | |
544 | pathSizer = wx.BoxSizer(wx.HORIZONTAL) | |
545 | HALF_SPACE = 5 | |
546 | pathSizer.Add(pathLabel, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, HALF_SPACE) | |
547 | pathSizer.Add(self._pathTextCtrl, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE) | |
548 | pathSizer.Add(choosePathButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE) | |
549 | wx.EVT_BUTTON(self, choosePathButton.GetId(), self.OnChoosePath) | |
550 | mainSizer = wx.BoxSizer(wx.VERTICAL) | |
551 | mainSizer.Add(pathSizer, 0, wx.LEFT | wx.RIGHT | wx.TOP, 10) | |
552 | ||
553 | self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = False, hasTabs = True, addPage=False) | |
554 | mainSizer.Add(self._otherOptions) | |
555 | self.SetSizer(mainSizer) | |
556 | parent.AddPage(self, _("Python")) | |
557 | ||
558 | def OnChoosePath(self, event): | |
559 | if _WINDOWS: | |
560 | wildcard = _("*.exe") | |
561 | else: | |
562 | wildcard = _("*") | |
563 | path = wx.FileSelector(_("Select a File"), | |
564 | _(""), | |
565 | _(""), | |
566 | wildcard = wildcard , | |
567 | flags = wx.HIDE_READONLY, | |
568 | parent = wx.GetApp().GetTopWindow()) | |
569 | if path: | |
570 | self._pathTextCtrl.SetValue(path) | |
571 | self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue()) | |
572 | self._pathTextCtrl.SetInsertionPointEnd() | |
573 | ||
574 | def OnOK(self, optionsDialog): | |
575 | if len(self._pathTextCtrl.GetValue()) > 0: | |
576 | config = wx.ConfigBase_Get() | |
577 | config.Write("ActiveGridPythonLocation", self._pathTextCtrl.GetValue()) | |
578 | ||
579 | self._otherOptions.OnOK(optionsDialog) | |
580 | #---------------------------------------------------------------------------- | |
581 | # Icon Bitmaps - generated by encode_bitmaps.py | |
582 | #---------------------------------------------------------------------------- | |
583 | from wx import ImageFromStream, BitmapFromImage | |
1f780e48 RD |
584 | import cStringIO |
585 | ||
586 | ||
587 | def getPythonData(): | |
588 | return \ | |
589 | "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
590 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
591 | \x00\x00\xd5IDAT8\x8d\x8d\x93Y\x0e\xc3 \x0cD\x9fM\xcf\xddNr2.\x96\xb8\x1f\ | |
592 | \x05\n\x84.#Y\x10\xa3\x19o\xb1\x99'*\xe2<\x82\x0e\xe6\xc9\xf8\x01\xef?\xa4\ | |
593 | \xf7)]\x05\x970O\xcdr\xce!\x119\xe7\x00\x02\x88\xfe}i\xb5\x848\x8f\xa8\x19\ | |
594 | \xcc\x19}+\xc5\xcc\xd3\x92<CZ\x0b\x99\xc4\xb2N\x01<\x80\xad\xdc?\x88\xf8\x1c\ | |
595 | X\x8f7\xe1\x1f\xdc*\xa9a+\xe1\xa3\xdc\xe7\xb4\xf6\xd1\xe5\xb6'\xc3@\xc5\xa0#\ | |
596 | \xab\x94\xd1\x0bL\xf0\xe6\x17\xa8v\xc3\x8aS\xa0.\x8be\x13\xe3\x15\x8f\xe1\ | |
597 | \xa5D\xee\xc9\xdb~%\xc7y\x84\xbb'sO\xd6\xd4\x17\xe4~\xc4\xf5\xef\xac\xa7\r\ | |
598 | \xbbp?b&\x0f\x89i\x14\x93\xca\x14z\xc5oh\x02E\xc4<\xd92\x03\xe0:B^\xc4K#\xe7\ | |
599 | \xe5\x00\x02\xfd\xb9H\x9ex\x02\x9a\x05a\xd2\xd3c\xc0\xcc\x00\x00\x00\x00IEND\ | |
600 | \xaeB`\x82" | |
601 | ||
602 | ||
603 | def getPythonBitmap(): | |
604 | return BitmapFromImage(getPythonImage()) | |
605 | ||
606 | def getPythonImage(): | |
607 | stream = cStringIO.StringIO(getPythonData()) | |
608 | return ImageFromStream(stream) | |
609 | ||
610 | def getPythonIcon(): | |
bbf7159c | 611 | return wx.IconFromBitmap(getPythonBitmap()) |