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
 
  24 from UICommon 
import CaseInsensitiveCompare
 
  26 if wx
.Platform 
== '__WXMSW__': 
  32 EXPAND_TEXT_ID 
= wx
.NewId() 
  33 COLLAPSE_TEXT_ID 
= wx
.NewId() 
  34 EXPAND_TOP_ID 
= wx
.NewId() 
  35 COLLAPSE_TOP_ID 
= wx
.NewId() 
  36 EXPAND_ALL_ID 
= wx
.NewId() 
  37 COLLAPSE_ALL_ID 
= wx
.NewId() 
  38 CHECK_CODE_ID 
= wx
.NewId() 
  39 AUTO_COMPLETE_ID 
= wx
.NewId() 
  40 CLEAN_WHITESPACE 
= wx
.NewId() 
  41 COMMENT_LINES_ID 
= wx
.NewId() 
  42 UNCOMMENT_LINES_ID 
= wx
.NewId() 
  43 INDENT_LINES_ID 
= wx
.NewId() 
  44 DEDENT_LINES_ID 
= wx
.NewId() 
  45 USE_TABS_ID 
= wx
.NewId() 
  46 SET_INDENT_WIDTH_ID 
= wx
.NewId() 
  47 FOLDING_ID 
= wx
.NewId() 
  50 class CodeDocument(STCTextEditor
.TextDocument
): 
  54 class CodeView(STCTextEditor
.TextView
): 
  57     #---------------------------------------------------------------------------- 
  59     #---------------------------------------------------------------------------- 
  62     def GetCtrlClass(self
): 
  63         """ Used in split window to instantiate new instances """ 
  67     def ProcessEvent(self
, event
): 
  69         if id == EXPAND_TEXT_ID
: 
  70             self
.GetCtrl().ToggleFold(self
.GetCtrl().GetCurrentLine()) 
  72         elif id == COLLAPSE_TEXT_ID
: 
  73             self
.GetCtrl().ToggleFold(self
.GetCtrl().GetCurrentLine()) 
  75         elif id == EXPAND_TOP_ID
: 
  76             self
.GetCtrl().ToggleFoldAll(expand 
= True, topLevelOnly 
= True) 
  78         elif id == COLLAPSE_TOP_ID
: 
  79             self
.GetCtrl().ToggleFoldAll(expand 
= False, topLevelOnly 
= True) 
  81         elif id == EXPAND_ALL_ID
: 
  82             self
.GetCtrl().ToggleFoldAll(expand 
= True) 
  84         elif id == COLLAPSE_ALL_ID
: 
  85             self
.GetCtrl().ToggleFoldAll(expand 
= False) 
  87         elif id == CHECK_CODE_ID
: 
  90         elif id == AUTO_COMPLETE_ID
: 
  93         elif id == CLEAN_WHITESPACE
: 
  94             self
.OnCleanWhiteSpace() 
  96         elif id == SET_INDENT_WIDTH_ID
: 
  97             self
.OnSetIndentWidth() 
  99         elif id == USE_TABS_ID
: 
 100             self
.GetCtrl().SetUseTabs(not self
.GetCtrl().GetUseTabs()) 
 102         elif id == INDENT_LINES_ID
: 
 103             self
.GetCtrl().CmdKeyExecute(wx
.stc
.STC_CMD_TAB
) 
 105         elif id == DEDENT_LINES_ID
: 
 106             self
.GetCtrl().CmdKeyExecute(wx
.stc
.STC_CMD_BACKTAB
) 
 108         elif id == COMMENT_LINES_ID
: 
 109             self
.OnCommentLines() 
 111         elif id == UNCOMMENT_LINES_ID
: 
 112             self
.OnUncommentLines() 
 115             return STCTextEditor
.TextView
.ProcessEvent(self
, event
) 
 118     def ProcessUpdateUIEvent(self
, event
): 
 119         if not self
.GetCtrl(): 
 122         if id == EXPAND_TEXT_ID
: 
 123             event
.Enable(self
.GetCtrl().CanLineExpand(self
.GetCtrl().GetCurrentLine())) 
 125         elif id == COLLAPSE_TEXT_ID
: 
 126             event
.Enable(self
.GetCtrl().CanLineCollapse(self
.GetCtrl().GetCurrentLine())) 
 128         elif (id == EXPAND_TOP_ID
 
 129         or id == COLLAPSE_TOP_ID
 
 130         or id == EXPAND_ALL_ID
 
 131         or id == COLLAPSE_ALL_ID
 
 132         or id == AUTO_COMPLETE_ID
 
 133         or id == CLEAN_WHITESPACE
 
 134         or id == INDENT_LINES_ID
 
 135         or id == DEDENT_LINES_ID
 
 136         or id == COMMENT_LINES_ID
 
 137         or id == UNCOMMENT_LINES_ID
): 
 138             event
.Enable(self
.GetCtrl().GetTextLength() > 0) 
 140         elif id == CHECK_CODE_ID
: 
 143         elif (id == SET_INDENT_WIDTH_ID
 
 144         or id == FOLDING_ID
): 
 147         elif id == USE_TABS_ID
: 
 149             event
.Check(self
.GetCtrl().GetUseTabs()) 
 152             return STCTextEditor
.TextView
.ProcessUpdateUIEvent(self
, event
) 
 155     #---------------------------------------------------------------------------- 
 156     # Methods for OutlineService 
 157     #---------------------------------------------------------------------------- 
 159     def OnChangeFilename(self
): 
 160         wx
.lib
.docview
.View
.OnChangeFilename(self
) 
 161         self
