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)