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():
120 hasText
= self
.GetCtrl().GetTextLength() > 0
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 event
.Enable(hasText
)
131 elif id == COLLAPSE_TOP_ID
:
132 event
.Enable(hasText
)
134 elif id == EXPAND_ALL_ID
:
135 event
.Enable(hasText
)
137 elif id == COLLAPSE_ALL_ID
:
138 event
.Enable(hasText
)
140 elif id == CHECK_CODE_ID
:
143 elif id == AUTO_COMPLETE_ID
:
144 event
.Enable(hasText
)
146 elif id == CLEAN_WHITESPACE
:
147 event
.Enable(hasText
)
149 elif id == SET_INDENT_WIDTH_ID
:
152 elif id == USE_TABS_ID
:
154 event
.Check(self
.GetCtrl().GetUseTabs())
156 elif id == INDENT_LINES_ID
:
157 event
.Enable(hasText
)
159 elif id == DEDENT_LINES_ID
:
160 event
.Enable(hasText
)
162 elif id == COMMENT_LINES_ID
:
163 event
.Enable(hasText
)
165 elif id == UNCOMMENT_LINES_ID
:
166 event
.Enable(hasText
)
168 elif id == FOLDING_ID
:
172 return STCTextEditor
.TextView
.ProcessUpdateUIEvent(self
, event
)
175 #----------------------------------------------------------------------------
176 # Methods for OutlineService
177 #----------------------------------------------------------------------------
179 def OnChangeFilename(self
):
180 wx
.lib
.docview
.View
.OnChangeFilename(self
)
181 self
.LoadOutline(force
=True)
184 def ClearOutline(self
):
185 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
186 if not outlineService
:
189 outlineView
= outlineService
.GetView()
193 outlineView
.ClearTreeCtrl()
196 def LoadOutline(self
, force
=False):
197 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
198 if not outlineService
:
200 outlineService
.LoadOutline(self
, force
=force
)
203 def DoLoadOutlineCallback(self
, force
=False):
204 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
205 if not outlineService
:
208 outlineView
= outlineService
.GetView()
212 treeCtrl
= outlineView
.GetTreeCtrl()
216 view
= treeCtrl
.GetCallbackView()
217 newCheckSum
= self
.GenCheckSum()
219 if view
and view
is self
:
220 if self
._checkSum
== newCheckSum
:
222 self
._checkSum
= newCheckSum
224 treeCtrl
.DeleteAllItems()
226 document
= self
.GetDocument()
230 filename
= document
.GetFilename()
232 rootItem
= treeCtrl
.AddRoot(os
.path
.basename(filename
))
233 treeCtrl
.SetDoSelectCallback(rootItem
, self
, None)
237 text
= self
.GetValue()
241 CLASS_PATTERN
= 'class[ \t]+\w+.*?:'
242 DEF_PATTERN
= 'def[ \t]+\w+\(.*?\)'
243 classPat
= re
.compile(CLASS_PATTERN
, re
.M|re
.S
)
244 defPat
= re
.compile(DEF_PATTERN
, re
.M|re
.S
)
245 pattern
= re
.compile('^[ \t]*((' + CLASS_PATTERN
+ ')|('+ DEF_PATTERN
+'.*?:)).*?$', re
.M|re
.S
)
247 iter = pattern
.finditer(text
)
248 indentStack
= [(0, rootItem
)]
250 line
= pattern
.string
[pattern
.start(0):pattern
.end(0)]
251 classLine
= classPat
.search(line
)
253 indent
= classLine
.start(0)
254 itemStr
= classLine
.string
[classLine
.start(0):classLine
.end(0)-1] # don't take the closing ':'
256 defLine
= defPat
.search(line
)
258 indent
= defLine
.start(0)
259 itemStr
= defLine
.string
[defLine
.start(0):defLine
.end(0)]
262 parentItem
= rootItem
264 lastItem
= indentStack
.pop()
265 while lastItem
[0] >= indent
:
266 lastItem
= indentStack
.pop()
267 indentStack
.append(lastItem
)
268 parentItem
= lastItem
[1]
270 item
= treeCtrl
.AppendItem(parentItem
, itemStr
)
271 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
272 indentStack
.append((indent
, item
))
274 treeCtrl
.Expand(rootItem
)
279 def DoSelectCallback(self
, data
):
281 self
.EnsureVisibleEnforcePolicy(self
.LineFromPosition(data
[0]))
282 # wxBug: need to select in reverse order (end, start) to place cursor at begining of line,
283 # otherwise, display is scrolled over to the right hard and is hard to view
284 self
.SetSelection(data
[1], data
[0])
287 ## def checksum(self, bytes):
288 ## def rotate_right(c):
290 ## return (c>>1)|0x8000
296 ## ch = ord(ch) & 0xFF
297 ## result = (rotate_right(result)+ch) & 0xFFFF
301 def GenCheckSum(self
):
302 """ Poor man's checksum. We'll assume most changes will change the length of the file.
304 text
= self
.GetValue()
311 #----------------------------------------------------------------------------
313 #----------------------------------------------------------------------------
315 def OnCheckCode(self
):
316 """ Need to overshadow this for each specific subclass """
319 code
= self
.GetCtrl().GetText()
320 codeObj
= compile(code
, self
.GetDocument().GetFilename(), 'exec')
321 self
._GetParentFrame
().SetStatusText(_("The file successfully compiled"))
322 except SyntaxError, (message
, (fileName
, line
, col
, text
)):
323 pos
= self
.GetCtrl().PositionFromLine(line
- 1) + col
- 1
324 self
.GetCtrl().SetSelection(pos
, pos
)
325 self
._GetParentFrame
().SetStatusText(_("Syntax Error: %s") % message
)
327 self
._GetParentFrame
().SetStatusText("%s: %s" % (sys
.exc_info()[0], sys
.exc_info()[1]))
330 def OnAutoComplete(self
):
331 self
.GetCtrl().AutoCompCancel()
332 self
.GetCtrl().AutoCompSetAutoHide(0)
333 self
.GetCtrl().AutoCompSetChooseSingle(True)
334 self
.GetCtrl().AutoCompSetIgnoreCase(True)
335 context
, hint
= self
.GetAutoCompleteHint()
336 replaceList
, replaceLen
= self
.GetAutoCompleteKeywordList(context
, hint
)
337 if replaceList
and len(replaceList
) != 0:
338 self
.GetCtrl().AutoCompShow(replaceLen
, replaceList
)
341 def GetAutoCompleteHint(self
):
342 """ Replace this method with Editor specific method """
343 pos
= self
.GetCtrl().GetCurrentPos()
346 if chr(self
.GetCtrl().GetCharAt(pos
- 1)) == '.':
352 validLetters
= string
.letters
+ string
.digits
+ '_.'
358 char
= chr(self
.GetCtrl().GetCharAt(pos
))
359 if char
not in validLetters
:
365 lastDot
= word
.rfind('.')
367 context
= word
[0:lastDot
]
368 hint
= word
[lastDot
+1:]
373 def GetAutoCompleteDefaultKeywords(self
):
374 """ Replace this method with Editor specific keywords """
375 return ['Put', 'Editor Specific', 'Keywords', 'Here']
378 def CaseInsensitiveCompare(self
, s1
, s2
):
379 """ GetAutoCompleteKeywordList() method used to show keywords in case insensitive order """
390 def GetAutoCompleteKeywordList(self
, context
, hint
):
391 """ Replace this method with Editor specific keywords """
392 kw
= self
.GetAutoCompleteDefaultKeywords()
394 if hint
and len(hint
):
395 lowerHint
= hint
.lower()
396 filterkw
= filter(lambda item
: item
.lower().startswith(lowerHint
), kw
) # remove variables and methods that don't match hint
400 replaceLen
= len(hint
)
404 kw
.sort(self
.CaseInsensitiveCompare
)
405 return " ".join(kw
), replaceLen
408 def OnCleanWhiteSpace(self
):
410 for lineNo
in self
._GetSelectedLineNumbers
():
411 lineText
= string
.rstrip(self
.GetCtrl().GetLine(lineNo
))
414 for char
in lineText
:
416 indent
= indent
+ self
.GetCtrl().GetIndent()
418 elif char
in string
.whitespace
:
423 if self
.GetCtrl().GetUseTabs():
424 indentText
= (indent
/ self
.GetCtrl().GetIndent()) * '\t' + (indent
% self
.GetCtrl().GetIndent()) * ' '
426 indentText
= indent
* ' '
427 lineText
= indentText
+ lineText
[lstrip
:] + '\n'
428 newText
= newText
+ lineText
429 self
._ReplaceSelectedLines
(newText
)
432 def OnSetIndentWidth(self
):
433 dialog
= wx
.TextEntryDialog(self
._GetParentFrame
(), _("Enter new indent width (2-10):"), _("Set Indent Width"), "%i" % self
.GetCtrl().GetIndent())
434 if dialog
.ShowModal() == wx
.ID_OK
:
436 indent
= int(dialog
.GetValue())
437 if indent
>= 2 and indent
<= 10:
438 self
.GetCtrl().SetIndent(indent
)
439 self
.GetCtrl().SetTabWidth(indent
)
445 def GetIndentWidth(self
):
446 return self
.GetCtrl().GetIndent()
449 def OnCommentLines(self
):
451 for lineNo
in self
._GetSelectedLineNumbers
():
452 lineText
= self
.GetCtrl().GetLine(lineNo
)
453 if (len(lineText
) > 1 and lineText
[0] == '#') or (len(lineText
) > 2 and lineText
[:2] == '##'):
454 newText
= newText
+ lineText
456 newText
= newText
+ "##" + lineText
457 self
._ReplaceSelectedLines
(newText
)
460 def OnUncommentLines(self
):
462 for lineNo
in self
._GetSelectedLineNumbers
():
463 lineText
= self
.GetCtrl().GetLine(lineNo
)
464 if len(lineText
) >= 2 and lineText
[:2] == "##":
465 lineText
= lineText
[2:]
466 elif len(lineText
) >= 1 and lineText
[:1] == "#":
467 lineText
= lineText
[1:]
468 newText
= newText
+ lineText
469 self
._ReplaceSelectedLines
(newText
)
472 def _GetSelectedLineNumbers(self
):
473 selStart
, selEnd
= self
._GetPositionsBoundingSelectedLines
()
474 return range(self
.GetCtrl().LineFromPosition(selStart
), self
.GetCtrl().LineFromPosition(selEnd
))
477 def _GetPositionsBoundingSelectedLines(self
):
478 startPos
= self
.GetCtrl().GetCurrentPos()
479 endPos
= self
.GetCtrl().GetAnchor()
480 if startPos
> endPos
:
484 if endPos
== self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(endPos
)):
485 endPos
= endPos
- 1 # If it's at the very beginning of a line, use the line above it as the ending line
486 selStart
= self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(startPos
))
487 selEnd
= self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(endPos
) + 1)
488 return selStart
, selEnd
491 def _ReplaceSelectedLines(self
, text
):
494 selStart
, selEnd
= self
._GetPositionsBoundingSelectedLines
()
495 self
.GetCtrl().SetSelection(selStart
, selEnd
)
496 self
.GetCtrl().ReplaceSelection(text
)
497 self
.GetCtrl().SetSelection(selStart
+ len(text
), selStart
)
500 def OnUpdate(self
, sender
= None, hint
= None):
501 if hint
== "ViewStuff":
502 self
.GetCtrl().SetViewDefaults()
504 font
, color
= self
.GetFontAndColorFromConfig()
505 self
.GetCtrl().SetFont(font
)
506 self
.GetCtrl().SetFontColor(color
)
508 dbg_service
= wx
.GetApp().GetService(DebuggerService
.DebuggerService
)
510 dbg_service
.SetCurrentBreakpointMarkers(self
)
513 class CodeService(STCTextEditor
.TextService
):
517 STCTextEditor
.TextService
.__init
__(self
)
520 def InstallControls(self
, frame
, menuBar
= None, toolBar
= None, statusBar
= None, document
= None):
521 # TODO NEED TO DO INSTANCEOF CHECK HERE FOR SDI
522 #if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument:
524 if not document
and wx
.GetApp().GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
:
527 viewMenu
= menuBar
.GetMenu(menuBar
.FindMenu(_("&View")))
528 isWindows
= (wx
.Platform
== '__WXMSW__')
530 if not menuBar
.FindItemById(EXPAND_TEXT_ID
): # check if below menu items have been already been installed
531 foldingMenu
= wx
.Menu()
533 foldingMenu
.Append(EXPAND_TEXT_ID
, _("&Expand\tNumpad-Plus"), _("Expands a collapsed block of text"))
535 foldingMenu
.Append(EXPAND_TEXT_ID
, _("&Expand"), _("Expands a collapsed block of text"))
537 wx
.EVT_MENU(frame
, EXPAND_TEXT_ID
, frame
.ProcessEvent
)
538 wx
.EVT_UPDATE_UI(frame
, EXPAND_TEXT_ID
, frame
.ProcessUpdateUIEvent
)
541 foldingMenu
.Append(COLLAPSE_TEXT_ID
, _("&Collapse\tNumpad+Minus"), _("Collapse a block of text"))
543 foldingMenu
.Append(COLLAPSE_TEXT_ID
, _("&Collapse"), _("Collapse a block of text"))
544 wx
.EVT_MENU(frame
, COLLAPSE_TEXT_ID
, frame
.ProcessEvent
)
545 wx
.EVT_UPDATE_UI(frame
, COLLAPSE_TEXT_ID
, frame
.ProcessUpdateUIEvent
)
548 foldingMenu
.Append(EXPAND_TOP_ID
, _("Expand &Top Level\tCtrl+Numpad+Plus"), _("Expands the top fold levels in the document"))
550 foldingMenu
.Append(EXPAND_TOP_ID
, _("Expand &Top Level"), _("Expands the top fold levels in the document"))
551 wx
.EVT_MENU(frame
, EXPAND_TOP_ID
, frame
.ProcessEvent
)
552 wx
.EVT_UPDATE_UI(frame
, EXPAND_TOP_ID
, frame
.ProcessUpdateUIEvent
)
555 foldingMenu
.Append(COLLAPSE_TOP_ID
, _("Collapse Top &Level\tCtrl+Numpad+Minus"), _("Collapses the top fold levels in the document"))
557 foldingMenu
.Append(COLLAPSE_TOP_ID
, _("Collapse Top &Level"), _("Collapses the top fold levels in the document"))
558 wx
.EVT_MENU(frame
, COLLAPSE_TOP_ID
, frame
.ProcessEvent
)
559 wx
.EVT_UPDATE_UI(frame
, COLLAPSE_TOP_ID
, frame
.ProcessUpdateUIEvent
)
562 foldingMenu
.Append(EXPAND_ALL_ID
, _("Expand &All\tShift+Numpad+Plus"), _("Expands all of the fold levels in the document"))
564 foldingMenu
.Append(EXPAND_ALL_ID
, _("Expand &All"), _("Expands all of the fold levels in the document"))
565 wx
.EVT_MENU(frame
, EXPAND_ALL_ID
, frame
.ProcessEvent
)
566 wx
.EVT_UPDATE_UI(frame
, EXPAND_ALL_ID
, frame
.ProcessUpdateUIEvent
)
569 foldingMenu
.Append(COLLAPSE_ALL_ID
, _("Colla&pse All\tShift+Numpad+Minus"), _("Collapses all of the fold levels in the document"))
571 foldingMenu
.Append(COLLAPSE_ALL_ID
, _("Colla&pse All"), _("Collapses all of the fold levels in the document"))
572 wx
.EVT_MENU(frame
, COLLAPSE_ALL_ID
, frame
.ProcessEvent
)
573 wx
.EVT_UPDATE_UI(frame
, COLLAPSE_ALL_ID
, frame
.ProcessUpdateUIEvent
)
575 viewMenu
.AppendMenu(FOLDING_ID
, _("&Folding"), foldingMenu
)
576 wx
.EVT_UPDATE_UI(frame
, FOLDING_ID
, frame
.ProcessUpdateUIEvent
)
578 formatMenuIndex
= menuBar
.FindMenu(_("&Format"))
579 if formatMenuIndex
> -1:
580 formatMenu
= menuBar
.GetMenu(formatMenuIndex
)
582 formatMenu
= wx
.Menu()
583 if not menuBar
.FindItemById(CHECK_CODE_ID
): # check if below menu items have been already been installed
584 formatMenu
.AppendSeparator()
585 formatMenu
.Append(CHECK_CODE_ID
, _("&Check Code"), _("Checks the document for syntax and indentation errors"))
586 wx
.EVT_MENU(frame
, CHECK_CODE_ID
, frame
.ProcessEvent
)
587 wx
.EVT_UPDATE_UI(frame
, CHECK_CODE_ID
, frame
.ProcessUpdateUIEvent
)
588 formatMenu
.Append(AUTO_COMPLETE_ID
, _("&Auto Complete\tCtrl+Space"), _("Provides suggestions on how to complete the current statement"))
589 wx
.EVT_MENU(frame
, AUTO_COMPLETE_ID
, frame
.ProcessEvent
)
590 wx
.EVT_UPDATE_UI(frame
, AUTO_COMPLETE_ID
, frame
.ProcessUpdateUIEvent
)
591 formatMenu
.Append(CLEAN_WHITESPACE
, _("Clean &Whitespace"), _("Converts leading spaces to tabs or vice versa per 'use tabs' and clears trailing spaces"))
592 wx
.EVT_MENU(frame
, CLEAN_WHITESPACE
, frame
.ProcessEvent
)
593 wx
.EVT_UPDATE_UI(frame
, CLEAN_WHITESPACE
, frame
.ProcessUpdateUIEvent
)
594 formatMenu
.AppendSeparator()
595 formatMenu
.Append(INDENT_LINES_ID
, _("&Indent Lines\tTab"), _("Indents the selected lines one indent width"))
596 wx
.EVT_MENU(frame
, INDENT_LINES_ID
, frame
.ProcessEvent
)
597 wx
.EVT_UPDATE_UI(frame
, INDENT_LINES_ID
, frame
.ProcessUpdateUIEvent
)
598 formatMenu
.Append(DEDENT_LINES_ID
, _("&Dedent Lines\tShift+Tab"), _("Dedents the selected lines one indent width"))
599 wx
.EVT_MENU(frame
, DEDENT_LINES_ID
, frame
.ProcessEvent
)
600 wx
.EVT_UPDATE_UI(frame
, DEDENT_LINES_ID
, frame
.ProcessUpdateUIEvent
)
601 formatMenu
.Append(COMMENT_LINES_ID
, _("Comment &Lines\tCtrl+Q"), _("Comments out the selected lines be prefixing each one with a comment indicator"))
602 wx
.EVT_MENU(frame
, COMMENT_LINES_ID
, frame
.ProcessEvent
)
603 wx
.EVT_UPDATE_UI(frame
, COMMENT_LINES_ID
, frame
.ProcessUpdateUIEvent
)
604 formatMenu
.Append(UNCOMMENT_LINES_ID
, _("&Uncomment Lines\tCtrl+Shift+Q"), _("Removes comment prefixes from each of the selected lines"))
605 wx
.EVT_MENU(frame
, UNCOMMENT_LINES_ID
, frame
.ProcessEvent
)
606 wx
.EVT_UPDATE_UI(frame
, UNCOMMENT_LINES_ID
, frame
.ProcessUpdateUIEvent
)
607 formatMenu
.AppendSeparator()
608 formatMenu
.AppendCheckItem(USE_TABS_ID
, _("Use &Tabs"), _("Toggles use of tabs or whitespaces for indents"))
609 wx
.EVT_MENU(frame
, USE_TABS_ID
, frame
.ProcessEvent
)
610 wx
.EVT_UPDATE_UI(frame
, USE_TABS_ID
, frame
.ProcessUpdateUIEvent
)
611 formatMenu
.Append(SET_INDENT_WIDTH_ID
, _("&Set Indent Width..."), _("Sets the indent width"))
612 wx
.EVT_MENU(frame
, SET_INDENT_WIDTH_ID
, frame
.ProcessEvent
)
613 wx
.EVT_UPDATE_UI(frame
, SET_INDENT_WIDTH_ID
, frame
.ProcessUpdateUIEvent
)
614 if formatMenuIndex
== -1:
615 viewMenuIndex
= menuBar
.FindMenu(_("&View"))
616 menuBar
.Insert(viewMenuIndex
+ 1, formatMenu
, _("&Format"))
618 ## accelTable = wx.AcceleratorTable([
619 ## (wx.ACCEL_NORMAL, wx.WXK_TAB, INDENT_LINES_ID),
620 ## (wx.ACCEL_SHIFT, wx.WXK_TAB, DEDENT_LINES_ID),
621 ## eval(_("wx.ACCEL_CTRL, ord('Q'), COMMENT_LINES_ID")),
622 ## eval(_("wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord('Q'), UNCOMMENT_LINES_ID"))
624 ## frame.SetAcceleratorTable(accelTable)
626 def ProcessUpdateUIEvent(self
, event
):
628 if id == EXPAND_TEXT_ID
:
631 elif id == COLLAPSE_TEXT_ID
:
634 elif id == EXPAND_TOP_ID
:
637 elif id == COLLAPSE_TOP_ID
:
640 elif id == EXPAND_ALL_ID
:
643 elif id == COLLAPSE_ALL_ID
:
646 elif id == CHECK_CODE_ID
:
649 elif id == AUTO_COMPLETE_ID
:
652 elif id == CLEAN_WHITESPACE
:
655 elif id == SET_INDENT_WIDTH_ID
:
658 elif id == USE_TABS_ID
:
661 elif id == INDENT_LINES_ID
:
664 elif id == DEDENT_LINES_ID
:
667 elif id == COMMENT_LINES_ID
:
670 elif id == UNCOMMENT_LINES_ID
:
673 elif id == FOLDING_ID
:
677 return STCTextEditor
.TextService
.ProcessUpdateUIEvent(self
, event
)
680 class CodeCtrl(STCTextEditor
.TextCtrl
):
681 CURRENT_LINE_MARKER_NUM
= 2
682 BREAKPOINT_MARKER_NUM
= 1
683 CURRENT_LINE_MARKER_MASK
= 0x4
684 BREAKPOINT_MARKER_MASK
= 0x2
687 def __init__(self
, parent
, ID
= -1, style
= wx
.NO_FULL_REPAINT_ON_RESIZE
):
690 STCTextEditor
.TextCtrl
.__init
__(self
, parent
, ID
, style
)
693 self
.Bind(wx
.EVT_RIGHT_UP
, self
.OnRightUp
)
694 self
.SetProperty("fold", "1")
696 # Setup a margin to hold fold markers
697 #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
698 self
.SetMarginType(2, wx
.stc
.STC_MARGIN_SYMBOL
)
699 self
.SetMarginMask(2, wx
.stc
.STC_MASK_FOLDERS
)
700 self
.SetMarginSensitive(2, True)
701 self
.SetMarginWidth(2, 12)
703 self
.SetMarginSensitive(1, False)
704 self
.SetMarginMask(1, 0x4)
706 self
.SetMarginSensitive(0, True)
707 self
.SetMarginType(0, wx
.stc
.STC_MARGIN_SYMBOL
)
708 self
.SetMarginMask(0, 0x3)
709 self
.SetMarginWidth(0, 12)
711 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEREND
, wx
.stc
.STC_MARK_BOXPLUSCONNECTED
, "white", "black")
712 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEROPENMID
, wx
.stc
.STC_MARK_BOXMINUSCONNECTED
, "white", "black")
713 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERMIDTAIL
, wx
.stc
.STC_MARK_TCORNER
, "white", "black")
714 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERTAIL
, wx
.stc
.STC_MARK_LCORNER
, "white", "black")
715 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERSUB
, wx
.stc
.STC_MARK_VLINE
, "white", "black")
716 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDER
, wx
.stc
.STC_MARK_BOXPLUS
, "white", "black")
717 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEROPEN
, wx
.stc
.STC_MARK_BOXMINUS
, "white", "black")
718 # Define the current line marker
719 self
.MarkerDefine(CodeCtrl
.CURRENT_LINE_MARKER_NUM
, wx
.stc
.STC_MARK_SHORTARROW
, wx
.BLACK
, (255,255,128))
720 # Define the breakpoint marker
721 self
.MarkerDefine(CodeCtrl
.BREAKPOINT_MARKER_NUM
, wx
.stc
.STC_MARK_CIRCLE
, wx
.BLACK
, (255,0,0))
723 if _WINDOWS
: # should test to see if menu item exists, if it does, add this workaround
724 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
726 wx
.stc
.EVT_STC_MARGINCLICK(self
, ID
, self
.OnMarginClick
)
727 wx
.EVT_KEY_DOWN(self
, self
.OnKeyPressed
)
728 if self
.GetMatchingBraces():
729 wx
.stc
.EVT_STC_UPDATEUI(self
, ID
, self
.OnUpdateUI
)
735 def OnRightUp(self
, event
):
736 #Hold onto the current line number, no way to get it later.
737 self
._rightClickPosition
= self
.PositionFromPoint(event
.GetPosition())
738 self
._rightClickLine
= self
.LineFromPosition(self
._rightClickPosition
)
739 self
.PopupMenu(self
.CreatePopupMenu(), event
.GetPosition())
740 self
._rightClickLine
= -1
741 self
._rightClickPosition
= -1
744 def CreatePopupMenu(self
):
745 TOGGLEBREAKPOINT_ID
= wx
.NewId()
746 TOGGLEMARKER_ID
= wx
.NewId()
747 SYNCTREE_ID
= wx
.NewId()
751 self
.Bind(wx
.EVT_MENU
, self
.OnPopSyncOutline
, id=SYNCTREE_ID
)
752 item
= wx
.MenuItem(menu
, SYNCTREE_ID
, _("Find in Outline View"))
753 menu
.AppendItem(item
)
754 menu
.AppendSeparator()
755 self
.Bind(wx
.EVT_MENU
, self
.OnPopToggleBP
, id=TOGGLEBREAKPOINT_ID
)
756 item
= wx
.MenuItem(menu
, TOGGLEBREAKPOINT_ID
, _("Toggle Breakpoint"))
757 menu
.AppendItem(item
)
758 self
.Bind(wx
.EVT_MENU
, self
.OnPopToggleMarker
, id=TOGGLEMARKER_ID
)
759 item
= wx
.MenuItem(menu
, TOGGLEMARKER_ID
, _("Toggle Marker"))
760 menu
.AppendItem(item
)
761 menu
.AppendSeparator()
763 itemIDs
= [wx
.ID_UNDO
, wx
.ID_REDO
, None,
764 wx
.ID_CUT
, wx
.ID_COPY
, wx
.ID_PASTE
, wx
.ID_CLEAR
, None, wx
.ID_SELECTALL
]
766 menuBar
= wx
.GetApp().GetTopWindow().GetMenuBar()
767 for itemID
in itemIDs
:
769 menu
.AppendSeparator()
771 item
= menuBar
.FindItemById(itemID
)
773 menu
.Append(itemID
, item
.GetLabel())
778 def OnPopToggleBP(self
, event
):
779 """ Toggle break point on right click line, not current line """
780 wx
.GetApp().GetService(DebuggerService
.DebuggerService
).OnToggleBreakpoint(event
, line
=self
._rightClickLine
)
783 def OnPopToggleMarker(self
, event
):
784 """ Toggle marker on right click line, not current line """
785 wx
.GetApp().GetDocumentManager().GetCurrentView().MarkerToggle(lineNum
= self
._rightClickLine
)
788 def OnPopSyncOutline(self
, event
):
789 wx
.GetApp().GetService(OutlineService
.OutlineService
).LoadOutline(wx
.GetApp().GetDocumentManager().GetCurrentView(), position
=self
._rightClickPosition
)
792 def HasSelection(self
):
793 return self
.GetSelectionStart() - self
.GetSelectionEnd() != 0
796 def ClearCurrentLineMarkers(self
):
797 self
.MarkerDeleteAll(CodeCtrl
.CURRENT_LINE_MARKER_NUM
)
800 def ClearCurrentBreakpoinMarkers(self
):
801 self
.MarkerDeleteAll(CodeCtrl
.BREAKPOINT_MARKER_NUM
)
804 def GetDefaultFont(self
):
805 if wx
.Platform
== '__WXMSW__':
809 return wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
, faceName
= font
)
812 def GetMatchingBraces(self
):
813 """ Overwrite this method for language specific braces """
817 def CanWordWrap(self
):
821 def SetFont(self
, font
):
825 def SetFontColor(self
, fontColor
):
826 self
._fontColor
= fontColor
829 def UpdateStyles(self
):
831 if not self
.GetFont():
834 faces
= { 'font' : self
.GetFont().GetFaceName(),
835 'size' : self
.GetFont().GetPointSize(),
836 'size2': self
.GetFont().GetPointSize() - 2,
837 'color' : "%02x%02x%02x" % (self
.GetFontColor().Red(), self
.GetFontColor().Green(), self
.GetFontColor().Blue())
840 # Global default styles for all languages
841 self
.StyleSetSpec(wx
.stc
.STC_STYLE_DEFAULT
, "face:%(font)s,fore:#FFFFFF,size:%(size)d" % faces
)
842 self
.StyleSetSpec(wx
.stc
.STC_STYLE_LINENUMBER
, "face:%(font)s,back:#C0C0C0,face:%(font)s,size:%(size2)d" % faces
)
843 self
.StyleSetSpec(wx
.stc
.STC_STYLE_CONTROLCHAR
, "face:%(font)s" % faces
)
844 self
.StyleSetSpec(wx
.stc
.STC_STYLE_BRACELIGHT
, "face:%(font)s,fore:#000000,back:#70FFFF,size:%(size)d" % faces
)
845 self
.StyleSetSpec(wx
.stc
.STC_STYLE_BRACEBAD
, "face:%(font)s,fore:#000000,back:#FF0000,size:%(size)d" % faces
)
848 def OnKeyPressed(self
, event
):
849 if self
.CallTipActive():
851 key
= event
.KeyCode()
852 if False: # key == wx.WXK_SPACE and event.ControlDown():
853 pos
= self
.GetCurrentPos()
855 if event
.ShiftDown():
856 self
.CallTipSetBackground("yellow")
857 self
.CallTipShow(pos
, 'param1, param2')
861 #for x in range(50000):
862 # lst.append('%05d' % x)
863 #st = string.join(lst)
865 #self.AutoCompShow(0, st)
867 kw
= keyword
.kwlist
[:]
870 kw
.append("__init__")
873 kw
.append("this_is_a_longer_value")
874 kw
.append("this_is_a_much_much_much_much_much_much_much_longer_value")
876 kw
.sort() # Python sorts are case sensitive
877 self
.AutoCompSetIgnoreCase(False) # so this needs to match
879 self
.AutoCompShow(0, string
.join(kw
))
880 elif key
== wx
.WXK_RETURN
:
883 STCTextEditor
.TextCtrl
.OnKeyPressed(self
, event
)
888 # Need to do a default one for all languges
891 def OnMarginClick(self
, evt
):
892 # fold and unfold as needed
893 if evt
.GetMargin() == 2:
894 if evt
.GetShift() and evt
.GetControl():
895 lineCount
= self
.GetLineCount()
898 # find out if we are folding or unfolding
899 for lineNum
in range(lineCount
):
900 if self
.GetFoldLevel(lineNum
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
:
901 expanding
= not self
.GetFoldExpanded(lineNum
)
904 self
.ToggleFoldAll(expanding
)
906 lineClicked
= self
.LineFromPosition(evt
.GetPosition())
907 if self
.GetFoldLevel(lineClicked
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
:
909 self
.SetFoldExpanded(lineClicked
, True)
910 self
.Expand(lineClicked
, True, True, 1)
911 elif evt
.GetControl():
912 if self
.GetFoldExpanded(lineClicked
):
913 self
.SetFoldExpanded(lineClicked
, False)
914 self
.Expand(lineClicked
, False, True, 0)
916 self
.SetFoldExpanded(lineClicked
, True)
917 self
.Expand(lineClicked
, True, True, 100)
919 self
.ToggleFold(lineClicked
)
921 elif evt
.GetMargin() == 0:
922 #This is used to toggle breakpoints via the debugger service.
923 db_service
= wx
.GetApp().GetService(DebuggerService
.DebuggerService
)
925 db_service
.OnToggleBreakpoint(evt
, line
=self
.LineFromPosition(evt
.GetPosition()))
928 def OnUpdateUI(self
, evt
):
929 braces
= self
.GetMatchingBraces()
931 # check for matching braces
935 caretPos
= self
.GetCurrentPos()
937 charBefore
= self
.GetCharAt(caretPos
- 1)
938 styleBefore
= self
.GetStyleAt(caretPos
- 1)
941 if charBefore
and chr(charBefore
) in braces
:
942 braceAtCaret
= caretPos
- 1
946 charAfter
= self
.GetCharAt(caretPos
)
947 styleAfter
= self
.GetStyleAt(caretPos
)
948 if charAfter
and chr(charAfter
) in braces
:
949 braceAtCaret
= caretPos
951 if braceAtCaret
>= 0:
952 braceOpposite
= self
.BraceMatch(braceAtCaret
)
954 if braceAtCaret
!= -1 and braceOpposite
== -1:
955 self
.BraceBadLight(braceAtCaret
)
957 self
.BraceHighlight(braceAtCaret
, braceOpposite
)
962 def ToggleFoldAll(self
, expand
= True, topLevelOnly
= False):
964 lineCount
= self
.GetLineCount()
966 if not topLevelOnly
or (topLevelOnly
and self
.GetFoldLevel(i
) & wx
.stc
.STC_FOLDLEVELNUMBERMASK
== wx
.stc
.STC_FOLDLEVELBASE
):
967 if (expand
and self
.CanLineExpand(i
)) or (not expand
and self
.CanLineCollapse(i
)):
972 def CanLineExpand(self
, line
):
973 return not self
.GetFoldExpanded(line
)
976 def CanLineCollapse(self
, line
):
977 return self
.GetFoldExpanded(line
) and self
.GetFoldLevel(line
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
980 def Expand(self
, line
, doExpand
, force
=False, visLevels
=0, level
=-1):
981 lastChild
= self
.GetLastChild(line
, level
)
983 while line
<= lastChild
:
986 self
.ShowLines(line
, line
)
988 self
.HideLines(line
, line
)
991 self
.ShowLines(line
, line
)
994 level
= self
.GetFoldLevel(line
)
996 if level
& wx
.stc
.STC_FOLDLEVELHEADERFLAG
:
999 self
.SetFoldExpanded(line
, True)
1001 self
.SetFoldExpanded(line
, False)
1002 line
= self
.Expand(line
, doExpand
, force
, visLevels
-1)
1005 if doExpand
and self
.GetFoldExpanded(line
):
1006 line
= self
.Expand(line
, True, force
, visLevels
-1)
1008 line
= self
.Expand(line
, False, force
, visLevels
-1)