.LoadOutline(force
=True) 
 164     def ClearOutline(self
): 
 165         outlineService 
= wx
.GetApp().GetService(OutlineService
.OutlineService
) 
 166         if not outlineService
: 
 169         outlineView 
= outlineService
.GetView() 
 173         outlineView
.ClearTreeCtrl() 
 176     def LoadOutline(self
, force
=False): 
 177         outlineService 
= wx
.GetApp().GetService(OutlineService
.OutlineService
) 
 178         if not outlineService
: 
 180         outlineService
.LoadOutline(self
, force
=force
) 
 183     def DoLoadOutlineCallback(self
, force
=False): 
 184         outlineService 
= wx
.GetApp().GetService(OutlineService
.OutlineService
) 
 185         if not outlineService
: 
 188         outlineView 
= outlineService
.GetView() 
 192         treeCtrl 
= outlineView
.GetTreeCtrl() 
 196         view 
= treeCtrl
.GetCallbackView() 
 197         newCheckSum 
= self
.GenCheckSum() 
 199             if view 
and view 
is self
: 
 200                 if self
._checkSum 
== newCheckSum
: 
 202         self
._checkSum 
= newCheckSum
 
 204         treeCtrl
.DeleteAllItems() 
 206         document 
= self
.GetDocument() 
 210         filename 
= document
.GetFilename() 
 212             rootItem 
= treeCtrl
.AddRoot(os
.path
.basename(filename
)) 
 213             treeCtrl
.SetDoSelectCallback(rootItem
, self
, None) 
 217         text 
= self
.GetValue() 
 221         CLASS_PATTERN 
= 'class[ \t]+\w+.*?:' 
 222         DEF_PATTERN 
= 'def[ \t]+\w+\(.*?\)' 
 223         classPat 
= re
.compile(CLASS_PATTERN
, re
.M|re
.S
) 
 224         defPat
= re
.compile(DEF_PATTERN
, re
.M|re
.S
) 
 225         pattern 
= re
.compile('^[ \t]*((' + CLASS_PATTERN 
+ ')|('+ DEF_PATTERN 
+'.*?:)).*?$', re
.M|re
.S
) 
 227         iter = pattern
.finditer(text
) 
 228         indentStack 
= [(0, rootItem
)] 
 230             line 
= pattern
.string
[pattern
.start(0):pattern
.end(0)] 
 231             classLine 
= classPat
.search(line
) 
 233                 indent 
= classLine
.start(0) 
 234                 itemStr 
= classLine
.string
[classLine
.start(0):classLine
.end(0)-1]  # don't take the closing ':' 
 236                 defLine 
= defPat
.search(line
) 
 238                     indent 
= defLine
.start(0) 
 239                     itemStr 
= defLine
.string
[defLine
.start(0):defLine
.end(0)] 
 242                 parentItem 
= rootItem
 
 244                 lastItem 
= indentStack
.pop() 
 245                 while lastItem
[0] >= indent
: 
 246                     lastItem 
= indentStack
.pop() 
 247                 indentStack
.append(lastItem
) 
 248                 parentItem 
= lastItem
[1] 
 250             item 
= treeCtrl
.AppendItem(parentItem
, itemStr
) 
 251             treeCtrl
.SetDoSelectCallback(item
, self
, (pattern
.end(0), pattern
.start(0) + indent
))  # select in reverse order because we want the cursor to be at the start of the line so it wouldn't scroll to the right 
 252             indentStack
.append((indent
, item
)) 
 254         treeCtrl
.Expand(rootItem
) 
 259     def DoSelectCallback(self
, data
): 
 261             self
.EnsureVisibleEnforcePolicy(self
.LineFromPosition(data
[0])) 
 262             # wxBug: need to select in reverse order (end, start) to place cursor at begining of line, 
 263             #        otherwise, display is scrolled over to the right hard and is hard to view 
 264             self
