]>
Commit | Line | Data |
---|---|---|
1f780e48 RD |
1 | #---------------------------------------------------------------------------- |
2 | # Name: CodeEditor.py | |
3 | # Purpose: Abstract Code Editor for pydocview tbat uses the Styled Text Control | |
4 | # | |
5 | # Author: Peter Yared | |
6 | # | |
7 | # Created: 8/10/03 | |
8 | # CVS-ID: $Id$ | |
9 | # Copyright: (c) 2004-2005 ActiveGrid, Inc. | |
10 | # License: wxWindows License | |
11 | #---------------------------------------------------------------------------- | |
12 | ||
13 | ||
14 | import STCTextEditor | |
15 | import wx | |
16 | import wx.lib.docview | |
17 | import OutlineService | |
18 | import os | |
19 | import re | |
20 | import string | |
21 | import sys | |
1f780e48 | 22 | import MarkerService |
02b800ce | 23 | from UICommon import CaseInsensitiveCompare |
1f780e48 RD |
24 | _ = wx.GetTranslation |
25 | if wx.Platform == '__WXMSW__': | |
26 | _WINDOWS = True | |
27 | else: | |
28 | _WINDOWS = False | |
29 | ||
30 | ||
31 | EXPAND_TEXT_ID = wx.NewId() | |
32 | COLLAPSE_TEXT_ID = wx.NewId() | |
33 | EXPAND_TOP_ID = wx.NewId() | |
34 | COLLAPSE_TOP_ID = wx.NewId() | |
35 | EXPAND_ALL_ID = wx.NewId() | |
36 | COLLAPSE_ALL_ID = wx.NewId() | |
37 | CHECK_CODE_ID = wx.NewId() | |
38 | AUTO_COMPLETE_ID = wx.NewId() | |
39 | CLEAN_WHITESPACE = wx.NewId() | |
40 | COMMENT_LINES_ID = wx.NewId() | |
41 | UNCOMMENT_LINES_ID = wx.NewId() | |
42 | INDENT_LINES_ID = wx.NewId() | |
43 | DEDENT_LINES_ID = wx.NewId() | |
44 | USE_TABS_ID = wx.NewId() | |
45 | SET_INDENT_WIDTH_ID = wx.NewId() | |
46 | FOLDING_ID = wx.NewId() | |
47 | ||
48 | ||
49 | class CodeDocument(STCTextEditor.TextDocument): | |
50 | pass | |
51 | ||
52 | ||
53 | class CodeView(STCTextEditor.TextView): | |
54 | ||
55 | ||
56 | #---------------------------------------------------------------------------- | |
57 | # Overridden methods | |
58 | #---------------------------------------------------------------------------- | |
59 | ||
60 | ||
61 | def GetCtrlClass(self): | |
62 | """ Used in split window to instantiate new instances """ | |
63 | return CodeCtrl | |
64 | ||
65 | ||
66 | def ProcessEvent(self, event): | |
67 | id = event.GetId() | |
68 | if id == EXPAND_TEXT_ID: | |
69 | self.GetCtrl().ToggleFold(self.GetCtrl().GetCurrentLine()) | |
70 | return True | |
71 | elif id == COLLAPSE_TEXT_ID: | |
72 | self.GetCtrl().ToggleFold(self.GetCtrl().GetCurrentLine()) | |
73 | return True | |
74 | elif id == EXPAND_TOP_ID: | |
75 | self.GetCtrl().ToggleFoldAll(expand = True, topLevelOnly = True) | |
76 | return True | |
77 | elif id == COLLAPSE_TOP_ID: | |
78 | self.GetCtrl().ToggleFoldAll(expand = False, topLevelOnly = True) | |
79 | return True | |
80 | elif id == EXPAND_ALL_ID: | |
81 | self.GetCtrl().ToggleFoldAll(expand = True) | |
82 | return True | |
83 | elif id == COLLAPSE_ALL_ID: | |
84 | self.GetCtrl().ToggleFoldAll(expand = False) | |
85 | return True | |
86 | elif id == CHECK_CODE_ID: | |
87 | self.OnCheckCode() | |
88 | return True | |
89 | elif id == AUTO_COMPLETE_ID: | |
90 | self.OnAutoComplete() | |
91 | return True | |
92 | elif id == CLEAN_WHITESPACE: | |
93 | self.OnCleanWhiteSpace() | |
94 | return True | |
95 | elif id == SET_INDENT_WIDTH_ID: | |
96 | self.OnSetIndentWidth() | |
97 | return True | |
98 | elif id == USE_TABS_ID: | |
99 | self.GetCtrl().SetUseTabs(not self.GetCtrl().GetUseTabs()) | |
100 | return True | |
101 | elif id == INDENT_LINES_ID: | |
102 | self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_TAB) | |
103 | return True | |
104 | elif id == DEDENT_LINES_ID: | |
105 | self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_BACKTAB) | |
106 | return True | |
107 | elif id == COMMENT_LINES_ID: | |
108 | self.OnCommentLines() | |
109 | return True | |
110 | elif id == UNCOMMENT_LINES_ID: | |
111 | self.OnUncommentLines() | |
112 | return True | |
113 | else: | |
114 | return STCTextEditor.TextView.ProcessEvent(self, event) | |
115 | ||
116 | ||
117 | def ProcessUpdateUIEvent(self, event): | |
118 | if not self.GetCtrl(): | |
119 | return False | |
1f780e48 RD |
120 | id = event.GetId() |
121 | if id == EXPAND_TEXT_ID: | |
aca310e5 RD |
122 | if self.GetCtrl().GetViewFolding(): |
123 | event.Enable(self.GetCtrl().CanLineExpand(self.GetCtrl().GetCurrentLine())) | |
124 | else: | |
125 | event.Enable(False) | |
1f780e48 RD |
126 | return True |
127 | elif id == COLLAPSE_TEXT_ID: | |
aca310e5 RD |
128 | if self.GetCtrl().GetViewFolding(): |
129 | event.Enable(self.GetCtrl().CanLineCollapse(self.GetCtrl().GetCurrentLine())) | |
130 | else: | |
131 | event.Enable(False) | |
1f780e48 | 132 | return True |
b792147d RD |
133 | elif (id == EXPAND_TOP_ID |
134 | or id == COLLAPSE_TOP_ID | |
135 | or id == EXPAND_ALL_ID | |
aca310e5 RD |
136 | or id == COLLAPSE_ALL_ID): |
137 | if self.GetCtrl().GetViewFolding(): | |
138 | event.Enable(self.GetCtrl().GetTextLength() > 0) | |
139 | else: | |
140 | event.Enable(False) | |
141 | return True | |
142 | elif (id == AUTO_COMPLETE_ID | |
b792147d RD |
143 | or id == CLEAN_WHITESPACE |
144 | or id == INDENT_LINES_ID | |
145 | or id == DEDENT_LINES_ID | |
146 | or id == COMMENT_LINES_ID | |
147 | or id == UNCOMMENT_LINES_ID): | |
148 | event.Enable(self.GetCtrl().GetTextLength() > 0) | |
1f780e48 RD |
149 | return True |
150 | elif id == CHECK_CODE_ID: | |
151 | event.Enable(False) | |
152 | return True | |
aca310e5 | 153 | elif id == SET_INDENT_WIDTH_ID: |
1f780e48 RD |
154 | event.Enable(True) |
155 | return True | |
aca310e5 RD |
156 | elif id == FOLDING_ID: |
157 | event.Enable(self.GetCtrl().GetViewFolding()) | |
158 | return True | |
1f780e48 RD |
159 | elif id == USE_TABS_ID: |
160 | event.Enable(True) | |
161 | event.Check(self.GetCtrl().GetUseTabs()) | |
162 | return True | |
1f780e48 RD |
163 | else: |
164 | return STCTextEditor.TextView.ProcessUpdateUIEvent(self, event) | |
165 | ||
166 | ||
167 | #---------------------------------------------------------------------------- | |
168 | # Methods for OutlineService | |
169 | #---------------------------------------------------------------------------- | |
170 | ||
171 | def OnChangeFilename(self): | |
172 | wx.lib.docview.View.OnChangeFilename(self) | |
173 | self.LoadOutline(force=True) | |
174 | ||
175 | ||
176 | def ClearOutline(self): | |
177 | outlineService = wx.GetApp().GetService(OutlineService.OutlineService) | |
178 | if not outlineService: | |
179 | return | |
180 | ||
181 | outlineView = outlineService.GetView() | |
182 | if not outlineView: | |
183 | return | |
184 | ||
185 | outlineView.ClearTreeCtrl() | |
186 | ||
187 | ||
188 | def LoadOutline(self, force=False): | |
189 | outlineService = wx.GetApp().GetService(OutlineService.OutlineService) | |
190 | if not outlineService: | |
191 | return | |
192 | outlineService.LoadOutline(self, force=force) | |
193 | ||
194 | ||
195 | def DoLoadOutlineCallback(self, force=False): | |
196 | outlineService = wx.GetApp().GetService(OutlineService.OutlineService) | |
197 | if not outlineService: | |
198 | return False | |
199 | ||
200 | outlineView = outlineService.GetView() | |
201 | if not outlineView: | |
202 | return False | |
203 | ||
204 | treeCtrl = outlineView.GetTreeCtrl() | |
205 | if not treeCtrl: | |
206 | return False | |
207 | ||
208 | view = treeCtrl.GetCallbackView() | |
209 | newCheckSum = self.GenCheckSum() | |
210 | if not force: | |
211 | if view and view is self: | |
212 | if self._checkSum == newCheckSum: | |
213 | return False | |
214 | self._checkSum = newCheckSum | |
215 | ||
216 | treeCtrl.DeleteAllItems() | |
217 | ||
218 | document = self.GetDocument() | |
219 | if not document: | |
220 | return True | |
221 | ||
222 | filename = document.GetFilename() | |
223 | if filename: | |
224 | rootItem = treeCtrl.AddRoot(os.path.basename(filename)) | |
aca310e5 | 225 | treeCtrl.SetDoSelectCallback(rootItem, self, (0,0)) |
1f780e48 RD |
226 | else: |
227 | return True | |
228 | ||
229 | text = self.GetValue() | |
230 | if not text: | |
231 | return True | |
232 | ||
233 | CLASS_PATTERN = 'class[ \t]+\w+.*?:' | |
234 | DEF_PATTERN = 'def[ \t]+\w+\(.*?\)' | |
235 | classPat = re.compile(CLASS_PATTERN, re.M|re.S) | |
236 | defPat= re.compile(DEF_PATTERN, re.M|re.S) | |
237 | pattern = re.compile('^[ \t]*((' + CLASS_PATTERN + ')|('+ DEF_PATTERN +'.*?:)).*?$', re.M|re.S) | |
238 | ||
239 | iter = pattern.finditer(text) | |
240 | indentStack = [(0, rootItem)] | |
241 | for pattern in iter: | |
242 | line = pattern.string[pattern.start(0):pattern.end(0)] | |
243 | classLine = classPat.search(line) | |
244 | if classLine: | |
245 | indent = classLine.start(0) | |
246 | itemStr = classLine.string[classLine.start(0):classLine.end(0)-1] # don't take the closing ':' | |
aca310e5 | 247 | itemStr = itemStr.replace("\n", "").replace("\r", "").replace(",\\", ",").replace(" ", "") # remove line continuations and spaces from outline view |
1f780e48 RD |
248 | else: |
249 | defLine = defPat.search(line) | |
250 | if defLine: | |
251 | indent = defLine.start(0) | |
252 | itemStr = defLine.string[defLine.start(0):defLine.end(0)] | |
aca310e5 | 253 | itemStr = itemStr.replace("\n", "").replace("\r", "").replace(",\\", ",").replace(" ", "") # remove line continuations and spaces from outline view |
1f780e48 RD |
254 | |
255 | if indent == 0: | |
256 | parentItem = rootItem | |
257 | else: | |
258 | lastItem = indentStack.pop() | |
259 | while lastItem[0] >= indent: | |
260 | lastItem = indentStack.pop() | |
261 | indentStack.append(lastItem) | |
262 | parentItem = lastItem[1] | |
263 | ||
264 | item = treeCtrl.AppendItem(parentItem, itemStr) | |
265 | treeCtrl.SetDoSelectCallback(item, self, (pattern.end(0), pattern.start(0) + indent)) # select in reverse order because we want the cursor to be at the start of the line so it wouldn't scroll to the right | |
266 | indentStack.append((indent, item)) | |
267 | ||
268 | treeCtrl.Expand(rootItem) | |
269 | ||
270 | return True | |
271 | ||
272 | ||
273 | def DoSelectCallback(self, data): | |
274 | if data: | |
275 | self.EnsureVisibleEnforcePolicy(self.LineFromPosition(data[0])) | |
276 | # wxBug: need to select in reverse order (end, start) to place cursor at begining of line, | |
277 | # otherwise, display is scrolled over to the right hard and is hard to view | |
278 | self.SetSelection(data[1], data[0]) | |
279 | ||
280 | ||
281 | ## def checksum(self, bytes): | |
282 | ## def rotate_right(c): | |
283 | ## if c&1: | |
284 | ## return (c>>1)|0x8000 | |
285 | ## else: | |
286 | ## return c>>1 | |
287 | ## | |
288 | ## result = 0 | |
289 | ## for ch in bytes: | |
290 | ## ch = ord(ch) & 0xFF | |
291 | ## result = (rotate_right(result)+ch) & 0xFFFF | |
292 | ## return result | |
293 | ## | |
294 | ||
295 | def GenCheckSum(self): | |
296 | """ Poor man's checksum. We'll assume most changes will change the length of the file. | |
297 | """ | |
298 | text = self.GetValue() | |
299 | if text: | |
300 | return len(text) | |
301 | else: | |
302 | return 0 | |
303 | ||
304 | ||
305 | #---------------------------------------------------------------------------- | |
306 | # Format methods | |
307 | #---------------------------------------------------------------------------- | |
308 | ||
309 | def OnCheckCode(self): | |
310 | """ Need to overshadow this for each specific subclass """ | |
311 | if 0: | |
312 | try: | |
313 | code = self.GetCtrl().GetText() | |
314 | codeObj = compile(code, self.GetDocument().GetFilename(), 'exec') | |
315 | self._GetParentFrame().SetStatusText(_("The file successfully compiled")) | |
316 | except SyntaxError, (message, (fileName, line, col, text)): | |
317 | pos = self.GetCtrl().PositionFromLine(line - 1) + col - 1 | |
318 | self.GetCtrl().SetSelection(pos, pos) | |
319 | self._GetParentFrame().SetStatusText(_("Syntax Error: %s") % message) | |
320 | except: | |
bbf7159c | 321 | self._GetParentFrame().SetStatusText("%s: %s" % (sys.exc_info()[0], sys.exc_info()[1])) |
1f780e48 RD |
322 | |
323 | ||
324 | def OnAutoComplete(self): | |
325 | self.GetCtrl().AutoCompCancel() | |
326 | self.GetCtrl().AutoCompSetAutoHide(0) | |
327 | self.GetCtrl().AutoCompSetChooseSingle(True) | |
328 | self.GetCtrl().AutoCompSetIgnoreCase(True) | |
329 | context, hint = self.GetAutoCompleteHint() | |
330 | replaceList, replaceLen = self.GetAutoCompleteKeywordList(context, hint) | |
331 | if replaceList and len(replaceList) != 0: | |
332 | self.GetCtrl().AutoCompShow(replaceLen, replaceList) | |
333 | ||
334 | ||
335 | def GetAutoCompleteHint(self): | |
336 | """ Replace this method with Editor specific method """ | |
337 | pos = self.GetCtrl().GetCurrentPos() | |
338 | if pos == 0: | |
339 | return None, None | |
340 | if chr(self.GetCtrl().GetCharAt(pos - 1)) == '.': | |
341 | pos = pos - 1 | |
342 | hint = None | |
343 | else: | |
344 | hint = '' | |
345 | ||
346 | validLetters = string.letters + string.digits + '_.' | |
347 | word = '' | |
348 | while (True): | |
349 | pos = pos - 1 | |
350 | if pos < 0: | |
351 | break | |
352 | char = chr(self.GetCtrl().GetCharAt(pos)) | |
353 | if char not in validLetters: | |
354 | break | |
355 | word = char + word | |
356 | ||
357 | context = word | |
358 | if hint is not None: | |
359 | lastDot = word.rfind('.') | |
360 | if lastDot != -1: | |
361 | context = word[0:lastDot] | |
362 | hint = word[lastDot+1:] | |
363 | ||
364 | return context, hint | |
365 | ||
366 | ||
367 | def GetAutoCompleteDefaultKeywords(self): | |
368 | """ Replace this method with Editor specific keywords """ | |
369 | return ['Put', 'Editor Specific', 'Keywords', 'Here'] | |
370 | ||
371 | ||
1f780e48 RD |
372 | def GetAutoCompleteKeywordList(self, context, hint): |
373 | """ Replace this method with Editor specific keywords """ | |
374 | kw = self.GetAutoCompleteDefaultKeywords() | |
375 | ||
376 | if hint and len(hint): | |
377 | lowerHint = hint.lower() | |
378 | filterkw = filter(lambda item: item.lower().startswith(lowerHint), kw) # remove variables and methods that don't match hint | |
379 | kw = filterkw | |
380 | ||
381 | if hint: | |
382 | replaceLen = len(hint) | |
383 | else: | |
384 | replaceLen = 0 | |
385 | ||
02b800ce | 386 | kw.sort(CaseInsensitiveCompare) |
1f780e48 RD |
387 | return " ".join(kw), replaceLen |
388 | ||
389 | ||
390 | def OnCleanWhiteSpace(self): | |
391 | newText = "" | |
392 | for lineNo in self._GetSelectedLineNumbers(): | |
393 | lineText = string.rstrip(self.GetCtrl().GetLine(lineNo)) | |
394 | indent = 0 | |
395 | lstrip = 0 | |
396 | for char in lineText: | |
397 | if char == '\t': | |
398 | indent = indent + self.GetCtrl().GetIndent() | |
399 | lstrip = lstrip + 1 | |
400 | elif char in string.whitespace: | |
401 | indent = indent + 1 | |
402 | lstrip = lstrip + 1 | |
403 | else: | |
404 | break | |
405 | if self.GetCtrl().GetUseTabs(): | |
406 | indentText = (indent / self.GetCtrl().GetIndent()) * '\t' + (indent % self.GetCtrl().GetIndent()) * ' ' | |
407 | else: | |
408 | indentText = indent * ' ' | |
409 | lineText = indentText + lineText[lstrip:] + '\n' | |
410 | newText = newText + lineText | |
411 | self._ReplaceSelectedLines(newText) | |
412 | ||
413 | ||
414 | def OnSetIndentWidth(self): | |
415 | dialog = wx.TextEntryDialog(self._GetParentFrame(), _("Enter new indent width (2-10):"), _("Set Indent Width"), "%i" % self.GetCtrl().GetIndent()) | |
02b800ce | 416 | dialog.CenterOnParent() |
1f780e48 RD |
417 | if dialog.ShowModal() == wx.ID_OK: |
418 | try: | |
419 | indent = int(dialog.GetValue()) | |
420 | if indent >= 2 and indent <= 10: | |
421 | self.GetCtrl().SetIndent(indent) | |
422 | self.GetCtrl().SetTabWidth(indent) | |
423 | except: | |
424 | pass | |
425 | dialog.Destroy() | |
426 | ||
427 | ||
428 | def GetIndentWidth(self): | |
429 | return self.GetCtrl().GetIndent() | |
430 | ||
431 | ||
432 | def OnCommentLines(self): | |
433 | newText = "" | |
434 | for lineNo in self._GetSelectedLineNumbers(): | |
435 | lineText = self.GetCtrl().GetLine(lineNo) | |
436 | if (len(lineText) > 1 and lineText[0] == '#') or (len(lineText) > 2 and lineText[:2] == '##'): | |
437 | newText = newText + lineText | |
438 | else: | |
439 | newText = newText + "##" + lineText | |
440 | self._ReplaceSelectedLines(newText) | |
441 | ||
442 | ||
443 | def OnUncommentLines(self): | |
444 | newText = "" | |
445 | for lineNo in self._GetSelectedLineNumbers(): | |
446 | lineText = self.GetCtrl().GetLine(lineNo) | |
447 | if len(lineText) >= 2 and lineText[:2] == "##": | |
448 | lineText = lineText[2:] | |
449 | elif len(lineText) >= 1 and lineText[:1] == "#": | |
450 | lineText = lineText[1:] | |
451 | newText = newText + lineText | |
452 | self._ReplaceSelectedLines(newText) | |
453 | ||
454 | ||
455 | def _GetSelectedLineNumbers(self): | |
456 | selStart, selEnd = self._GetPositionsBoundingSelectedLines() | |
457 | return range(self.GetCtrl().LineFromPosition(selStart), self.GetCtrl().LineFromPosition(selEnd)) | |
458 | ||
459 | ||
460 | def _GetPositionsBoundingSelectedLines(self): | |
461 | startPos = self.GetCtrl().GetCurrentPos() | |
462 | endPos = self.GetCtrl().GetAnchor() | |
463 | if startPos > endPos: | |
464 | temp = endPos | |
465 | endPos = startPos | |
466 | startPos = temp | |
467 | if endPos == self.GetCtrl().PositionFromLine(self.GetCtrl().LineFromPosition(endPos)): | |
468 | endPos = endPos - 1 # If it's at the very beginning of a line, use the line above it as the ending line | |
469 | selStart = self.GetCtrl().PositionFromLine(self.GetCtrl().LineFromPosition(startPos)) | |
470 | selEnd = self.GetCtrl().PositionFromLine(self.GetCtrl().LineFromPosition(endPos) + 1) | |
471 | return selStart, selEnd | |
472 | ||
473 | ||
474 | def _ReplaceSelectedLines(self, text): | |
475 | if len(text) == 0: | |
476 | return | |
477 | selStart, selEnd = self._GetPositionsBoundingSelectedLines() | |
478 | self.GetCtrl().SetSelection(selStart, selEnd) | |
479 | self.GetCtrl().ReplaceSelection(text) | |
480 | self.GetCtrl().SetSelection(selStart + len(text), selStart) | |
481 | ||
482 | ||
483 | def OnUpdate(self, sender = None, hint = None): | |
aca310e5 RD |
484 | if wx.lib.docview.View.OnUpdate(self, sender, hint): |
485 | return | |
486 | ||
1f780e48 RD |
487 | if hint == "ViewStuff": |
488 | self.GetCtrl().SetViewDefaults() | |
489 | elif hint == "Font": | |
02b800ce | 490 | font, color = self.GetCtrl().GetFontAndColorFromConfig() |
1f780e48 RD |
491 | self.GetCtrl().SetFont(font) |
492 | self.GetCtrl().SetFontColor(color) | |
493 | else: | |
aca310e5 | 494 | import DebuggerService |
1f780e48 RD |
495 | dbg_service = wx.GetApp().GetService(DebuggerService.DebuggerService) |
496 | if dbg_service: | |
497 | dbg_service.SetCurrentBreakpointMarkers(self) | |
498 | ||
499 | ||
500 | class CodeService(STCTextEditor.TextService): | |
501 | ||
502 | ||
503 | def __init__(self): | |
504 | STCTextEditor.TextService.__init__(self) | |
505 | ||
506 | ||
507 | def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None): | |
508 | # TODO NEED TO DO INSTANCEOF CHECK HERE FOR SDI | |
509 | #if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument: | |
510 | # return | |
511 | if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI: | |
512 | return | |
513 | ||
514 | viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View"))) | |
515 | isWindows = (wx.Platform == '__WXMSW__') | |
516 | ||
517 | if not menuBar.FindItemById(EXPAND_TEXT_ID): # check if below menu items have been already been installed | |
518 | foldingMenu = wx.Menu() | |
519 | if isWindows: | |
520 | foldingMenu.Append(EXPAND_TEXT_ID, _("&Expand\tNumpad-Plus"), _("Expands a collapsed block of text")) | |
521 | else: | |
522 | foldingMenu.Append(EXPAND_TEXT_ID, _("&Expand"), _("Expands a collapsed block of text")) | |
523 | ||
524 | wx.EVT_MENU(frame, EXPAND_TEXT_ID, frame.ProcessEvent) | |
525 | wx.EVT_UPDATE_UI(frame, EXPAND_TEXT_ID, frame.ProcessUpdateUIEvent) | |
526 | ||
527 | if isWindows: | |
528 | foldingMenu.Append(COLLAPSE_TEXT_ID, _("&Collapse\tNumpad+Minus"), _("Collapse a block of text")) | |
529 | else: | |
530 | foldingMenu.Append(COLLAPSE_TEXT_ID, _("&Collapse"), _("Collapse a block of text")) | |
531 | wx.EVT_MENU(frame, COLLAPSE_TEXT_ID, frame.ProcessEvent) | |
532 | wx.EVT_UPDATE_UI(frame, COLLAPSE_TEXT_ID, frame.ProcessUpdateUIEvent) | |
533 | ||
534 | if isWindows: | |
535 | foldingMenu.Append(EXPAND_TOP_ID, _("Expand &Top Level\tCtrl+Numpad+Plus"), _("Expands the top fold levels in the document")) | |
536 | else: | |
537 | foldingMenu.Append(EXPAND_TOP_ID, _("Expand &Top Level"), _("Expands the top fold levels in the document")) | |
538 | wx.EVT_MENU(frame, EXPAND_TOP_ID, frame.ProcessEvent) | |
539 | wx.EVT_UPDATE_UI(frame, EXPAND_TOP_ID, frame.ProcessUpdateUIEvent) | |
540 | ||
541 | if isWindows: | |
542 | foldingMenu.Append(COLLAPSE_TOP_ID, _("Collapse Top &Level\tCtrl+Numpad+Minus"), _("Collapses the top fold levels in the document")) | |
543 | else: | |
544 | foldingMenu.Append(COLLAPSE_TOP_ID, _("Collapse Top &Level"), _("Collapses the top fold levels in the document")) | |
545 | wx.EVT_MENU(frame, COLLAPSE_TOP_ID, frame.ProcessEvent) | |
546 | wx.EVT_UPDATE_UI(frame, COLLAPSE_TOP_ID, frame.ProcessUpdateUIEvent) | |
547 | ||
548 | if isWindows: | |
549 | foldingMenu.Append(EXPAND_ALL_ID, _("Expand &All\tShift+Numpad+Plus"), _("Expands all of the fold levels in the document")) | |
550 | else: | |
551 | foldingMenu.Append(EXPAND_ALL_ID, _("Expand &All"), _("Expands all of the fold levels in the document")) | |
552 | wx.EVT_MENU(frame, EXPAND_ALL_ID, frame.ProcessEvent) | |
553 | wx.EVT_UPDATE_UI(frame, EXPAND_ALL_ID, frame.ProcessUpdateUIEvent) | |
554 | ||
555 | if isWindows: | |
556 | foldingMenu.Append(COLLAPSE_ALL_ID, _("Colla&pse All\tShift+Numpad+Minus"), _("Collapses all of the fold levels in the document")) | |
557 | else: | |
558 | foldingMenu.Append(COLLAPSE_ALL_ID, _("Colla&pse All"), _("Collapses all of the fold levels in the document")) | |
559 | wx.EVT_MENU(frame, COLLAPSE_ALL_ID, frame.ProcessEvent) | |
560 | wx.EVT_UPDATE_UI(frame, COLLAPSE_ALL_ID, frame.ProcessUpdateUIEvent) | |
561 | ||
562 | viewMenu.AppendMenu(FOLDING_ID, _("&Folding"), foldingMenu) | |
563 | wx.EVT_UPDATE_UI(frame, FOLDING_ID, frame.ProcessUpdateUIEvent) | |
564 | ||
565 | formatMenuIndex = menuBar.FindMenu(_("&Format")) | |
566 | if formatMenuIndex > -1: | |
567 | formatMenu = menuBar.GetMenu(formatMenuIndex) | |
568 | else: | |
569 | formatMenu = wx.Menu() | |
570 | if not menuBar.FindItemById(CHECK_CODE_ID): # check if below menu items have been already been installed | |
571 | formatMenu.AppendSeparator() | |
572 | formatMenu.Append(CHECK_CODE_ID, _("&Check Code"), _("Checks the document for syntax and indentation errors")) | |
573 | wx.EVT_MENU(frame, CHECK_CODE_ID, frame.ProcessEvent) | |
574 | wx.EVT_UPDATE_UI(frame, CHECK_CODE_ID, frame.ProcessUpdateUIEvent) | |
575 | formatMenu.Append(AUTO_COMPLETE_ID, _("&Auto Complete\tCtrl+Space"), _("Provides suggestions on how to complete the current statement")) | |
576 | wx.EVT_MENU(frame, AUTO_COMPLETE_ID, frame.ProcessEvent) | |
577 | wx.EVT_UPDATE_UI(frame, AUTO_COMPLETE_ID, frame.ProcessUpdateUIEvent) | |
578 | formatMenu.Append(CLEAN_WHITESPACE, _("Clean &Whitespace"), _("Converts leading spaces to tabs or vice versa per 'use tabs' and clears trailing spaces")) | |
579 | wx.EVT_MENU(frame, CLEAN_WHITESPACE, frame.ProcessEvent) | |
580 | wx.EVT_UPDATE_UI(frame, CLEAN_WHITESPACE, frame.ProcessUpdateUIEvent) | |
581 | formatMenu.AppendSeparator() | |
582 | formatMenu.Append(INDENT_LINES_ID, _("&Indent Lines\tTab"), _("Indents the selected lines one indent width")) | |
583 | wx.EVT_MENU(frame, INDENT_LINES_ID, frame.ProcessEvent) | |
584 | wx.EVT_UPDATE_UI(frame, INDENT_LINES_ID, frame.ProcessUpdateUIEvent) | |
585 | formatMenu.Append(DEDENT_LINES_ID, _("&Dedent Lines\tShift+Tab"), _("Dedents the selected lines one indent width")) | |
586 | wx.EVT_MENU(frame, DEDENT_LINES_ID, frame.ProcessEvent) | |
587 | wx.EVT_UPDATE_UI(frame, DEDENT_LINES_ID, frame.ProcessUpdateUIEvent) | |
588 | formatMenu.Append(COMMENT_LINES_ID, _("Comment &Lines\tCtrl+Q"), _("Comments out the selected lines be prefixing each one with a comment indicator")) | |
589 | wx.EVT_MENU(frame, COMMENT_LINES_ID, frame.ProcessEvent) | |
590 | wx.EVT_UPDATE_UI(frame, COMMENT_LINES_ID, frame.ProcessUpdateUIEvent) | |
591 | formatMenu.Append(UNCOMMENT_LINES_ID, _("&Uncomment Lines\tCtrl+Shift+Q"), _("Removes comment prefixes from each of the selected lines")) | |
592 | wx.EVT_MENU(frame, UNCOMMENT_LINES_ID, frame.ProcessEvent) | |
593 | wx.EVT_UPDATE_UI(frame, UNCOMMENT_LINES_ID, frame.ProcessUpdateUIEvent) | |
594 | formatMenu.AppendSeparator() | |
595 | formatMenu.AppendCheckItem(USE_TABS_ID, _("Use &Tabs"), _("Toggles use of tabs or whitespaces for indents")) | |
596 | wx.EVT_MENU(frame, USE_TABS_ID, frame.ProcessEvent) | |
597 | wx.EVT_UPDATE_UI(frame, USE_TABS_ID, frame.ProcessUpdateUIEvent) | |
598 | formatMenu.Append(SET_INDENT_WIDTH_ID, _("&Set Indent Width..."), _("Sets the indent width")) | |
599 | wx.EVT_MENU(frame, SET_INDENT_WIDTH_ID, frame.ProcessEvent) | |
600 | wx.EVT_UPDATE_UI(frame, SET_INDENT_WIDTH_ID, frame.ProcessUpdateUIEvent) | |
601 | if formatMenuIndex == -1: | |
602 | viewMenuIndex = menuBar.FindMenu(_("&View")) | |
603 | menuBar.Insert(viewMenuIndex + 1, formatMenu, _("&Format")) | |
604 | ||
605 | ## accelTable = wx.AcceleratorTable([ | |
606 | ## (wx.ACCEL_NORMAL, wx.WXK_TAB, INDENT_LINES_ID), | |
607 | ## (wx.ACCEL_SHIFT, wx.WXK_TAB, DEDENT_LINES_ID), | |
608 | ## eval(_("wx.ACCEL_CTRL, ord('Q'), COMMENT_LINES_ID")), | |
609 | ## eval(_("wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord('Q'), UNCOMMENT_LINES_ID")) | |
610 | ## ]) | |
611 | ## frame.SetAcceleratorTable(accelTable) | |
612 | ||
613 | def ProcessUpdateUIEvent(self, event): | |
614 | id = event.GetId() | |
b792147d RD |
615 | if (id == EXPAND_TEXT_ID |
616 | or id == COLLAPSE_TEXT_ID | |
617 | or id == EXPAND_TOP_ID | |
618 | or id == COLLAPSE_TOP_ID | |
619 | or id == EXPAND_ALL_ID | |
620 | or id == COLLAPSE_ALL_ID | |
621 | or id == CHECK_CODE_ID | |
622 | or id == AUTO_COMPLETE_ID | |
623 | or id == CLEAN_WHITESPACE | |
624 | or id == SET_INDENT_WIDTH_ID | |
625 | or id == USE_TABS_ID | |
626 | or id == INDENT_LINES_ID | |
627 | or id == DEDENT_LINES_ID | |
628 | or id == COMMENT_LINES_ID | |
629 | or id == UNCOMMENT_LINES_ID | |
630 | or id == FOLDING_ID): | |
1f780e48 RD |
631 | event.Enable(False) |
632 | return True | |
633 | else: | |
634 | return STCTextEditor.TextService.ProcessUpdateUIEvent(self, event) | |
635 | ||
636 | ||
637 | class CodeCtrl(STCTextEditor.TextCtrl): | |
638 | CURRENT_LINE_MARKER_NUM = 2 | |
639 | BREAKPOINT_MARKER_NUM = 1 | |
640 | CURRENT_LINE_MARKER_MASK = 0x4 | |
641 | BREAKPOINT_MARKER_MASK = 0x2 | |
642 | ||
643 | ||
aca310e5 | 644 | def __init__(self, parent, id=-1, style = wx.NO_FULL_REPAINT_ON_RESIZE, clearTab=True): |
26ee3a06 | 645 | STCTextEditor.TextCtrl.__init__(self, parent, id, style) |
1f780e48 RD |
646 | |
647 | self.UsePopUp(False) | |
648 | self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) | |
649 | self.SetProperty("fold", "1") | |
650 | ||
651 | # Setup a margin to hold fold markers | |
652 | #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER? | |
653 | self.SetMarginType(2, wx.stc.STC_MARGIN_SYMBOL) | |
654 | self.SetMarginMask(2, wx.stc.STC_MASK_FOLDERS) | |
655 | self.SetMarginSensitive(2, True) | |
1f780e48 RD |
656 | |
657 | self.SetMarginSensitive(1, False) | |
658 | self.SetMarginMask(1, 0x4) | |
659 | ||
660 | self.SetMarginSensitive(0, True) | |
661 | self.SetMarginType(0, wx.stc.STC_MARGIN_SYMBOL) | |
662 | self.SetMarginMask(0, 0x3) | |
663 | self.SetMarginWidth(0, 12) | |
664 | ||
665 | self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEREND, wx.stc.STC_MARK_BOXPLUSCONNECTED, "white", "black") | |
666 | self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPENMID, wx.stc.STC_MARK_BOXMINUSCONNECTED, "white", "black") | |
667 | self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERMIDTAIL, wx.stc.STC_MARK_TCORNER, "white", "black") | |
668 | self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERTAIL, wx.stc.STC_MARK_LCORNER, "white", "black") | |
669 | self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERSUB, wx.stc.STC_MARK_VLINE, "white", "black") | |
670 | self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDER, wx.stc.STC_MARK_BOXPLUS, "white", "black") | |
671 | self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPEN, wx.stc.STC_MARK_BOXMINUS, "white", "black") | |
672 | # Define the current line marker | |
673 | self.MarkerDefine(CodeCtrl.CURRENT_LINE_MARKER_NUM, wx.stc.STC_MARK_SHORTARROW, wx.BLACK, (255,255,128)) | |
674 | # Define the breakpoint marker | |
675 | self.MarkerDefine(CodeCtrl.BREAKPOINT_MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, (255,0,0)) | |
676 | ||
aca310e5 | 677 | if _WINDOWS and clearTab: # should test to see if menu item exists, if it does, add this workaround |
1f780e48 RD |
678 | self.CmdKeyClear(wx.stc.STC_KEY_TAB, 0) # menu item "Indent Lines" from CodeService.InstallControls() generates another INDENT_LINES_ID event, so we'll explicitly disable the tab processing in the editor |
679 | ||
26ee3a06 | 680 | wx.stc.EVT_STC_MARGINCLICK(self, self.GetId(), self.OnMarginClick) |
1f780e48 RD |
681 | wx.EVT_KEY_DOWN(self, self.OnKeyPressed) |
682 | if self.GetMatchingBraces(): | |
26ee3a06 | 683 | wx.stc.EVT_STC_UPDATEUI(self, self.GetId(), self.OnUpdateUI) |
1f780e48 RD |
684 | |
685 | self.StyleClearAll() | |
686 | self.UpdateStyles() | |
687 | ||
688 | ||
689 | def OnRightUp(self, event): | |
690 | #Hold onto the current line number, no way to get it later. | |
691 | self._rightClickPosition = self.PositionFromPoint(event.GetPosition()) | |
692 | self._rightClickLine = self.LineFromPosition(self._rightClickPosition) | |
693 | self.PopupMenu(self.CreatePopupMenu(), event.GetPosition()) | |
694 | self._rightClickLine = -1 | |
695 | self._rightClickPosition = -1 | |
696 | ||
697 | ||
698 | def CreatePopupMenu(self): | |
699 | TOGGLEBREAKPOINT_ID = wx.NewId() | |
700 | TOGGLEMARKER_ID = wx.NewId() | |
701 | SYNCTREE_ID = wx.NewId() | |
702 | ||
703 | menu = wx.Menu() | |
704 | ||
705 | self.Bind(wx.EVT_MENU, self.OnPopSyncOutline, id=SYNCTREE_ID) | |
706 | item = wx.MenuItem(menu, SYNCTREE_ID, _("Find in Outline View")) | |
707 | menu.AppendItem(item) | |
708 | menu.AppendSeparator() | |
709 | self.Bind(wx.EVT_MENU, self.OnPopToggleBP, id=TOGGLEBREAKPOINT_ID) | |
710 | item = wx.MenuItem(menu, TOGGLEBREAKPOINT_ID, _("Toggle Breakpoint")) | |
711 | menu.AppendItem(item) | |
712 | self.Bind(wx.EVT_MENU, self.OnPopToggleMarker, id=TOGGLEMARKER_ID) | |
aca310e5 | 713 | item = wx.MenuItem(menu, TOGGLEMARKER_ID, _("Toggle Bookmark")) |
1f780e48 RD |
714 | menu.AppendItem(item) |
715 | menu.AppendSeparator() | |
716 | ||
717 | itemIDs = [wx.ID_UNDO, wx.ID_REDO, None, | |
718 | wx.ID_CUT, wx.ID_COPY, wx.ID_PASTE, wx.ID_CLEAR, None, wx.ID_SELECTALL] | |
719 | ||
720 | menuBar = wx.GetApp().GetTopWindow().GetMenuBar() | |
721 | for itemID in itemIDs: | |
722 | if not itemID: | |
723 | menu.AppendSeparator() | |
724 | else: | |
725 | item = menuBar.FindItemById(itemID) | |
726 | if item: | |
727 | menu.Append(itemID, item.GetLabel()) | |
26ee3a06 RD |
728 | wx.EVT_MENU(self, itemID, self.DSProcessEvent) # wxHack: for customized right mouse menu doesn't work with new DynamicSashWindow |
729 | wx.EVT_UPDATE_UI(self, itemID, self.DSProcessUpdateUIEvent) # wxHack: for customized right mouse menu doesn't work with new DynamicSashWindow | |
1f780e48 RD |
730 | return menu |
731 | ||
732 | ||
733 | def OnPopToggleBP(self, event): | |
734 | """ Toggle break point on right click line, not current line """ | |
aca310e5 | 735 | import DebuggerService |
1f780e48 RD |
736 | wx.GetApp().GetService(DebuggerService.DebuggerService).OnToggleBreakpoint(event, line=self._rightClickLine) |
737 | ||
738 | ||
739 | def OnPopToggleMarker(self, event): | |
740 | """ Toggle marker on right click line, not current line """ | |
741 | wx.GetApp().GetDocumentManager().GetCurrentView().MarkerToggle(lineNum = self._rightClickLine) | |
742 | ||
743 | ||
744 | def OnPopSyncOutline(self, event): | |
745 | wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(wx.GetApp().GetDocumentManager().GetCurrentView(), position=self._rightClickPosition) | |
746 | ||
747 | ||
748 | def HasSelection(self): | |
749 | return self.GetSelectionStart() - self.GetSelectionEnd() != 0 | |
750 | ||
751 | ||
752 | def ClearCurrentLineMarkers(self): | |
753 | self.MarkerDeleteAll(CodeCtrl.CURRENT_LINE_MARKER_NUM) | |
754 | ||
755 | ||
756 | def ClearCurrentBreakpoinMarkers(self): | |
757 | self.MarkerDeleteAll(CodeCtrl.BREAKPOINT_MARKER_NUM) | |
758 | ||
759 | ||
760 | def GetDefaultFont(self): | |
761 | if wx.Platform == '__WXMSW__': | |
762 | font = "Courier New" | |
763 | else: | |
764 | font = "Courier" | |
765 | return wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font) | |
766 | ||
767 | ||
768 | def GetMatchingBraces(self): | |
769 | """ Overwrite this method for language specific braces """ | |
770 | return "[]{}()" | |
771 | ||
772 | ||
773 | def CanWordWrap(self): | |
774 | return False | |
775 | ||
776 | ||
777 | def SetFont(self, font): | |
778 | self._font = font | |
779 | ||
780 | ||
781 | def SetFontColor(self, fontColor): | |
782 | self._fontColor = fontColor | |
783 | ||
784 | ||
785 | def UpdateStyles(self): | |
786 | ||
787 | if not self.GetFont(): | |
788 | return | |
789 | ||
790 | faces = { 'font' : self.GetFont().GetFaceName(), | |
791 | 'size' : self.GetFont().GetPointSize(), | |
792 | 'size2': self.GetFont().GetPointSize() - 2, | |
793 | 'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue()) | |
794 | } | |
795 | ||
796 | # Global default styles for all languages | |
797 | self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(font)s,fore:#FFFFFF,size:%(size)d" % faces) | |
798 | self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER, "face:%(font)s,back:#C0C0C0,face:%(font)s,size:%(size2)d" % faces) | |
799 | self.StyleSetSpec(wx.stc.STC_STYLE_CONTROLCHAR, "face:%(font)s" % faces) | |
800 | self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT, "face:%(font)s,fore:#000000,back:#70FFFF,size:%(size)d" % faces) | |
801 | self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD, "face:%(font)s,fore:#000000,back:#FF0000,size:%(size)d" % faces) | |
802 | ||
803 | ||
804 | def OnKeyPressed(self, event): | |
805 | if self.CallTipActive(): | |
806 | self.CallTipCancel() | |
807 | key = event.KeyCode() | |
808 | if False: # key == wx.WXK_SPACE and event.ControlDown(): | |
809 | pos = self.GetCurrentPos() | |
810 | # Tips | |
811 | if event.ShiftDown(): | |
812 | self.CallTipSetBackground("yellow") | |
813 | self.CallTipShow(pos, 'param1, param2') | |
814 | # Code completion | |
815 | else: | |
816 | #lst = [] | |
817 | #for x in range(50000): | |
818 | # lst.append('%05d' % x) | |
819 | #st = string.join(lst) | |
820 | #print len(st) | |
821 | #self.AutoCompShow(0, st) | |
822 | ||
823 | kw = keyword.kwlist[:] | |
824 | kw.append("zzzzzz") | |
825 | kw.append("aaaaa") | |
826 | kw.append("__init__") | |
827 | kw.append("zzaaaaa") | |
828 | kw.append("zzbaaaa") | |
829 | kw.append("this_is_a_longer_value") | |
830 | kw.append("this_is_a_much_much_much_much_much_much_much_longer_value") | |
831 | ||
832 | kw.sort() # Python sorts are case sensitive | |
833 | self.AutoCompSetIgnoreCase(False) # so this needs to match | |
834 | ||
835 | self.AutoCompShow(0, string.join(kw)) | |
836 | elif key == wx.WXK_RETURN: | |
837 | self.DoIndent() | |
838 | else: | |
839 | STCTextEditor.TextCtrl.OnKeyPressed(self, event) | |
840 | ||
841 | ||
842 | def DoIndent(self): | |
843 | self.AddText('\n') | |
26ee3a06 | 844 | self.EnsureCaretVisible() |
1f780e48 RD |
845 | # Need to do a default one for all languges |
846 | ||
847 | ||
848 | def OnMarginClick(self, evt): | |
849 | # fold and unfold as needed | |
850 | if evt.GetMargin() == 2: | |
851 | if evt.GetShift() and evt.GetControl(): | |
852 | lineCount = self.GetLineCount() | |
853 | expanding = True | |
854 | ||
855 | # find out if we are folding or unfolding | |
856 | for lineNum in range(lineCount): | |
857 | if self.GetFoldLevel(lineNum) & wx.stc.STC_FOLDLEVELHEADERFLAG: | |
858 | expanding = not self.GetFoldExpanded(lineNum) | |
859 | break; | |
860 | ||
861 | self.ToggleFoldAll(expanding) | |
862 | else: | |
863 | lineClicked = self.LineFromPosition(evt.GetPosition()) | |
864 | if self.GetFoldLevel(lineClicked) & wx.stc.STC_FOLDLEVELHEADERFLAG: | |
865 | if evt.GetShift(): | |
866 | self.SetFoldExpanded(lineClicked, True) | |
867 | self.Expand(lineClicked, True, True, 1) | |
868 | elif evt.GetControl(): | |
869 | if self.GetFoldExpanded(lineClicked): | |
870 | self.SetFoldExpanded(lineClicked, False) | |
871 | self.Expand(lineClicked, False, True, 0) | |
872 | else: | |
873 | self.SetFoldExpanded(lineClicked, True) | |
874 | self.Expand(lineClicked, True, True, 100) | |
875 | else: | |
876 | self.ToggleFold(lineClicked) | |
877 | ||
878 | elif evt.GetMargin() == 0: | |
879 | #This is used to toggle breakpoints via the debugger service. | |
aca310e5 | 880 | import DebuggerService |
1f780e48 RD |
881 | db_service = wx.GetApp().GetService(DebuggerService.DebuggerService) |
882 | if db_service: | |
883 | db_service.OnToggleBreakpoint(evt, line=self.LineFromPosition(evt.GetPosition())) | |
884 | ||
885 | ||
886 | def OnUpdateUI(self, evt): | |
887 | braces = self.GetMatchingBraces() | |
888 | ||
889 | # check for matching braces | |
890 | braceAtCaret = -1 | |
891 | braceOpposite = -1 | |
892 | charBefore = None | |
893 | caretPos = self.GetCurrentPos() | |
894 | if caretPos > 0: | |
895 | charBefore = self.GetCharAt(caretPos - 1) | |
896 | styleBefore = self.GetStyleAt(caretPos - 1) | |
897 | ||
898 | # check before | |
899 | if charBefore and chr(charBefore) in braces: | |
900 | braceAtCaret = caretPos - 1 | |
901 | ||
902 | # check after | |
903 | if braceAtCaret < 0: | |
904 | charAfter = self.GetCharAt(caretPos) | |
905 | styleAfter = self.GetStyleAt(caretPos) | |
906 | if charAfter and chr(charAfter) in braces: | |
907 | braceAtCaret = caretPos | |
908 | ||
909 | if braceAtCaret >= 0: | |
910 | braceOpposite = self.BraceMatch(braceAtCaret) | |
911 | ||
912 | if braceAtCaret != -1 and braceOpposite == -1: | |
913 | self.BraceBadLight(braceAtCaret) | |
914 | else: | |
915 | self.BraceHighlight(braceAtCaret, braceOpposite) | |
916 | ||
917 | evt.Skip() | |
918 | ||
919 | ||
920 | def ToggleFoldAll(self, expand = True, topLevelOnly = False): | |
921 | i = 0 | |
922 | lineCount = self.GetLineCount() | |
923 | while i < lineCount: | |
924 | if not topLevelOnly or (topLevelOnly and self.GetFoldLevel(i) & wx.stc.STC_FOLDLEVELNUMBERMASK == wx.stc.STC_FOLDLEVELBASE): | |
925 | if (expand and self.CanLineExpand(i)) or (not expand and self.CanLineCollapse(i)): | |
926 | self.ToggleFold(i) | |
927 | i = i + 1 | |
928 | ||
929 | ||
930 | def CanLineExpand(self, line): | |
931 | return not self.GetFoldExpanded(line) | |
932 | ||
933 | ||
934 | def CanLineCollapse(self, line): | |
935 | return self.GetFoldExpanded(line) and self.GetFoldLevel(line) & wx.stc.STC_FOLDLEVELHEADERFLAG | |
936 | ||
937 | ||
938 | def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): | |
939 | lastChild = self.GetLastChild(line, level) | |
940 | line = line + 1 | |
941 | while line <= lastChild: | |
942 | if force: | |
943 | if visLevels > 0: | |
944 | self.ShowLines(line, line) | |
945 | else: | |
946 | self.HideLines(line, line) | |
947 | else: | |
948 | if doExpand: | |
949 | self.ShowLines(line, line) | |
950 | ||
951 | if level == -1: | |
952 | level = self.GetFoldLevel(line) | |
953 | ||
954 | if level & wx.stc.STC_FOLDLEVELHEADERFLAG: | |
955 | if force: | |
956 | if visLevels > 1: | |
957 | self.SetFoldExpanded(line, True) | |
958 | else: | |
959 | self.SetFoldExpanded(line, False) | |
960 | line = self.Expand(line, doExpand, force, visLevels-1) | |
961 | ||
962 | else: | |
963 | if doExpand and self.GetFoldExpanded(line): | |
964 | line = self.Expand(line, True, force, visLevels-1) | |
965 | else: | |
966 | line = self.Expand(line, False, force, visLevels-1) | |
967 | else: | |
968 | line = line + 1; | |
969 | ||
970 | return line | |
971 | ||
972 |