1 #----------------------------------------------------------------------------
3 # Purpose: Abstract Code Editor for pydocview tbat uses the Styled Text Control
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
22 import DebuggerService
25 if wx
.Platform
== '__WXMSW__':
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()
49 class CodeDocument(STCTextEditor
.TextDocument
):
53 class CodeView(STCTextEditor
.TextView
):
56 #----------------------------------------------------------------------------
58 #----------------------------------------------------------------------------
61 def GetCtrlClass(self
):
62 """ Used in split window to instantiate new instances """
66 def ProcessEvent(self
, event
):
68 if id == EXPAND_TEXT_ID
:
69 self
.GetCtrl().ToggleFold(self
.GetCtrl().GetCurrentLine())
71 elif id == COLLAPSE_TEXT_ID
:
72 self
.GetCtrl().ToggleFold(self
.GetCtrl().GetCurrentLine())
74 elif id == EXPAND_TOP_ID
:
75 self
.GetCtrl().ToggleFoldAll(expand
= True, topLevelOnly
= True)
77 elif id == COLLAPSE_TOP_ID
:
78 self
.GetCtrl().ToggleFoldAll(expand
= False, topLevelOnly
= True)
80 elif id == EXPAND_ALL_ID
:
81 self
.GetCtrl().ToggleFoldAll(expand
= True)
83 elif id == COLLAPSE_ALL_ID
:
84 self
.GetCtrl().ToggleFoldAll(expand
= False)
86 elif id == CHECK_CODE_ID
:
89 elif id == AUTO_COMPLETE_ID
:
92 elif id == CLEAN_WHITESPACE
:
93 self
.OnCleanWhiteSpace()
95 elif id == SET_INDENT_WIDTH_ID
:
96 self
.OnSetIndentWidth()
98 elif id == USE_TABS_ID
:
99 self
.GetCtrl().SetUseTabs(not self
.GetCtrl().GetUseTabs())
101 elif id == INDENT_LINES_ID
:
102 self
.GetCtrl().CmdKeyExecute(wx
.stc
.STC_CMD_TAB
)
104 elif id == DEDENT_LINES_ID
:
105 self
.GetCtrl().CmdKeyExecute(wx
.stc
.STC_CMD_BACKTAB
)
107 elif id == COMMENT_LINES_ID
:
108 self
.OnCommentLines()
110 elif id == UNCOMMENT_LINES_ID
:
111 self
.OnUncommentLines()
114 return STCTextEditor
.TextView
.ProcessEvent(self
, event
)
117 def ProcessUpdateUIEvent(self
, event
):
118 if not self
.GetCtrl():
121 if id == EXPAND_TEXT_ID
:
122 event
.Enable(self
.GetCtrl().CanLineExpand(self
.GetCtrl().GetCurrentLine()))
124 elif id == COLLAPSE_TEXT_ID
:
125 event
.Enable(self
.GetCtrl().CanLineCollapse(self
.GetCtrl().GetCurrentLine()))
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)
139 elif id == CHECK_CODE_ID
:
142 elif (id == SET_INDENT_WIDTH_ID
143 or id == FOLDING_ID
):
146 elif id == USE_TABS_ID
:
148 event
.Check(self
.GetCtrl().GetUseTabs())
151 return STCTextEditor
.TextView
.ProcessUpdateUIEvent(self
, event
)
154 #----------------------------------------------------------------------------
155 # Methods for OutlineService
156 #----------------------------------------------------------------------------
158 def OnChangeFilename(self
):
159 wx
.lib
.docview
.View
.OnChangeFilename(self
)
160 self
.LoadOutline(force
=True)
163 def ClearOutline(self
):
164 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
165 if not outlineService
:
168 outlineView
= outlineService
.GetView()
172 outlineView
.ClearTreeCtrl()
175 def LoadOutline(self
, force
=False):
176 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
177 if not outlineService
:
179 outlineService
.LoadOutline(self
, force
=force
)
182 def DoLoadOutlineCallback(self
, force
=False):
183 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
184 if not outlineService
:
187 outlineView
= outlineService
.GetView()
191 treeCtrl
= outlineView
.GetTreeCtrl()
195 view
= treeCtrl
.GetCallbackView()
196 newCheckSum
= self
.GenCheckSum()
198 if view
and view
is self
:
199 if self
._checkSum
== newCheckSum
:
201 self
._checkSum
= newCheckSum
203 treeCtrl
.DeleteAllItems()
205 document
= self
.GetDocument()
209 filename
= document
.GetFilename()
211 rootItem
= treeCtrl
.AddRoot(os
.path
.basename(filename
))
212 treeCtrl
.SetDoSelectCallback(rootItem
, self
, None)
216 text
= self
.GetValue()
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
)
226 iter = pattern
.finditer(text
)
227 indentStack
= [(0, rootItem
)]
229 line
= pattern
.string
[pattern
.start(0):pattern
.end(0)]
230 classLine
= classPat
.search(line
)
232 indent
= classLine
.start(0)
233 itemStr
= classLine
.string
[classLine
.start(0):classLine
.end(0)-1] # don't take the closing ':'
235 defLine
= defPat
.search(line
)
237 indent
= defLine
.start(0)
238 itemStr
= defLine
.string
[defLine
.start(0):defLine
.end(0)]
241 parentItem
= rootItem
243 lastItem
= indentStack
.pop()
244 while lastItem
[0] >= indent
:
245 lastItem
= indentStack
.pop()
246 indentStack
.append(lastItem
)
247 parentItem
= lastItem
[1]
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
))
253 treeCtrl
.Expand(rootItem
)
258 def DoSelectCallback(self
, 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])
266 ## def checksum(self, bytes):
267 ## def rotate_right(c):
269 ## return (c>>1)|0x8000
275 ## ch = ord(ch) & 0xFF
276 ## result = (rotate_right(result)+ch) & 0xFFFF
280 def GenCheckSum(self
):
281 """ Poor man's checksum. We'll assume most changes will change the length of the file.
283 text
= self
.GetValue()
290 #----------------------------------------------------------------------------
292 #----------------------------------------------------------------------------
294 def OnCheckCode(self
):
295 """ Need to overshadow this for each specific subclass """
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
)
306 self
._GetParentFrame
().SetStatusText("%s: %s" % (sys
.exc_info()[0], sys
.exc_info()[1]))
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
)
320 def GetAutoCompleteHint(self
):
321 """ Replace this method with Editor specific method """
322 pos
= self
.GetCtrl().GetCurrentPos()
325 if chr(self
.GetCtrl().GetCharAt(pos
- 1)) == '.':
331 validLetters
= string
.letters
+ string
.digits
+ '_.'
337 char
= chr(self
.GetCtrl().GetCharAt(pos
))
338 if char
not in validLetters
:
344 lastDot
= word
.rfind('.')
346 context
= word
[0:lastDot
]
347 hint
= word
[lastDot
+1:]
352 def GetAutoCompleteDefaultKeywords(self
):
353 """ Replace this method with Editor specific keywords """
354 return ['Put', 'Editor Specific', 'Keywords', 'Here']
357 def CaseInsensitiveCompare(self
, s1
, s2
):
358 """ GetAutoCompleteKeywordList() method used to show keywords in case insensitive order """
369 def GetAutoCompleteKeywordList(self
, context
, hint
):
370 """ Replace this method with Editor specific keywords """
371 kw
= self
.GetAutoCompleteDefaultKeywords()
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
379 replaceLen
= len(hint
)
383 kw
.sort(self
.CaseInsensitiveCompare
)
384 return " ".join(kw
), replaceLen
387 def OnCleanWhiteSpace(self
):
389 for lineNo
in self
._GetSelectedLineNumbers
():
390 lineText
= string
.rstrip(self
.GetCtrl().GetLine(lineNo
))
393 for char
in lineText
:
395 indent
= indent
+ self
.GetCtrl().GetIndent()
397 elif char
in string
.whitespace
:
402 if self
.GetCtrl().GetUseTabs():
403 indentText
= (indent
/ self
.GetCtrl().GetIndent()) * '\t' + (indent
% self
.GetCtrl().GetIndent()) * ' '
405 indentText
= indent
* ' '
406 lineText
= indentText
+ lineText
[lstrip
:] + '\n'
407 newText
= newText
+ lineText
408 self
._ReplaceSelectedLines
(newText
)
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
:
415 indent
= int(dialog
.GetValue())
416 if indent
>= 2 and indent
<= 10:
417 self
.GetCtrl().SetIndent(indent
)
418 self
.GetCtrl().SetTabWidth(indent
)
424 def GetIndentWidth(self
):
425 return self
.GetCtrl().GetIndent()
428 def OnCommentLines(self
):
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
435 newText
= newText
+ "##" + lineText
436 self
._ReplaceSelectedLines
(newText
)
439 def OnUncommentLines(self
):
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
)
451 def _GetSelectedLineNumbers(self
):
452 selStart
, selEnd
= self
._GetPositionsBoundingSelectedLines
()
453 return range(self
.GetCtrl().LineFromPosition(selStart
), self
.GetCtrl().LineFromPosition(selEnd
))
456 def _GetPositionsBoundingSelectedLines(self
):
457 startPos
= self
.GetCtrl().GetCurrentPos()
458 endPos
= self
.GetCtrl().GetAnchor()
459 if startPos
> endPos
:
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
470 def _ReplaceSelectedLines(self
, text
):
473 selStart
, selEnd
= self
._GetPositionsBoundingSelectedLines
()
474 self
.GetCtrl().SetSelection(selStart
, selEnd
)
475 self
.GetCtrl().ReplaceSelection(text
)
476 self
.GetCtrl().SetSelection(selStart
+ len(text
), selStart
)
479 def OnUpdate(self
, sender
= None, hint
= None):
480 if hint
== "ViewStuff":
481 self
.GetCtrl().SetViewDefaults()
483 font
, color
= self
.GetFontAndColorFromConfig()
484 self
.GetCtrl().SetFont(font
)
485 self
.GetCtrl().SetFontColor(color
)
487 dbg_service
= wx
.GetApp().GetService(DebuggerService
.DebuggerService
)
489 dbg_service
.SetCurrentBreakpointMarkers(self
)
492 class CodeService(STCTextEditor
.TextService
):
496 STCTextEditor
.TextService
.__init
__(self
)
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:
503 if not document
and wx
.GetApp().GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
:
506 viewMenu
= menuBar
.GetMenu(menuBar
.FindMenu(_("&View")))
507 isWindows
= (wx
.Platform
== '__WXMSW__')
509 if not menuBar
.FindItemById(EXPAND_TEXT_ID
): # check if below menu items have been already been installed
510 foldingMenu
= wx
.Menu()
512 foldingMenu
.Append(EXPAND_TEXT_ID
, _("&Expand\tNumpad-Plus"), _("Expands a collapsed block of text"))
514 foldingMenu
.Append(EXPAND_TEXT_ID
, _("&Expand"), _("Expands a collapsed block of text"))
516 wx
.EVT_MENU(frame
, EXPAND_TEXT_ID
, frame
.ProcessEvent
)
517 wx
.EVT_UPDATE_UI(frame
, EXPAND_TEXT_ID
, frame
.ProcessUpdateUIEvent
)
520 foldingMenu
.Append(COLLAPSE_TEXT_ID
, _("&Collapse\tNumpad+Minus"), _("Collapse a block of text"))
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
)
527 foldingMenu
.Append(EXPAND_TOP_ID
, _("Expand &Top Level\tCtrl+Numpad+Plus"), _("Expands the top fold levels in the document"))
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
)
534 foldingMenu
.Append(COLLAPSE_TOP_ID
, _("Collapse Top &Level\tCtrl+Numpad+Minus"), _("Collapses the top fold levels in the document"))
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
)
541 foldingMenu
.Append(EXPAND_ALL_ID
, _("Expand &All\tShift+Numpad+Plus"), _("Expands all of the fold levels in the document"))
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
)
548 foldingMenu
.Append(COLLAPSE_ALL_ID
, _("Colla&pse All\tShift+Numpad+Minus"), _("Collapses all of the fold levels in the document"))
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
)
554 viewMenu
.AppendMenu(FOLDING_ID
, _("&Folding"), foldingMenu
)
555 wx
.EVT_UPDATE_UI(frame
, FOLDING_ID
, frame
.ProcessUpdateUIEvent
)
557 formatMenuIndex
= menuBar
.FindMenu(_("&Format"))
558 if formatMenuIndex
> -1:
559 formatMenu
= menuBar
.GetMenu(formatMenuIndex
)
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"))
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"))
603 ## frame.SetAcceleratorTable(accelTable)
605 def ProcessUpdateUIEvent(self
, event
):
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
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
):
626 return STCTextEditor
.TextService
.ProcessUpdateUIEvent(self
, event
)
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
636 def __init__(self
, parent
, id=-1, style
= wx
.NO_FULL_REPAINT_ON_RESIZE
):
637 STCTextEditor
.TextCtrl
.__init
__(self
, parent
, id, style
)
640 self
.Bind(wx
.EVT_RIGHT_UP
, self
.OnRightUp
)
641 self
.SetProperty("fold", "1")
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)
650 self
.SetMarginSensitive(1, False)
651 self
.SetMarginMask(1, 0x4)
653 self
.SetMarginSensitive(0, True)
654 self
.SetMarginType(0, wx
.stc
.STC_MARGIN_SYMBOL
)
655 self
.SetMarginMask(0, 0x3)
656 self
.SetMarginWidth(0, 12)
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))
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
673 wx
.stc
.EVT_STC_MARGINCLICK(self
, self
.GetId(), self
.OnMarginClick
)
674 wx
.EVT_KEY_DOWN(self
, self
.OnKeyPressed
)
675 if self
.GetMatchingBraces():
676 wx
.stc
.EVT_STC_UPDATEUI(self
, self
.GetId(), self
.OnUpdateUI
)
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
691 def CreatePopupMenu(self
):
692 TOGGLEBREAKPOINT_ID
= wx
.NewId()
693 TOGGLEMARKER_ID
= wx
.NewId()
694 SYNCTREE_ID
= wx
.NewId()
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()
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
]
713 menuBar
= wx
.GetApp().GetTopWindow().GetMenuBar()
714 for itemID
in itemIDs
:
716 menu
.AppendSeparator()
718 item
= menuBar
.FindItemById(itemID
)
720 menu
.Append(itemID
, item
.GetLabel())
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
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
)
731 def OnPopToggleMarker(self
, event
):
732 """ Toggle marker on right click line, not current line """
733 wx
.GetApp().GetDocumentManager().GetCurrentView().MarkerToggle(lineNum
= self
._rightClickLine
)
736 def OnPopSyncOutline(self
, event
):
737 wx
.GetApp().GetService(OutlineService
.OutlineService
).LoadOutline(wx
.GetApp().GetDocumentManager().GetCurrentView(), position
=self
._rightClickPosition
)
740 def HasSelection(self
):
741 return self
.GetSelectionStart() - self
.GetSelectionEnd() != 0
744 def ClearCurrentLineMarkers(self
):
745 self
.MarkerDeleteAll(CodeCtrl
.CURRENT_LINE_MARKER_NUM
)
748 def ClearCurrentBreakpoinMarkers(self
):
749 self
.MarkerDeleteAll(CodeCtrl
.BREAKPOINT_MARKER_NUM
)
752 def GetDefaultFont(self
):
753 if wx
.Platform
== '__WXMSW__':
757 return wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
, faceName
= font
)
760 def GetMatchingBraces(self
):
761 """ Overwrite this method for language specific braces """
765 def CanWordWrap(self
):
769 def SetFont(self
, font
):
773 def SetFontColor(self
, fontColor
):
774 self
._fontColor
= fontColor
777 def UpdateStyles(self
):
779 if not self
.GetFont():
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())
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
)
796 def OnKeyPressed(self
, event
):
797 if self
.CallTipActive():
799 key
= event
.KeyCode()
800 if False: # key == wx.WXK_SPACE and event.ControlDown():
801 pos
= self
.GetCurrentPos()
803 if event
.ShiftDown():
804 self
.CallTipSetBackground("yellow")
805 self
.CallTipShow(pos
, 'param1, param2')
809 #for x in range(50000):
810 # lst.append('%05d' % x)
811 #st = string.join(lst)
813 #self.AutoCompShow(0, st)
815 kw
= keyword
.kwlist
[:]
818 kw
.append("__init__")
821 kw
.append("this_is_a_longer_value")
822 kw
.append("this_is_a_much_much_much_much_much_much_much_longer_value")
824 kw
.sort() # Python sorts are case sensitive
825 self
.AutoCompSetIgnoreCase(False) # so this needs to match
827 self
.AutoCompShow(0, string
.join(kw
))
828 elif key
== wx
.WXK_RETURN
:
831 STCTextEditor
.TextCtrl
.OnKeyPressed(self
, event
)
836 self
.EnsureCaretVisible()
837 # Need to do a default one for all languges
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()
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
)
853 self
.ToggleFoldAll(expanding
)
855 lineClicked
= self
.LineFromPosition(evt
.GetPosition())
856 if self
.GetFoldLevel(lineClicked
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
:
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)
865 self
.SetFoldExpanded(lineClicked
, True)
866 self
.Expand(lineClicked
, True, True, 100)
868 self
.ToggleFold(lineClicked
)
870 elif evt
.GetMargin() == 0:
871 #This is used to toggle breakpoints via the debugger service.
872 db_service
= wx
.GetApp().GetService(DebuggerService
.DebuggerService
)
874 db_service
.OnToggleBreakpoint(evt
, line
=self
.LineFromPosition(evt
.GetPosition()))
877 def OnUpdateUI(self
, evt
):
878 braces
= self
.GetMatchingBraces()
880 # check for matching braces
884 caretPos
= self
.GetCurrentPos()
886 charBefore
= self
.GetCharAt(caretPos
- 1)
887 styleBefore
= self
.GetStyleAt(caretPos
- 1)
890 if charBefore
and chr(charBefore
) in braces
:
891 braceAtCaret
= caretPos
- 1
895 charAfter
= self
.GetCharAt(caretPos
)
896 styleAfter
= self
.GetStyleAt(caretPos
)
897 if charAfter
and chr(charAfter
) in braces
:
898 braceAtCaret
= caretPos
900 if braceAtCaret
>= 0:
901 braceOpposite
= self
.BraceMatch(braceAtCaret
)
903 if braceAtCaret
!= -1 and braceOpposite
== -1:
904 self
.BraceBadLight(braceAtCaret
)
906 self
.BraceHighlight(braceAtCaret
, braceOpposite
)
911 def ToggleFoldAll(self
, expand
= True, topLevelOnly
= False):
913 lineCount
= self
.GetLineCount()
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
)):
921 def CanLineExpand(self
, line
):
922 return not self
.GetFoldExpanded(line
)
925 def CanLineCollapse(self
, line
):
926 return self
.GetFoldExpanded(line
) and self
.GetFoldLevel(line
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
929 def Expand(self
, line
, doExpand
, force
=False, visLevels
=0, level
=-1):
930 lastChild
= self
.GetLastChild(line
, level
)
932 while line
<= lastChild
:
935 self
.ShowLines(line
, line
)
937 self
.HideLines(line
, line
)
940 self
.ShowLines(line
, line
)
943 level
= self
.GetFoldLevel(line
)
945 if level
& wx
.stc
.STC_FOLDLEVELHEADERFLAG
:
948 self
.SetFoldExpanded(line
, True)
950 self
.SetFoldExpanded(line
, False)
951 line
= self
.Expand(line
, doExpand
, force
, visLevels
-1)
954 if doExpand
and self
.GetFoldExpanded(line
):
955 line
= self
.Expand(line
, True, force
, visLevels
-1)
957 line
= self
.Expand(line
, False, force
, visLevels
-1)