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)