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