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 #----------------------------------------------------------------------------
23 from UICommon
import CaseInsensitiveCompare
25 if wx
.Platform
== '__WXMSW__':
31 EXPAND_TEXT_ID
= wx
.NewId()
32 COLLAPSE_TEXT_ID
= wx
.NewId()
33 EXPAND_TOP_ID
= wx
.NewId()
34 COLLAPSE_TOP_ID
= wx
.NewId()
35 EXPAND_ALL_ID
= wx
.NewId()
36 COLLAPSE_ALL_ID
= wx
.NewId()
37 CHECK_CODE_ID
= wx
.NewId()
38 AUTO_COMPLETE_ID
= wx
.NewId()
39 CLEAN_WHITESPACE
= wx
.NewId()
40 COMMENT_LINES_ID
= wx
.NewId()
41 UNCOMMENT_LINES_ID
= wx
.NewId()
42 INDENT_LINES_ID
= wx
.NewId()
43 DEDENT_LINES_ID
= wx
.NewId()
44 USE_TABS_ID
= wx
.NewId()
45 SET_INDENT_WIDTH_ID
= wx
.NewId()
46 FOLDING_ID
= wx
.NewId()
49 class CodeDocument(STCTextEditor
.TextDocument
):
53 class CodeView(STCTextEditor
.TextView
):
56 #----------------------------------------------------------------------------
58 #----------------------------------------------------------------------------
61 def GetCtrlClass(self
):
62 """ Used in split window to instantiate new instances """
66 def ProcessEvent(self
, event
):
68 if id == EXPAND_TEXT_ID
:
69 self
.GetCtrl().ToggleFold(self
.GetCtrl().GetCurrentLine())
71 elif id == COLLAPSE_TEXT_ID
:
72 self
.GetCtrl().ToggleFold(self
.GetCtrl().GetCurrentLine())
74 elif id == EXPAND_TOP_ID
:
75 self
.GetCtrl().ToggleFoldAll(expand
= True, topLevelOnly
= True)
77 elif id == COLLAPSE_TOP_ID
:
78 self
.GetCtrl().ToggleFoldAll(expand
= False, topLevelOnly
= True)
80 elif id == EXPAND_ALL_ID
:
81 self
.GetCtrl().ToggleFoldAll(expand
= True)
83 elif id == COLLAPSE_ALL_ID
:
84 self
.GetCtrl().ToggleFoldAll(expand
= False)
86 elif id == CHECK_CODE_ID
:
89 elif id == AUTO_COMPLETE_ID
:
92 elif id == CLEAN_WHITESPACE
:
93 self
.OnCleanWhiteSpace()
95 elif id == SET_INDENT_WIDTH_ID
:
96 self
.OnSetIndentWidth()
98 elif id == USE_TABS_ID
:
99 self
.GetCtrl().SetUseTabs(not self
.GetCtrl().GetUseTabs())
101 elif id == INDENT_LINES_ID
:
102 self
.GetCtrl().CmdKeyExecute(wx
.stc
.STC_CMD_TAB
)
104 elif id == DEDENT_LINES_ID
:
105 self
.GetCtrl().CmdKeyExecute(wx
.stc
.STC_CMD_BACKTAB
)
107 elif id == COMMENT_LINES_ID
:
108 self
.OnCommentLines()
110 elif id == UNCOMMENT_LINES_ID
:
111 self
.OnUncommentLines()
114 return STCTextEditor
.TextView
.ProcessEvent(self
, event
)
117 def ProcessUpdateUIEvent(self
, event
):
118 if not self
.GetCtrl():
121 if id == EXPAND_TEXT_ID
:
122 if self
.GetCtrl().GetViewFolding():
123 event
.Enable(self
.GetCtrl().CanLineExpand(self
.GetCtrl().GetCurrentLine()))
127 elif id == COLLAPSE_TEXT_ID
:
128 if self
.GetCtrl().GetViewFolding():
129 event
.Enable(self
.GetCtrl().CanLineCollapse(self
.GetCtrl().GetCurrentLine()))
133 elif (id == EXPAND_TOP_ID
134 or id == COLLAPSE_TOP_ID
135 or id == EXPAND_ALL_ID
136 or id == COLLAPSE_ALL_ID
):
137 if self
.GetCtrl().GetViewFolding():
138 event
.Enable(self
.GetCtrl().GetTextLength() > 0)
142 elif (id == AUTO_COMPLETE_ID
143 or id == CLEAN_WHITESPACE
144 or id == INDENT_LINES_ID
145 or id == DEDENT_LINES_ID
146 or id == COMMENT_LINES_ID
147 or id == UNCOMMENT_LINES_ID
):
148 event
.Enable(self
.GetCtrl().GetTextLength() > 0)
150 elif id == CHECK_CODE_ID
:
153 elif id == SET_INDENT_WIDTH_ID
:
156 elif id == FOLDING_ID
:
157 event
.Enable(self
.GetCtrl().GetViewFolding())
159 elif id == USE_TABS_ID
:
161 event
.Check(self
.GetCtrl().GetUseTabs())
164 return STCTextEditor
.TextView
.ProcessUpdateUIEvent(self
, event
)
167 #----------------------------------------------------------------------------
168 # Methods for OutlineService
169 #----------------------------------------------------------------------------
171 def OnChangeFilename(self
):
172 wx
.lib
.docview
.View
.OnChangeFilename(self
)
173 self
.LoadOutline(force
=True)
176 def ClearOutline(self
):
177 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
178 if not outlineService
:
181 outlineView
= outlineService
.GetView()
185 outlineView
.ClearTreeCtrl()
188 def LoadOutline(self
, force
=False):
189 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
190 if not outlineService
:
192 outlineService
.LoadOutline(self
, force
=force
)
195 def DoLoadOutlineCallback(self
, force
=False):
196 outlineService
= wx
.GetApp().GetService(OutlineService
.OutlineService
)
197 if not outlineService
:
200 outlineView
= outlineService
.GetView()
204 treeCtrl
= outlineView
.GetTreeCtrl()
208 view
= treeCtrl
.GetCallbackView()
209 newCheckSum
= self
.GenCheckSum()
211 if view
and view
is self
:
212 if self
._checkSum
== newCheckSum
:
214 self
._checkSum
= newCheckSum
216 treeCtrl
.DeleteAllItems()
218 document
= self
.GetDocument()
222 filename
= document
.GetFilename()
224 rootItem
= treeCtrl
.AddRoot(os
.path
.basename(filename
))
225 treeCtrl
.SetDoSelectCallback(rootItem
, self
, (0,0))
229 text
= self
.GetValue()
233 CLASS_PATTERN
= 'class[ \t]+\w+.*?:'
234 DEF_PATTERN
= 'def[ \t]+\w+\(.*?\)'
235 classPat
= re
.compile(CLASS_PATTERN
, re
.M|re
.S
)
236 defPat
= re
.compile(DEF_PATTERN
, re
.M|re
.S
)
237 pattern
= re
.compile('^[ \t]*((' + CLASS_PATTERN
+ ')|('+ DEF_PATTERN
+'.*?:)).*?$', re
.M|re
.S
)
239 iter = pattern
.finditer(text
)
240 indentStack
= [(0, rootItem
)]
242 line
= pattern
.string
[pattern
.start(0):pattern
.end(0)]
243 classLine
= classPat
.search(line
)
245 indent
= classLine
.start(0)
246 itemStr
= classLine
.string
[classLine
.start(0):classLine
.end(0)-1] # don't take the closing ':'
247 itemStr
= itemStr
.replace("\n", "").replace("\r", "").replace(",\\", ",").replace(" ", "") # remove line continuations and spaces from outline view
249 defLine
= defPat
.search(line
)
251 indent
= defLine
.start(0)
252 itemStr
= defLine
.string
[defLine
.start(0):defLine
.end(0)]
253 itemStr
= itemStr
.replace("\n", "").replace("\r", "").replace(",\\", ",").replace(" ", "") # remove line continuations and spaces from outline view
256 parentItem
= rootItem
258 lastItem
= indentStack
.pop()
259 while lastItem
[0] >= indent
:
260 lastItem
= indentStack
.pop()
261 indentStack
.append(lastItem
)
262 parentItem
= lastItem
[1]
264 item
= treeCtrl
.AppendItem(parentItem
, itemStr
)
265 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
266 indentStack
.append((indent
, item
))
268 treeCtrl
.Expand(rootItem
)
273 def DoSelectCallback(self
, data
):
275 self
.EnsureVisibleEnforcePolicy(self
.LineFromPosition(data
[0]))
276 # wxBug: need to select in reverse order (end, start) to place cursor at begining of line,
277 # otherwise, display is scrolled over to the right hard and is hard to view
278 self
.SetSelection(data
[1], data
[0])
281 ## def checksum(self, bytes):
282 ## def rotate_right(c):
284 ## return (c>>1)|0x8000
290 ## ch = ord(ch) & 0xFF
291 ## result = (rotate_right(result)+ch) & 0xFFFF
295 def GenCheckSum(self
):
296 """ Poor man's checksum. We'll assume most changes will change the length of the file.
298 text
= self
.GetValue()
305 #----------------------------------------------------------------------------
307 #----------------------------------------------------------------------------
309 def OnCheckCode(self
):
310 """ Need to overshadow this for each specific subclass """
313 code
= self
.GetCtrl().GetText()
314 codeObj
= compile(code
, self
.GetDocument().GetFilename(), 'exec')
315 self
._GetParentFrame
().SetStatusText(_("The file successfully compiled"))
316 except SyntaxError, (message
, (fileName
, line
, col
, text
)):
317 pos
= self
.GetCtrl().PositionFromLine(line
- 1) + col
- 1
318 self
.GetCtrl().SetSelection(pos
, pos
)
319 self
._GetParentFrame
().SetStatusText(_("Syntax Error: %s") % message
)
321 self
._GetParentFrame
().SetStatusText("%s: %s" % (sys
.exc_info()[0], sys
.exc_info()[1]))
324 def OnAutoComplete(self
):
325 self
.GetCtrl().AutoCompCancel()
326 self
.GetCtrl().AutoCompSetAutoHide(0)
327 self
.GetCtrl().AutoCompSetChooseSingle(True)
328 self
.GetCtrl().AutoCompSetIgnoreCase(True)
329 context
, hint
= self
.GetAutoCompleteHint()
330 replaceList
, replaceLen
= self
.GetAutoCompleteKeywordList(context
, hint
)
331 if replaceList
and len(replaceList
) != 0:
332 self
.GetCtrl().AutoCompShow(replaceLen
, replaceList
)
335 def GetAutoCompleteHint(self
):
336 """ Replace this method with Editor specific method """
337 pos
= self
.GetCtrl().GetCurrentPos()
340 if chr(self
.GetCtrl().GetCharAt(pos
- 1)) == '.':
346 validLetters
= string
.letters
+ string
.digits
+ '_.'
352 char
= chr(self
.GetCtrl().GetCharAt(pos
))
353 if char
not in validLetters
:
359 lastDot
= word
.rfind('.')
361 context
= word
[0:lastDot
]
362 hint
= word
[lastDot
+1:]
367 def GetAutoCompleteDefaultKeywords(self
):
368 """ Replace this method with Editor specific keywords """
369 return ['Put', 'Editor Specific', 'Keywords', 'Here']
372 def GetAutoCompleteKeywordList(self
, context
, hint
):
373 """ Replace this method with Editor specific keywords """
374 kw
= self
.GetAutoCompleteDefaultKeywords()
376 if hint
and len(hint
):
377 lowerHint
= hint
.lower()
378 filterkw
= filter(lambda item
: item
.lower().startswith(lowerHint
), kw
) # remove variables and methods that don't match hint
382 replaceLen
= len(hint
)
386 kw
.sort(CaseInsensitiveCompare
)
387 return " ".join(kw
), replaceLen
390 def OnCleanWhiteSpace(self
):
392 for lineNo
in self
._GetSelectedLineNumbers
():
393 lineText
= string
.rstrip(self
.GetCtrl().GetLine(lineNo
))
396 for char
in lineText
:
398 indent
= indent
+ self
.GetCtrl().GetIndent()
400 elif char
in string
.whitespace
:
405 if self
.GetCtrl().GetUseTabs():
406 indentText
= (indent
/ self
.GetCtrl().GetIndent()) * '\t' + (indent
% self
.GetCtrl().GetIndent()) * ' '
408 indentText
= indent
* ' '
409 lineText
= indentText
+ lineText
[lstrip
:] + '\n'
410 newText
= newText
+ lineText
411 self
._ReplaceSelectedLines
(newText
)
414 def OnSetIndentWidth(self
):
415 dialog
= wx
.TextEntryDialog(self
._GetParentFrame
(), _("Enter new indent width (2-10):"), _("Set Indent Width"), "%i" % self
.GetCtrl().GetIndent())
416 dialog
.CenterOnParent()
417 if dialog
.ShowModal() == wx
.ID_OK
:
419 indent
= int(dialog
.GetValue())
420 if indent
>= 2 and indent
<= 10:
421 self
.GetCtrl().SetIndent(indent
)
422 self
.GetCtrl().SetTabWidth(indent
)
428 def GetIndentWidth(self
):
429 return self
.GetCtrl().GetIndent()
432 def OnCommentLines(self
):
434 for lineNo
in self
._GetSelectedLineNumbers
():
435 lineText
= self
.GetCtrl().GetLine(lineNo
)
436 if (len(lineText
) > 1 and lineText
[0] == '#') or (len(lineText
) > 2 and lineText
[:2] == '##'):
437 newText
= newText
+ lineText
439 newText
= newText
+ "##" + lineText
440 self
._ReplaceSelectedLines
(newText
)
443 def OnUncommentLines(self
):
445 for lineNo
in self
._GetSelectedLineNumbers
():
446 lineText
= self
.GetCtrl().GetLine(lineNo
)
447 if len(lineText
) >= 2 and lineText
[:2] == "##":
448 lineText
= lineText
[2:]
449 elif len(lineText
) >= 1 and lineText
[:1] == "#":
450 lineText
= lineText
[1:]
451 newText
= newText
+ lineText
452 self
._ReplaceSelectedLines
(newText
)
455 def _GetSelectedLineNumbers(self
):
456 selStart
, selEnd
= self
._GetPositionsBoundingSelectedLines
()
457 return range(self
.GetCtrl().LineFromPosition(selStart
), self
.GetCtrl().LineFromPosition(selEnd
))
460 def _GetPositionsBoundingSelectedLines(self
):
461 startPos
= self
.GetCtrl().GetCurrentPos()
462 endPos
= self
.GetCtrl().GetAnchor()
463 if startPos
> endPos
:
467 if endPos
== self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(endPos
)):
468 endPos
= endPos
- 1 # If it's at the very beginning of a line, use the line above it as the ending line
469 selStart
= self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(startPos
))
470 selEnd
= self
.GetCtrl().PositionFromLine(self
.GetCtrl().LineFromPosition(endPos
) + 1)
471 return selStart
, selEnd
474 def _ReplaceSelectedLines(self
, text
):
477 selStart
, selEnd
= self
._GetPositionsBoundingSelectedLines
()
478 self
.GetCtrl().SetSelection(selStart
, selEnd
)
479 self
.GetCtrl().ReplaceSelection(text
)
480 self
.GetCtrl().SetSelection(selStart
+ len(text
), selStart
)
483 def OnUpdate(self
, sender
= None, hint
= None):
484 if wx
.lib
.docview
.View
.OnUpdate(self
, sender
, hint
):
487 if hint
== "ViewStuff":
488 self
.GetCtrl().SetViewDefaults()
490 font
, color
= self
.GetCtrl().GetFontAndColorFromConfig()
491 self
.GetCtrl().SetFont(font
)
492 self
.GetCtrl().SetFontColor(color
)
494 import DebuggerService
495 dbg_service
= wx
.GetApp().GetService(DebuggerService
.DebuggerService
)
497 dbg_service
.SetCurrentBreakpointMarkers(self
)
500 class CodeService(STCTextEditor
.TextService
):
504 STCTextEditor
.TextService
.__init
__(self
)
507 def InstallControls(self
, frame
, menuBar
= None, toolBar
= None, statusBar
= None, document
= None):
508 # TODO NEED TO DO INSTANCEOF CHECK HERE FOR SDI
509 #if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument:
511 if not document
and wx
.GetApp().GetDocumentManager().GetFlags() & wx
.lib
.docview
.DOC_SDI
:
514 viewMenu
= menuBar
.GetMenu(menuBar
.FindMenu(_("&View")))
515 isWindows
= (wx
.Platform
== '__WXMSW__')
517 if not menuBar
.FindItemById(EXPAND_TEXT_ID
): # check if below menu items have been already been installed
518 foldingMenu
= wx
.Menu()
520 foldingMenu
.Append(EXPAND_TEXT_ID
, _("&Expand\tNumpad-Plus"), _("Expands a collapsed block of text"))
522 foldingMenu
.Append(EXPAND_TEXT_ID
, _("&Expand"), _("Expands a collapsed block of text"))
524 wx
.EVT_MENU(frame
, EXPAND_TEXT_ID
, frame
.ProcessEvent
)
525 wx
.EVT_UPDATE_UI(frame
, EXPAND_TEXT_ID
, frame
.ProcessUpdateUIEvent
)
528 foldingMenu
.Append(COLLAPSE_TEXT_ID
, _("&Collapse\tNumpad+Minus"), _("Collapse a block of text"))
530 foldingMenu
.Append(COLLAPSE_TEXT_ID
, _("&Collapse"), _("Collapse a block of text"))
531 wx
.EVT_MENU(frame
, COLLAPSE_TEXT_ID
, frame
.ProcessEvent
)
532 wx
.EVT_UPDATE_UI(frame
, COLLAPSE_TEXT_ID
, frame
.ProcessUpdateUIEvent
)
535 foldingMenu
.Append(EXPAND_TOP_ID
, _("Expand &Top Level\tCtrl+Numpad+Plus"), _("Expands the top fold levels in the document"))
537 foldingMenu
.Append(EXPAND_TOP_ID
, _("Expand &Top Level"), _("Expands the top fold levels in the document"))
538 wx
.EVT_MENU(frame
, EXPAND_TOP_ID
, frame
.ProcessEvent
)
539 wx
.EVT_UPDATE_UI(frame
, EXPAND_TOP_ID
, frame
.ProcessUpdateUIEvent
)
542 foldingMenu
.Append(COLLAPSE_TOP_ID
, _("Collapse Top &Level\tCtrl+Numpad+Minus"), _("Collapses the top fold levels in the document"))
544 foldingMenu
.Append(COLLAPSE_TOP_ID
, _("Collapse Top &Level"), _("Collapses the top fold levels in the document"))
545 wx
.EVT_MENU(frame
, COLLAPSE_TOP_ID
, frame
.ProcessEvent
)
546 wx
.EVT_UPDATE_UI(frame
, COLLAPSE_TOP_ID
, frame
.ProcessUpdateUIEvent
)
549 foldingMenu
.Append(EXPAND_ALL_ID
, _("Expand &All\tShift+Numpad+Plus"), _("Expands all of the fold levels in the document"))
551 foldingMenu
.Append(EXPAND_ALL_ID
, _("Expand &All"), _("Expands all of the fold levels in the document"))
552 wx
.EVT_MENU(frame
, EXPAND_ALL_ID
, frame
.ProcessEvent
)
553 wx
.EVT_UPDATE_UI(frame
, EXPAND_ALL_ID
, frame
.ProcessUpdateUIEvent
)
556 foldingMenu
.Append(COLLAPSE_ALL_ID
, _("Colla&pse All\tShift+Numpad+Minus"), _("Collapses all of the fold levels in the document"))
558 foldingMenu
.Append(COLLAPSE_ALL_ID
, _("Colla&pse All"), _("Collapses all of the fold levels in the document"))
559 wx
.EVT_MENU(frame
, COLLAPSE_ALL_ID
, frame
.ProcessEvent
)
560 wx
.EVT_UPDATE_UI(frame
, COLLAPSE_ALL_ID
, frame
.ProcessUpdateUIEvent
)
562 viewMenu
.AppendMenu(FOLDING_ID
, _("&Folding"), foldingMenu
)
563 wx
.EVT_UPDATE_UI(frame
, FOLDING_ID
, frame
.ProcessUpdateUIEvent
)
565 formatMenuIndex
= menuBar
.FindMenu(_("&Format"))
566 if formatMenuIndex
> -1:
567 formatMenu
= menuBar
.GetMenu(formatMenuIndex
)
569 formatMenu
= wx
.Menu()
570 if not menuBar
.FindItemById(CHECK_CODE_ID
): # check if below menu items have been already been installed
571 formatMenu
.AppendSeparator()
572 formatMenu
.Append(CHECK_CODE_ID
, _("&Check Code"), _("Checks the document for syntax and indentation errors"))
573 wx
.EVT_MENU(frame
, CHECK_CODE_ID
, frame
.ProcessEvent
)
574 wx
.EVT_UPDATE_UI(frame
, CHECK_CODE_ID
, frame
.ProcessUpdateUIEvent
)
575 formatMenu
.Append(AUTO_COMPLETE_ID
, _("&Auto Complete\tCtrl+Space"), _("Provides suggestions on how to complete the current statement"))
576 wx
.EVT_MENU(frame
, AUTO_COMPLETE_ID
, frame
.ProcessEvent
)
577 wx
.EVT_UPDATE_UI(frame
, AUTO_COMPLETE_ID
, frame
.ProcessUpdateUIEvent
)
578 formatMenu
.Append(CLEAN_WHITESPACE
, _("Clean &Whitespace"), _("Converts leading spaces to tabs or vice versa per 'use tabs' and clears trailing spaces"))
579 wx
.EVT_MENU(frame
, CLEAN_WHITESPACE
, frame
.ProcessEvent
)
580 wx
.EVT_UPDATE_UI(frame
, CLEAN_WHITESPACE
, frame
.ProcessUpdateUIEvent
)
581 formatMenu
.AppendSeparator()
582 formatMenu
.Append(INDENT_LINES_ID
, _("&Indent Lines\tTab"), _("Indents the selected lines one indent width"))
583 wx
.EVT_MENU(frame
, INDENT_LINES_ID
, frame
.ProcessEvent
)
584 wx
.EVT_UPDATE_UI(frame
, INDENT_LINES_ID
, frame
.ProcessUpdateUIEvent
)
585 formatMenu
.Append(DEDENT_LINES_ID
, _("&Dedent Lines\tShift+Tab"), _("Dedents the selected lines one indent width"))
586 wx
.EVT_MENU(frame
, DEDENT_LINES_ID
, frame
.ProcessEvent
)
587 wx
.EVT_UPDATE_UI(frame
, DEDENT_LINES_ID
, frame
.ProcessUpdateUIEvent
)
588 formatMenu
.Append(COMMENT_LINES_ID
, _("Comment &Lines\tCtrl+Q"), _("Comments out the selected lines be prefixing each one with a comment indicator"))
589 wx
.EVT_MENU(frame
, COMMENT_LINES_ID
, frame
.ProcessEvent
)
590 wx
.EVT_UPDATE_UI(frame
, COMMENT_LINES_ID
, frame
.ProcessUpdateUIEvent
)
591 formatMenu
.Append(UNCOMMENT_LINES_ID
, _("&Uncomment Lines\tCtrl+Shift+Q"), _("Removes comment prefixes from each of the selected lines"))
592 wx
.EVT_MENU(frame
, UNCOMMENT_LINES_ID
, frame
.ProcessEvent
)
593 wx
.EVT_UPDATE_UI(frame
, UNCOMMENT_LINES_ID
, frame
.ProcessUpdateUIEvent
)
594 formatMenu
.AppendSeparator()
595 formatMenu
.AppendCheckItem(USE_TABS_ID
, _("Use &Tabs"), _("Toggles use of tabs or whitespaces for indents"))
596 wx
.EVT_MENU(frame
, USE_TABS_ID
, frame
.ProcessEvent
)
597 wx
.EVT_UPDATE_UI(frame
, USE_TABS_ID
, frame
.ProcessUpdateUIEvent
)
598 formatMenu
.Append(SET_INDENT_WIDTH_ID
, _("&Set Indent Width..."), _("Sets the indent width"))
599 wx
.EVT_MENU(frame
, SET_INDENT_WIDTH_ID
, frame
.ProcessEvent
)
600 wx
.EVT_UPDATE_UI(frame
, SET_INDENT_WIDTH_ID
, frame
.ProcessUpdateUIEvent
)
601 if formatMenuIndex
== -1:
602 viewMenuIndex
= menuBar
.FindMenu(_("&View"))
603 menuBar
.Insert(viewMenuIndex
+ 1, formatMenu
, _("&Format"))
605 ## accelTable = wx.AcceleratorTable([
606 ## (wx.ACCEL_NORMAL, wx.WXK_TAB, INDENT_LINES_ID),
607 ## (wx.ACCEL_SHIFT, wx.WXK_TAB, DEDENT_LINES_ID),
608 ## eval(_("wx.ACCEL_CTRL, ord('Q'), COMMENT_LINES_ID")),
609 ## eval(_("wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord('Q'), UNCOMMENT_LINES_ID"))
611 ## frame.SetAcceleratorTable(accelTable)
613 def ProcessUpdateUIEvent(self
, event
):
615 if (id == EXPAND_TEXT_ID
616 or id == COLLAPSE_TEXT_ID
617 or id == EXPAND_TOP_ID
618 or id == COLLAPSE_TOP_ID
619 or id == EXPAND_ALL_ID
620 or id == COLLAPSE_ALL_ID
621 or id == CHECK_CODE_ID
622 or id == AUTO_COMPLETE_ID
623 or id == CLEAN_WHITESPACE
624 or id == SET_INDENT_WIDTH_ID
626 or id == INDENT_LINES_ID
627 or id == DEDENT_LINES_ID
628 or id == COMMENT_LINES_ID
629 or id == UNCOMMENT_LINES_ID
630 or id == FOLDING_ID
):
634 return STCTextEditor
.TextService
.ProcessUpdateUIEvent(self
, event
)
637 class CodeCtrl(STCTextEditor
.TextCtrl
):
638 CURRENT_LINE_MARKER_NUM
= 2
639 BREAKPOINT_MARKER_NUM
= 1
640 CURRENT_LINE_MARKER_MASK
= 0x4
641 BREAKPOINT_MARKER_MASK
= 0x2
644 def __init__(self
, parent
, id=-1, style
= wx
.NO_FULL_REPAINT_ON_RESIZE
, clearTab
=True):
645 STCTextEditor
.TextCtrl
.__init
__(self
, parent
, id, style
)
648 self
.Bind(wx
.EVT_RIGHT_UP
, self
.OnRightUp
)
649 self
.SetProperty("fold", "1")
651 # Setup a margin to hold fold markers
652 #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
653 self
.SetMarginType(2, wx
.stc
.STC_MARGIN_SYMBOL
)
654 self
.SetMarginMask(2, wx
.stc
.STC_MASK_FOLDERS
)
655 self
.SetMarginSensitive(2, True)
657 self
.SetMarginSensitive(1, False)
658 self
.SetMarginMask(1, 0x4)
660 self
.SetMarginSensitive(0, True)
661 self
.SetMarginType(0, wx
.stc
.STC_MARGIN_SYMBOL
)
662 self
.SetMarginMask(0, 0x3)
663 self
.SetMarginWidth(0, 12)
665 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEREND
, wx
.stc
.STC_MARK_BOXPLUSCONNECTED
, "white", "black")
666 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEROPENMID
, wx
.stc
.STC_MARK_BOXMINUSCONNECTED
, "white", "black")
667 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERMIDTAIL
, wx
.stc
.STC_MARK_TCORNER
, "white", "black")
668 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERTAIL
, wx
.stc
.STC_MARK_LCORNER
, "white", "black")
669 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDERSUB
, wx
.stc
.STC_MARK_VLINE
, "white", "black")
670 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDER
, wx
.stc
.STC_MARK_BOXPLUS
, "white", "black")
671 self
.MarkerDefine(wx
.stc
.STC_MARKNUM_FOLDEROPEN
, wx
.stc
.STC_MARK_BOXMINUS
, "white", "black")
672 # Define the current line marker
673 self
.MarkerDefine(CodeCtrl
.CURRENT_LINE_MARKER_NUM
, wx
.stc
.STC_MARK_SHORTARROW
, wx
.BLACK
, (255,255,128))
674 # Define the breakpoint marker
675 self
.MarkerDefine(CodeCtrl
.BREAKPOINT_MARKER_NUM
, wx
.stc
.STC_MARK_CIRCLE
, wx
.BLACK
, (255,0,0))
677 if _WINDOWS
and clearTab
: # should test to see if menu item exists, if it does, add this workaround
678 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
680 wx
.stc
.EVT_STC_MARGINCLICK(self
, self
.GetId(), self
.OnMarginClick
)
681 wx
.EVT_KEY_DOWN(self
, self
.OnKeyPressed
)
682 if self
.GetMatchingBraces():
683 wx
.stc
.EVT_STC_UPDATEUI(self
, self
.GetId(), self
.OnUpdateUI
)
689 def OnRightUp(self
, event
):
690 #Hold onto the current line number, no way to get it later.
691 self
._rightClickPosition
= self
.PositionFromPoint(event
.GetPosition())
692 self
._rightClickLine
= self
.LineFromPosition(self
._rightClickPosition
)
693 self
.PopupMenu(self
.CreatePopupMenu(), event
.GetPosition())
694 self
._rightClickLine
= -1
695 self
._rightClickPosition
= -1
698 def CreatePopupMenu(self
):
699 TOGGLEBREAKPOINT_ID
= wx
.NewId()
700 TOGGLEMARKER_ID
= wx
.NewId()
701 SYNCTREE_ID
= wx
.NewId()
705 self
.Bind(wx
.EVT_MENU
, self
.OnPopSyncOutline
, id=SYNCTREE_ID
)
706 item
= wx
.MenuItem(menu
, SYNCTREE_ID
, _("Find in Outline View"))
707 menu
.AppendItem(item
)
708 menu
.AppendSeparator()
709 self
.Bind(wx
.EVT_MENU
, self
.OnPopToggleBP
, id=TOGGLEBREAKPOINT_ID
)
710 item
= wx
.MenuItem(menu
, TOGGLEBREAKPOINT_ID
, _("Toggle Breakpoint"))
711 menu
.AppendItem(item
)
712 self
.Bind(wx
.EVT_MENU
, self
.OnPopToggleMarker
, id=TOGGLEMARKER_ID
)
713 item
= wx
.MenuItem(menu
, TOGGLEMARKER_ID
, _("Toggle Bookmark"))
714 menu
.AppendItem(item
)
715 menu
.AppendSeparator()
717 itemIDs
= [wx
.ID_UNDO
, wx
.ID_REDO
, None,
718 wx
.ID_CUT
, wx
.ID_COPY
, wx
.ID_PASTE
, wx
.ID_CLEAR
, None, wx
.ID_SELECTALL
]
720 menuBar
= wx
.GetApp().GetTopWindow().GetMenuBar()
721 for itemID
in itemIDs
:
723 menu
.AppendSeparator()
725 item
= menuBar
.FindItemById(itemID
)
727 menu
.Append(itemID
, item
.GetLabel())
728 wx
.EVT_MENU(self
, itemID
, self
.DSProcessEvent
) # wxHack: for customized right mouse menu doesn't work with new DynamicSashWindow
729 wx
.EVT_UPDATE_UI(self
, itemID
, self
.DSProcessUpdateUIEvent
) # wxHack: for customized right mouse menu doesn't work with new DynamicSashWindow
733 def OnPopToggleBP(self
, event
):
734 """ Toggle break point on right click line, not current line """
735 import DebuggerService
736 wx
.GetApp().GetService(DebuggerService
.DebuggerService
).OnToggleBreakpoint(event
, line
=self
._rightClickLine
)
739 def OnPopToggleMarker(self
, event
):
740 """ Toggle marker on right click line, not current line """
741 wx
.GetApp().GetDocumentManager().GetCurrentView().MarkerToggle(lineNum
= self
._rightClickLine
)
744 def OnPopSyncOutline(self
, event
):
745 wx
.GetApp().GetService(OutlineService
.OutlineService
).LoadOutline(wx
.GetApp().GetDocumentManager().GetCurrentView(), position
=self
._rightClickPosition
)
748 def HasSelection(self
):
749 return self
.GetSelectionStart() - self
.GetSelectionEnd() != 0
752 def ClearCurrentLineMarkers(self
):
753 self
.MarkerDeleteAll(CodeCtrl
.CURRENT_LINE_MARKER_NUM
)
756 def ClearCurrentBreakpoinMarkers(self
):
757 self
.MarkerDeleteAll(CodeCtrl
.BREAKPOINT_MARKER_NUM
)
760 def GetDefaultFont(self
):
761 if wx
.Platform
== '__WXMSW__':
765 return wx
.Font(10, wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
, faceName
= font
)
768 def GetMatchingBraces(self
):
769 """ Overwrite this method for language specific braces """
773 def CanWordWrap(self
):
777 def SetFont(self
, font
):
781 def SetFontColor(self
, fontColor
):
782 self
._fontColor
= fontColor
785 def UpdateStyles(self
):
787 if not self
.GetFont():
790 faces
= { 'font' : self
.GetFont().GetFaceName(),
791 'size' : self
.GetFont().GetPointSize(),
792 'size2': self
.GetFont().GetPointSize() - 2,
793 'color' : "%02x%02x%02x" % (self
.GetFontColor().Red(), self
.GetFontColor().Green(), self
.GetFontColor().Blue())
796 # Global default styles for all languages
797 self
.StyleSetSpec(wx
.stc
.STC_STYLE_DEFAULT
, "face:%(font)s,fore:#FFFFFF,size:%(size)d" % faces
)
798 self
.StyleSetSpec(wx
.stc
.STC_STYLE_LINENUMBER
, "face:%(font)s,back:#C0C0C0,face:%(font)s,size:%(size2)d" % faces
)
799 self
.StyleSetSpec(wx
.stc
.STC_STYLE_CONTROLCHAR
, "face:%(font)s" % faces
)
800 self
.StyleSetSpec(wx
.stc
.STC_STYLE_BRACELIGHT
, "face:%(font)s,fore:#000000,back:#70FFFF,size:%(size)d" % faces
)
801 self
.StyleSetSpec(wx
.stc
.STC_STYLE_BRACEBAD
, "face:%(font)s,fore:#000000,back:#FF0000,size:%(size)d" % faces
)
804 def OnKeyPressed(self
, event
):
805 if self
.CallTipActive():
807 key
= event
.KeyCode()
808 if False: # key == wx.WXK_SPACE and event.ControlDown():
809 pos
= self
.GetCurrentPos()
811 if event
.ShiftDown():
812 self
.CallTipSetBackground("yellow")
813 self
.CallTipShow(pos
, 'param1, param2')
817 #for x in range(50000):
818 # lst.append('%05d' % x)
819 #st = string.join(lst)
821 #self.AutoCompShow(0, st)
823 kw
= keyword
.kwlist
[:]
826 kw
.append("__init__")
829 kw
.append("this_is_a_longer_value")
830 kw
.append("this_is_a_much_much_much_much_much_much_much_longer_value")
832 kw
.sort() # Python sorts are case sensitive
833 self
.AutoCompSetIgnoreCase(False) # so this needs to match
835 self
.AutoCompShow(0, string
.join(kw
))
836 elif key
== wx
.WXK_RETURN
:
839 STCTextEditor
.TextCtrl
.OnKeyPressed(self
, event
)
844 self
.EnsureCaretVisible()
845 # Need to do a default one for all languges
848 def OnMarginClick(self
, evt
):
849 # fold and unfold as needed
850 if evt
.GetMargin() == 2:
851 if evt
.GetShift() and evt
.GetControl():
852 lineCount
= self
.GetLineCount()
855 # find out if we are folding or unfolding
856 for lineNum
in range(lineCount
):
857 if self
.GetFoldLevel(lineNum
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
:
858 expanding
= not self
.GetFoldExpanded(lineNum
)
861 self
.ToggleFoldAll(expanding
)
863 lineClicked
= self
.LineFromPosition(evt
.GetPosition())
864 if self
.GetFoldLevel(lineClicked
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
:
866 self
.SetFoldExpanded(lineClicked
, True)
867 self
.Expand(lineClicked
, True, True, 1)
868 elif evt
.GetControl():
869 if self
.GetFoldExpanded(lineClicked
):
870 self
.SetFoldExpanded(lineClicked
, False)
871 self
.Expand(lineClicked
, False, True, 0)
873 self
.SetFoldExpanded(lineClicked
, True)
874 self
.Expand(lineClicked
, True, True, 100)
876 self
.ToggleFold(lineClicked
)
878 elif evt
.GetMargin() == 0:
879 #This is used to toggle breakpoints via the debugger service.
880 import DebuggerService
881 db_service
= wx
.GetApp().GetService(DebuggerService
.DebuggerService
)
883 db_service
.OnToggleBreakpoint(evt
, line
=self
.LineFromPosition(evt
.GetPosition()))
886 def OnUpdateUI(self
, evt
):
887 braces
= self
.GetMatchingBraces()
889 # check for matching braces
893 caretPos
= self
.GetCurrentPos()
895 charBefore
= self
.GetCharAt(caretPos
- 1)
896 styleBefore
= self
.GetStyleAt(caretPos
- 1)
899 if charBefore
and chr(charBefore
) in braces
:
900 braceAtCaret
= caretPos
- 1
904 charAfter
= self
.GetCharAt(caretPos
)
905 styleAfter
= self
.GetStyleAt(caretPos
)
906 if charAfter
and chr(charAfter
) in braces
:
907 braceAtCaret
= caretPos
909 if braceAtCaret
>= 0:
910 braceOpposite
= self
.BraceMatch(braceAtCaret
)
912 if braceAtCaret
!= -1 and braceOpposite
== -1:
913 self
.BraceBadLight(braceAtCaret
)
915 self
.BraceHighlight(braceAtCaret
, braceOpposite
)
920 def ToggleFoldAll(self
, expand
= True, topLevelOnly
= False):
922 lineCount
= self
.GetLineCount()
924 if not topLevelOnly
or (topLevelOnly
and self
.GetFoldLevel(i
) & wx
.stc
.STC_FOLDLEVELNUMBERMASK
== wx
.stc
.STC_FOLDLEVELBASE
):
925 if (expand
and self
.CanLineExpand(i
)) or (not expand
and self
.CanLineCollapse(i
)):
930 def CanLineExpand(self
, line
):
931 return not self
.GetFoldExpanded(line
)
934 def CanLineCollapse(self
, line
):
935 return self
.GetFoldExpanded(line
) and self
.GetFoldLevel(line
) & wx
.stc
.STC_FOLDLEVELHEADERFLAG
938 def Expand(self
, line
, doExpand
, force
=False, visLevels
=0, level
=-1):
939 lastChild
= self
.GetLastChild(line
, level
)
941 while line
<= lastChild
:
944 self
.ShowLines(line
, line
)
946 self
.HideLines(line
, line
)
949 self
.ShowLines(line
, line
)
952 level
= self
.GetFoldLevel(line
)
954 if level
& wx
.stc
.STC_FOLDLEVELHEADERFLAG
:
957 self
.SetFoldExpanded(line
, True)
959 self
.SetFoldExpanded(line
, False)
960 line
= self
.Expand(line
, doExpand
, force
, visLevels
-1)
963 if doExpand
and self
.GetFoldExpanded(line
):
964 line
= self
.Expand(line
, True, force
, visLevels
-1)
966 line
= self
.Expand(line
, False, force
, visLevels
-1)