.SetSelection(data
[1], data
[0]) 
 267 ##    def checksum(self, bytes):         
 268 ##        def rotate_right(c): 
 270 ##                return (c>>1)|0x8000 
 276 ##            ch = ord(ch) & 0xFF 
 277 ##            result = (rotate_right(result)+ch) & 0xFFFF 
 281     def GenCheckSum(self
): 
 282         """ Poor man's checksum.  We'll assume most changes will change the length of the file. 
 284         text 
= self
.GetValue() 
 291     #---------------------------------------------------------------------------- 
 293     #---------------------------------------------------------------------------- 
 295     def OnCheckCode(self
): 
 296         """ Need to overshadow this for each specific subclass """ 
 299                 code 
= self
.GetCtrl().GetText() 
 300                 codeObj 
= compile(code
, self
.GetDocument().GetFilename(), 'exec') 
 301                 self
._GetParentFrame
().SetStatusText(_("The file successfully compiled")) 
 302             except SyntaxError, (message
, (fileName
, line
, col
, text
)): 
 303                 pos 
= self
.GetCtrl().PositionFromLine(line 
- 1) + col 
- 1 
 304                 self
.GetCtrl().SetSelection(pos
, pos
) 
 305                 self
._GetParentFrame
().SetStatusText(_("Syntax Error: %s") % message
) 
 307                 self
._GetParentFrame
().SetStatusText("%s: %s" % (sys
.exc_info()[0], sys
.exc_info()[1])) 
 310     def OnAutoComplete(self
): 
 311         self
.GetCtrl().AutoCompCancel() 
 312         self
.GetCtrl().AutoCompSetAutoHide(0) 
 313         self
.GetCtrl().AutoCompSetChooseSingle(True) 
 314         self
.GetCtrl().AutoCompSetIgnoreCase(True) 
 315         context
, hint 
= self
.GetAutoCompleteHint() 
 316         replaceList
, replaceLen 
= self
.GetAutoCompleteKeywordList(context
, hint
) 
 317         if replaceList 
and len(replaceList
) != 0:  
 318             self
.GetCtrl().AutoCompShow(replaceLen
, replaceList
) 
 321     def GetAutoCompleteHint(self
): 
 322         """ Replace this method with Editor specific method """ 
 323         pos 
= self
.GetCtrl().GetCurrentPos() 
 326         if chr(self
.GetCtrl().GetCharAt(pos 
- 1)) == '.': 
 332         validLetters 
= string
.letters 
+ string
.digits 
+ '_.' 
 338             char 
= chr(self
.GetCtrl().GetCharAt(pos
)) 
 339             if char 
not in validLetters
: 
 345             lastDot 
= word
.rfind('.') 
 347                 context 
= word
[0:lastDot
] 
 348                 hint 
= word
[lastDot
+1:] 
 353     def GetAutoCompleteDefaultKeywords(self
): 
 354         """ Replace this method with Editor specific keywords """ 
 355         return ['Put', 'Editor Specific', 'Keywords', 'Here'] 
 358     def GetAutoCompleteKeywordList(self
, context
, hint
):             
 359         """ Replace this method with Editor specific keywords """ 
 360         kw 
= self
.GetAutoCompleteDefaultKeywords() 
 362         if hint 
and len(hint
): 
 363             lowerHint 
= hint
.lower() 
 364             filterkw 
= filter(lambda item
: item
.lower().startswith(lowerHint
), kw
)  # remove variables and methods that don't match hint 
 368             replaceLen 
= len(hint
) 
 372         kw
.sort(CaseInsensitiveCompare
) 
 373         return " ".join(kw
), replaceLen
 
 376     def OnCleanWhiteSpace(self
): 
 378         for lineNo 
in self
._GetSelectedLineNumbers
(): 
 379             lineText 
= string
.rstrip(self
.GetCtrl().GetLine(lineNo
)) 
 382             for char 
in lineText
: 
 384                     indent 
= indent 
+ self
.GetCtrl().GetIndent() 
 386                 elif char 
in string
.whitespace
: 
 391             if self
.GetCtrl().GetUseTabs(): 
 392                 indentText 
= (indent 
/ self
.GetCtrl().GetIndent()) * '\t' + (indent 
% self
.GetCtrl().GetIndent()) * ' ' 
 394                 indentText 
= indent 
* ' ' 
 395             lineText 
= indentText 
+ lineText
[lstrip
:] + '\n' 
 396             newText 
= newText 
+ lineText
 
 397         self
._ReplaceSelectedLines
(newText
) 
 400     def OnSetIndentWidth(self
): 
 401         dialog 
= wx
.TextEntryDialog(self
._GetParentFrame
(), _("Enter new indent width (2-10):"), _("Set Indent Width"), "%i" % self
.GetCtrl().GetIndent()) 
 402         dialog
.CenterOnParent() 
 403         if dialog
.ShowModal() == wx
.ID_OK
: 
 405                 indent 
= int(dialog
.GetValue()) 
 406                 if indent 
>= 2 and indent 
<= 10: 
 407                     self
.GetCtrl().SetIndent(indent
) 
 408                     self
.GetCtrl().SetTabWidth(indent
) 
 414     def GetIndentWidth(self
): 
 415         return self
.GetCtrl().GetIndent() 
 418     def OnCommentLines(self
): 
 420         for lineNo 
in self
._GetSelectedLineNumbers
(): 
 421             lineText 
= self
.GetCtrl().GetLine(lineNo
) 
 422             if (len(lineText
) > 1 and lineText
[0] == '#') or (len(lineText
) > 2 and lineText
[:2] == '##'): 
 423                 newText 
= newText 
+ lineText
 
 425                 newText 
= newText 
+ "##" + lineText
 
 426         self
._ReplaceSelectedLines
(newText
) 
 429     def OnUncommentLines(self
): 
 431         for lineNo 
in self
._GetSelectedLineNumbers
(): 
 432             lineText 
= self
.GetCtrl().GetLine(lineNo
) 
 433             if len(lineText
) >= 2 and lineText
[:2] == "##": 
 434                 lineText 
= lineText
[2:] 
 435             elif len(lineText
) >= 1 and lineText
[:1] == "#": 
 436                 lineText 
= lineText
[1:] 
 437             newText 
= newText 
+ lineText
 
 438         self
._ReplaceSelectedLines
(newText
) 
 441     def _GetSelectedLineNumbers(self
): 
 442         selStart
, selEnd 
= self
._GetPositionsBoundingSelectedLines
() 
 443         return range(self
.GetCtrl().LineFromPosition(selStart
), self
.GetCtrl().LineFromPosition(selEnd
)) 
 446     def _GetPositionsBoundingSelectedLines(self
): 
 447         startPos 
= self
.GetCtrl().GetCurrentPos() 
 448         endPos 
= self
.GetCtrl().GetAnchor() 
 449         if startPos 
> endPos
: 
 453         if endPos 
== self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(endPos
)): 
 454             endPos 
= endPos 
- 1  # If it's at the very beginning of a line, use the line above it as the ending line 
 455         selStart 
= self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(startPos
)) 
 456         selEnd 
= self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(endPos
) + 1) 
 457         return selStart
, selEnd
 
 460     def _ReplaceSelectedLines(self
, text
): 
 463         selStart
, selEnd 
= self
._GetPositionsBoundingSelectedLines
() 
 464         self
.GetCtrl().SetSelection(selStart
, selEnd
) 
 465         self
.GetCtrl().ReplaceSelection(text
) 
 466         self
.GetCtrl().SetSelection(selStart 
+ len(text
), selStart
) 
 469     def OnUpdate(self
, sender 
= None, hint 
= None): 
 470         if hint 
== "ViewStuff": 
 471             self
.GetCtrl().SetViewDefaults() 
 473             font
, color 
= self
.GetCtrl().GetFontAndColorFromConfig() 
 474             self
.GetCtrl().SetFont(font
) 
 475             self
.GetCtrl().SetFontColor(color
) 
 477             dbg_service 
= wx
.GetApp().GetService(DebuggerService
.DebuggerService
) 
 479                 dbg_service
.SetCurrentBreakpointMarkers(self
) 
 482 class CodeService(STCTextEditor
.TextService
): 
 486         STCTextEditor
.TextService
.__init
__(self
) 
 489     def InstallControls(self
, frame
, menuBar 
= None, toolBar 
= None, statusBar 
= None, document 
= None): 
 490         # TODO NEED TO DO INSTANCEOF CHECK HERE FOR SDI 
 491         #if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument: 
 493         if not document 
and wx
.GetApp().GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
: 
 496         viewMenu 
= menuBar
.GetMenu(menuBar
.FindMenu(_("&View"))) 
 497         isWindows 
= (wx
.Platform 
== '__WXMSW__') 
 499         if not menuBar
.FindItemById(EXPAND_TEXT_ID
):  # check if below menu items have been already been installed 
 500             foldingMenu 
= wx
.Menu() 
 502                 foldingMenu
.Append(EXPAND_TEXT_ID
, _("&Expand\tNumpad-Plus"), _("Expands a collapsed block of text")) 
 504                 foldingMenu
.Append(EXPAND_TEXT_ID
, _("&Expand"), _("Expands a collapsed block of text")) 
 506             wx
.EVT_MENU(frame
, EXPAND_TEXT_ID
, frame
.ProcessEvent
) 
 507             wx
.EVT_UPDATE_UI(frame
, EXPAND_TEXT_ID
, frame
.ProcessUpdateUIEvent
) 
 510                 foldingMenu
.Append(COLLAPSE_TEXT_ID
, _("&Collapse\tNumpad+Minus"), _("Collapse a block of text")) 
 512                 foldingMenu
.Append(COLLAPSE_TEXT_ID
, _("&Collapse"), _("Collapse a block of text")) 
 513             wx
.EVT_MENU(frame
, COLLAPSE_TEXT_ID
, frame
.ProcessEvent
) 
 514             wx
.EVT_UPDATE_UI(frame
, COLLAPSE_TEXT_ID
, frame
.ProcessUpdateUIEvent
) 
 517                 foldingMenu
.Append(EXPAND_TOP_ID
, _("Expand &Top Level\tCtrl+Numpad+Plus"), _("Expands the top fold levels in the document")) 
 519                 foldingMenu
.Append(EXPAND_TOP_ID
, _("Expand &Top Level"), _("Expands the top fold levels in the document")) 
 520             wx
.EVT_MENU(frame
, EXPAND_TOP_ID
, frame
.ProcessEvent
) 
 521             wx
.EVT_UPDATE_UI(frame
, EXPAND_TOP_ID
, frame
.ProcessUpdateUIEvent
) 
 524                 foldingMenu
.Append(COLLAPSE_TOP_ID
, _("Collapse Top &Level\tCtrl+Numpad+Minus"), _("Collapses the top fold levels in the document")) 
 526                 foldingMenu
.Append(COLLAPSE_TOP_ID
, _("Collapse Top &Level"), _("Collapses the top fold levels in the document")) 
 527             wx
.EVT_MENU(frame
, COLLAPSE_TOP_ID
, frame
.ProcessEvent
) 
 528             wx
.EVT_UPDATE_UI(frame
, COLLAPSE_TOP_ID
, frame
.ProcessUpdateUIEvent
) 
 531                 foldingMenu
.Append(EXPAND_ALL_ID
, _("Expand &All\tShift+Numpad+Plus"), _("Expands all of the fold levels in the document")) 
 533                 foldingMenu
.Append(EXPAND_ALL_ID
, _("Expand &All"), _("Expands all of the fold levels in the document")) 
 534             wx
.EVT_MENU(frame
, EXPAND_ALL_ID
, frame
.ProcessEvent
) 
 535             wx
.EVT_UPDATE_UI(frame
, EXPAND_ALL_ID
, frame
.ProcessUpdateUIEvent
) 
 538                 foldingMenu
.Append(COLLAPSE_ALL_ID
, _("Colla&pse All\tShift+Numpad+Minus"), _("Collapses all of the fold levels in the document")) 
 540                 foldingMenu
.Append(COLLAPSE_ALL_ID
, _("Colla&pse All"), _("Collapses all of the fold levels in the document")) 
 541             wx
.EVT_MENU(frame
, COLLAPSE_ALL_ID
, frame
.ProcessEvent
) 
 542             wx
.EVT_UPDATE_UI(frame
, COLLAPSE_ALL_ID
, frame
.ProcessUpdateUIEvent
) 
 544             viewMenu
.AppendMenu(FOLDING_ID
, _("&Folding"), foldingMenu
) 
 545             wx
.EVT_UPDATE_UI(frame
, FOLDING_ID
, frame
.ProcessUpdateUIEvent
) 
 547         formatMenuIndex 
= menuBar
.FindMenu(_("&Format")) 
 548         if formatMenuIndex 
> -1: 
 549             formatMenu 
= menuBar
.GetMenu(formatMenuIndex
) 
 551             formatMenu 
= wx
.Menu() 
 552         if not menuBar
.FindItemById(CHECK_CODE_ID
):  # check if below menu items have been already been installed 
 553             formatMenu
.AppendSeparator() 
 554             formatMenu
.Append(CHECK_CODE_ID
, _("&Check Code"), _("Checks the document for syntax and indentation errors")) 
 555             wx
.EVT_MENU(frame
, CHECK_CODE_ID
, frame
.ProcessEvent
) 
 556             wx
.EVT_UPDATE_UI(frame
, CHECK_CODE_ID
, frame
.ProcessUpdateUIEvent
) 
 557             formatMenu
.Append(AUTO_COMPLETE_ID
, _("&Auto Complete\tCtrl+Space"), _("Provides suggestions on how to complete the current statement")) 
 558             wx
.EVT_MENU(frame
, AUTO_COMPLETE_ID
, frame
.ProcessEvent
) 
 559             wx
.EVT_UPDATE_UI(frame
, AUTO_COMPLETE_ID
, frame
.ProcessUpdateUIEvent
) 
 560             formatMenu
.Append(CLEAN_WHITESPACE
, _("Clean &Whitespace"), _("Converts leading spaces to tabs or vice versa per 'use tabs' and clears trailing spaces")) 
 561             wx
.EVT_MENU(frame
, CLEAN_WHITESPACE
, frame
.ProcessEvent
) 
 562             wx
.EVT_UPDATE_UI(frame
, CLEAN_WHITESPACE
, frame
.ProcessUpdateUIEvent
) 
 563             formatMenu
.AppendSeparator() 
 564             formatMenu
.Append(INDENT_LINES_ID
, _("&Indent Lines\tTab"), _("Indents the selected lines one indent width")) 
 565             wx
.EVT_MENU(frame
, INDENT_LINES_ID
, frame
.ProcessEvent
) 
 566             wx
.EVT_UPDATE_UI(frame
, INDENT_LINES_ID
, frame
.ProcessUpdateUIEvent
) 
 567             formatMenu
.Append(DEDENT_LINES_ID
, _("&Dedent Lines\tShift+Tab"), _("Dedents the selected lines one indent width")) 
 568             wx
.EVT_MENU(frame
, DEDENT_LINES_ID
, frame
.ProcessEvent
) 
 569             wx
.EVT_UPDATE_UI(frame
, DEDENT_LINES_ID
, frame
.ProcessUpdateUIEvent
) 
 570             formatMenu
.Append(COMMENT_LINES_ID
, _("Comment &Lines\tCtrl+Q"), _("Comments out the selected lines be prefixing each one with a comment indicator")) 
 571             wx
.EVT_MENU(frame
, COMMENT_LINES_ID
, frame
.ProcessEvent
) 
 572             wx
.EVT_UPDATE_UI(frame
, COMMENT_LINES_ID
, frame
.ProcessUpdateUIEvent
) 
 573             formatMenu
.Append(UNCOMMENT_LINES_ID
, _("&Uncomment Lines\tCtrl+Shift+Q"), _("Removes comment prefixes from each of the selected lines")) 
 574             wx
.EVT_MENU(frame
, UNCOMMENT_LINES_ID
, frame
.ProcessEvent
) 
 575             wx
.EVT_UPDATE_UI(frame
, UNCOMMENT_LINES_ID
, frame
.ProcessUpdateUIEvent
) 
 576             formatMenu
.AppendSeparator() 
 577             formatMenu
.AppendCheckItem(USE_TABS_ID
, _("Use &Tabs"), _("Toggles use of tabs or whitespaces for indents")) 
 578             wx
.EVT_MENU(frame
, USE_TABS_ID
, frame
.ProcessEvent
) 
 579             wx
.EVT_UPDATE_UI(frame
, USE_TABS_ID
, frame
.ProcessUpdateUIEvent
) 
 580             formatMenu
.Append(SET_INDENT_WIDTH_ID
, _("&Set Indent Width..."), _("Sets the indent width")) 
 581             wx
.EVT_MENU(frame
, SET_INDENT_WIDTH_ID
, frame
.ProcessEvent
) 
 582             wx
.EVT_UPDATE_UI(frame
, SET_INDENT_WIDTH_ID
, frame
.ProcessUpdateUIEvent
) 
 583         if formatMenuIndex 
== -1: 
 584             viewMenuIndex 
= menuBar
.FindMenu(_("&View")) 
 585             menuBar
.Insert(viewMenuIndex 
+ 1, formatMenu
, _("&Format")) 
 587 ##        accelTable = wx.AcceleratorTable([ 
 588 ##            (wx.ACCEL_NORMAL, wx.WXK_TAB, INDENT_LINES_ID), 
 589 ##            (wx.ACCEL_SHIFT, wx.WXK_TAB, DEDENT_LINES_ID), 
 590 ##            eval(_("wx.ACCEL_CTRL, ord('Q'), COMMENT_LINES_ID")), 
 591 ##            eval(_("wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord('Q'), UNCOMMENT_LINES_ID")) 
 593 ##        frame.SetAcceleratorTable(accelTable) 
 595     def ProcessUpdateUIEvent(self
, event
): 
 597         if (id == EXPAND_TEXT_ID
 
 598         or id == COLLAPSE_TEXT_ID
 
 599         or id == EXPAND_TOP_ID
 
 600         or id == COLLAPSE_TOP_ID
 
 601         or id == EXPAND_ALL_ID
 
 602         or id == COLLAPSE_ALL_ID
 
 603         or id == CHECK_CODE_ID
 
 604         or id == AUTO_COMPLETE_ID
 
 605         or id == CLEAN_WHITESPACE
 
 606         or id == SET_INDENT_WIDTH_ID
 
 608         or id == INDENT_LINES_ID
 
 609         or id == DEDENT_LINES_ID
 
 610         or id == COMMENT_LINES_ID
 
 611         or id == UNCOMMENT_LINES_ID
 
 612         or id == FOLDING_ID
): 
 616             return STCTextEditor
.TextService
.ProcessUpdateUIEvent(self
, event
) 
 619 class CodeCtrl(STCTextEditor
.TextCtrl
): 
 620     CURRENT_LINE_MARKER_NUM 
= 2 
 621     BREAKPOINT_MARKER_NUM 
= 1 
 622     CURRENT_LINE_MARKER_MASK 
= 0x4 
 623     BREAKPOINT_MARKER_MASK 
= 0x2 
 626     def __init__(self
, parent
, id=-1, style 
= wx
.NO_FULL_REPAINT_ON_RESIZE
): 
 627         STCTextEditor
.TextCtrl
.__init
__(self
, parent
, id, style
) 
 630         self
.Bind(wx
.EVT_RIGHT_UP
, self
.OnRightUp
) 
 631         self
.SetProperty("fold", "1") 
 633         # Setup a margin to hold fold markers 
 634         #self.SetFoldFlags(16)  ###  WHAT IS THIS VALUE?  WHAT ARE THE OTHER FLAGS?  DOES IT MATTER? 
 635         self
.SetMarginType(2, wx
.stc
.STC_MARGIN_SYMBOL
) 
 636         self
.SetMarginMask(2, wx
.stc
.STC_MASK_FOLDERS
) 
 637         self
.SetMarginSensitive(2, True) 
 638         self
.SetMarginWidth(2, 12) 
 640         self
.SetMarginSensitive(1, False) 
 641         self
.SetMarginMask(1, 0x4) 
 643         self
.SetMarginSensitive(0, True) 
 644         self
.SetMarginType(0, wx
.stc
.STC_MARGIN_SYMBOL
) 
 645         self
.SetMarginMask(0, 0x3) 
 646         self
.SetMarginWidth(0, 12) 
 648         self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEREND
,     wx
.stc
.STC_MARK_BOXPLUSCONNECTED
,  "white", "black") 
 649         self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEROPENMID
, wx
.stc
.STC_MARK_BOXMINUSCONNECTED
, "white", "black") 
 650         self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERMIDTAIL
, wx
.stc
.STC_MARK_TCORNER
,  "white", "black") 
 651         self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERTAIL
,    wx
.stc
.STC_MARK_LCORNER
,  "white", "black") 
 652         self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERSUB
,     wx
.stc
.STC_MARK_VLINE
,    "white", "black") 
 653         self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDER
,        wx
.stc
.STC_MARK_BOXPLUS
,  "white", "black") 
 654         self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEROPEN
,    wx
.stc
.STC_MARK_BOXMINUS
, "white", "black") 
 655         # Define the current line marker 
 656         self
.MarkerDefine(CodeCtrl
.CURRENT_LINE_MARKER_NUM
, wx
.stc
.STC_MARK_SHORTARROW
, wx
.BLACK
, (255,255,128)) 
 657         # Define the breakpoint marker 
 658         self
.MarkerDefine(CodeCtrl
.BREAKPOINT_MARKER_NUM
, wx
.stc
.STC_MARK_CIRCLE
, wx
.BLACK
, (255,0,0)) 
 660         if _WINDOWS
:  # should test to see if menu item exists, if it does, add this workaround 
 661             self
.CmdKeyClear(wx
.stc
.STC_KEY_TAB
, 0)  # menu item "Indent Lines" from CodeService.InstallControls() generates another INDENT_LINES_ID event, so we'll explicitly disable the tab processing in the editor 
 663         wx
.stc
.EVT_STC_MARGINCLICK(self
, self
.GetId(), self
.OnMarginClick
) 
 664         wx
.EVT_KEY_DOWN(self
, self
.OnKeyPressed
) 
 665         if self
.GetMatchingBraces():  
 666             wx
.stc
.EVT_STC_UPDATEUI(self
, self
.GetId(), self
.OnUpdateUI
) 
 672     def OnRightUp(self
, event
): 
 673         #Hold onto the current line number, no way to get it later. 
 674         self
._rightClickPosition 
= self
.PositionFromPoint(event
.GetPosition()) 
 675         self
._rightClickLine 
= self
.LineFromPosition(self
._rightClickPosition
) 
 676         self
.PopupMenu(self
.CreatePopupMenu(), event
.GetPosition()) 
 677         self
._rightClickLine 
= -1 
 678         self
._rightClickPosition 
= -1 
 681     def CreatePopupMenu(self
): 
 682         TOGGLEBREAKPOINT_ID 
= wx
.NewId() 
 683         TOGGLEMARKER_ID 
= wx
.NewId() 
 684         SYNCTREE_ID 
= wx
.NewId() 
 688         self
.Bind(wx
.EVT_MENU
, self
.OnPopSyncOutline
, id=SYNCTREE_ID
) 
 689         item 
= wx
.MenuItem(menu
, SYNCTREE_ID
, _("Find in Outline View")) 
 690         menu
.AppendItem(item
) 
 691         menu
.AppendSeparator() 
 692         self
.Bind(wx
.EVT_MENU
, self
.OnPopToggleBP
, id=TOGGLEBREAKPOINT_ID
) 
 693         item 
= wx
.MenuItem(menu
, TOGGLEBREAKPOINT_ID
, _("Toggle Breakpoint")) 
 694         menu
.AppendItem(item
) 
 695         self
.Bind(wx
.EVT_MENU
, self
.OnPopToggleMarker
, id=TOGGLEMARKER_ID
) 
 696         item 
= wx
.MenuItem(menu
, TOGGLEMARKER_ID
, _("Toggle Marker")) 
 697         menu
.AppendItem(item
) 
 698         menu
.AppendSeparator() 
 700         itemIDs 
= [wx
.ID_UNDO
, wx
.ID_REDO
, None, 
 701                    wx
.ID_CUT
, wx
.ID_COPY
, wx
.ID_PASTE
, wx
.ID_CLEAR
, None, wx
.ID_SELECTALL
] 
 703         menuBar 
= wx
.GetApp().GetTopWindow().GetMenuBar() 
 704         for itemID 
in itemIDs
: 
 706                 menu
.AppendSeparator() 
 708                 item 
= menuBar
.FindItemById(itemID
) 
 710                     menu
.Append(itemID
, item
.GetLabel()) 
 711                     wx
.EVT_MENU(self
, itemID
, self
.DSProcessEvent
)  # wxHack: for customized right mouse menu doesn't work with new DynamicSashWindow 
 712                     wx
.EVT_UPDATE_UI(self
, itemID
, self
.DSProcessUpdateUIEvent
)  # wxHack: for customized right mouse menu doesn't work with new DynamicSashWindow 
 716     def OnPopToggleBP(self
, event
): 
 717         """ Toggle break point on right click line, not current line """ 
 718         wx
.GetApp().GetService(DebuggerService
.DebuggerService
).OnToggleBreakpoint(event
, line
=self
._rightClickLine
) 
 721     def OnPopToggleMarker(self
, event
): 
 722         """ Toggle marker on right click line, not current line """ 
 723         wx
.GetApp().GetDocumentManager().GetCurrentView().MarkerToggle(lineNum 
= self
._rightClickLine
) 
 726     def OnPopSyncOutline(self
, event
): 
 727         wx
.GetApp().GetService(OutlineService
.OutlineService
).LoadOutline(wx
.GetApp().GetDocumentManager().GetCurrentView(), position
=self
._rightClickPosition
) 
 730     def HasSelection(self
): 
 731         return self
.GetSelectionStart() - self
.GetSelectionEnd() != 0   
 734     def ClearCurrentLineMarkers(self
): 
 735         self
.MarkerDeleteAll(CodeCtrl
.CURRENT_LINE_MARKER_NUM
) 
 738     def ClearCurrentBreakpoinMarkers(self
): 
 739         self
.MarkerDeleteAll(CodeCtrl
.BREAKPOINT_MARKER_NUM
) 
 742     def GetDefaultFont(self
): 
 743         if wx
.Platform 
== '__WXMSW__': 
 747         return wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
, faceName 
= font
) 
 750     def GetMatchingBraces(self
): 
 751         """ Overwrite this method for language specific braces """ 
 755     def CanWordWrap(self
): 
 759     def SetFont(self
, font
): 
 763     def SetFontColor(self
, fontColor
): 
 764         self
._fontColor 
= fontColor
 
 767     def UpdateStyles(self
): 
 769         if not self
.GetFont(): 
 772         faces 
= { 'font' : self
.GetFont().GetFaceName(), 
 773                   'size' : self
.GetFont().GetPointSize(), 
 774                   'size2': self
.GetFont().GetPointSize() - 2, 
 775                   'color' : "%02x%02x%02x" % (self
.GetFontColor().Red(), self
.GetFontColor().Green(), self
.GetFontColor().Blue()) 
 778         # Global default styles for all languages 
 779         self
.StyleSetSpec(wx
.stc
.STC_STYLE_DEFAULT
,     "face:%(font)s,fore:#FFFFFF,size:%(size)d" % faces
) 
 780         self
.StyleSetSpec(wx
.stc
.STC_STYLE_LINENUMBER
,  "face:%(font)s,back:#C0C0C0,face:%(font)s,size:%(size2)d" % faces
) 
 781         self
.StyleSetSpec(wx
.stc
.STC_STYLE_CONTROLCHAR
, "face:%(font)s" % faces
) 
 782         self
.StyleSetSpec(wx
.stc
.STC_STYLE_BRACELIGHT
,  "face:%(font)s,fore:#000000,back:#70FFFF,size:%(size)d" % faces
) 
 783         self
.StyleSetSpec(wx
.stc
.STC_STYLE_BRACEBAD
,    "face:%(font)s,fore:#000000,back:#FF0000,size:%(size)d" % faces
) 
 786     def OnKeyPressed(self
, event
): 
 787         if self
.CallTipActive(): 
 789         key 
= event
.KeyCode() 
 790         if False:  # key == wx.WXK_SPACE and event.ControlDown(): 
 791             pos 
= self
.GetCurrentPos() 
 793             if event
.ShiftDown(): 
 794                 self
.CallTipSetBackground("yellow") 
 795                 self
.CallTipShow(pos
, 'param1, param2') 
 799                 #for x in range(50000): 
 800                 #    lst.append('%05d' % x) 
 801                 #st = string.join(lst) 
 803                 #self.AutoCompShow(0, st) 
 805                 kw 
= keyword
.kwlist
[:] 
 808                 kw
.append("__init__") 
 811                 kw
.append("this_is_a_longer_value") 
 812                 kw
.append("this_is_a_much_much_much_much_much_much_much_longer_value") 
 814                 kw
.sort()  # Python sorts are case sensitive 
 815                 self
.AutoCompSetIgnoreCase(False)  # so this needs to match 
 817                 self
.AutoCompShow(0, string
.join(kw
)) 
 818         elif key 
== wx
.WXK_RETURN
: 
 821             STCTextEditor
.TextCtrl
.OnKeyPressed(self
, event
) 
 826         self
.EnsureCaretVisible() 
 827         # Need to do a default one for all languges 
 830     def OnMarginClick(self
, evt
): 
 831         # fold and unfold as needed 
 832         if evt
.GetMargin() == 2: 
 833             if evt
.GetShift() and evt
.GetControl(): 
 834                 lineCount 
= self
.GetLineCount() 
 837                 # find out if we are folding or unfolding 
 838                 for lineNum 
in range(lineCount
): 
 839                     if self
.GetFoldLevel(lineNum
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
: 
 840                         expanding 
= not self
.GetFoldExpanded(lineNum
) 
 843                 self
.ToggleFoldAll(expanding
) 
 845                 lineClicked 
= self
.LineFromPosition(evt
.GetPosition()) 
 846                 if self
.GetFoldLevel(lineClicked
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
: 
 848                         self
.SetFoldExpanded(lineClicked
, True) 
 849                         self
.Expand(lineClicked
, True, True, 1) 
 850                     elif evt
.GetControl(): 
 851                         if self
.GetFoldExpanded(lineClicked
): 
 852                             self
.SetFoldExpanded(lineClicked
, False) 
 853                             self
.Expand(lineClicked
, False, True, 0) 
 855                             self
.SetFoldExpanded(lineClicked
, True) 
 856                             self
.Expand(lineClicked
, True, True, 100) 
 858                         self
.ToggleFold(lineClicked
) 
 860         elif evt
.GetMargin() == 0: 
 861             #This is used to toggle breakpoints via the debugger service. 
 862             db_service 
= wx
.GetApp().GetService(DebuggerService
.DebuggerService
) 
 864                 db_service
.OnToggleBreakpoint(evt
, line
=self
.LineFromPosition(evt
.GetPosition())) 
 867     def OnUpdateUI(self
, evt
): 
 868         braces 
= self
.GetMatchingBraces() 
 870         # check for matching braces 
 874         caretPos 
= self
.GetCurrentPos() 
 876             charBefore 
= self
.GetCharAt(caretPos 
- 1) 
 877             styleBefore 
= self
.GetStyleAt(caretPos 
- 1) 
 880         if charBefore 
and chr(charBefore
) in braces
: 
 881             braceAtCaret 
= caretPos 
- 1 
 885             charAfter 
= self
.GetCharAt(caretPos
) 
 886             styleAfter 
= self
.GetStyleAt(caretPos
) 
 887             if charAfter 
and chr(charAfter
) in braces
: 
 888                 braceAtCaret 
= caretPos
 
 890         if braceAtCaret 
>= 0: 
 891             braceOpposite 
= self
.BraceMatch(braceAtCaret
) 
 893         if braceAtCaret 
!= -1  and braceOpposite 
== -1: 
 894             self
.BraceBadLight(braceAtCaret
) 
 896             self
.BraceHighlight(braceAtCaret
, braceOpposite
) 
 901     def ToggleFoldAll(self
, expand 
= True, topLevelOnly 
= False): 
 903         lineCount 
= self
.GetLineCount() 
 905             if not topLevelOnly 
or (topLevelOnly 
and self
.GetFoldLevel(i
) & wx
.stc
.STC_FOLDLEVELNUMBERMASK  
== wx
.stc
.STC_FOLDLEVELBASE
): 
 906                 if (expand 
and self
.CanLineExpand(i
)) or (not expand 
and self
.CanLineCollapse(i
)): 
 911     def CanLineExpand(self
, line
): 
 912         return not self
.GetFoldExpanded(line
) 
 915     def CanLineCollapse(self
, line
): 
 916         return self
.GetFoldExpanded(line
) and self
.GetFoldLevel(line
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
 
 919     def Expand(self
, line
, doExpand
, force
=False, visLevels
=0, level
=-1): 
 920         lastChild 
= self
.GetLastChild(line
, level
) 
 922         while line 
<= lastChild
: 
 925                     self
.ShowLines(line
, line
) 
 927                     self
.HideLines(line
, line
) 
 930                     self
.ShowLines(line
, line
) 
 933                 level 
= self
.GetFoldLevel(line
) 
 935             if level 
& wx
.stc
.STC_FOLDLEVELHEADERFLAG
: 
 938                         self
.SetFoldExpanded(line
, True) 
 940                         self
.SetFoldExpanded(line
, False) 
 941                     line 
= self
.Expand(line
, doExpand
, force
, visLevels
-1) 
 944                     if doExpand 
and self
.GetFoldExpanded(line
): 
 945                         line 
= self
.Expand(line
, True, force
, visLevels
-1) 
 947                         line 
= self
.Expand(line
, False, force
, visLevels
-1)