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