X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/510bb7480c5138dd5127ed3d8b1d9cbab39983c9..1f780e48af479e7bf9a07eaaa1ab6b41f1ffb17b:/wxPython/samples/ide/activegrid/tool/CodeEditor.py?ds=sidebyside diff --git a/wxPython/samples/ide/activegrid/tool/CodeEditor.py b/wxPython/samples/ide/activegrid/tool/CodeEditor.py new file mode 100644 index 0000000000..09a96abf53 --- /dev/null +++ b/wxPython/samples/ide/activegrid/tool/CodeEditor.py @@ -0,0 +1,1014 @@ +#---------------------------------------------------------------------------- +# Name: CodeEditor.py +# Purpose: Abstract Code Editor for pydocview tbat uses the Styled Text Control +# +# Author: Peter Yared +# +# Created: 8/10/03 +# CVS-ID: $Id$ +# Copyright: (c) 2004-2005 ActiveGrid, Inc. +# License: wxWindows License +#---------------------------------------------------------------------------- + + +import STCTextEditor +import wx +import wx.lib.docview +import OutlineService +import os +import re +import string +import sys +import DebuggerService +import MarkerService +_ = wx.GetTranslation +if wx.Platform == '__WXMSW__': + _WINDOWS = True +else: + _WINDOWS = False + + +EXPAND_TEXT_ID = wx.NewId() +COLLAPSE_TEXT_ID = wx.NewId() +EXPAND_TOP_ID = wx.NewId() +COLLAPSE_TOP_ID = wx.NewId() +EXPAND_ALL_ID = wx.NewId() +COLLAPSE_ALL_ID = wx.NewId() +CHECK_CODE_ID = wx.NewId() +AUTO_COMPLETE_ID = wx.NewId() +CLEAN_WHITESPACE = wx.NewId() +COMMENT_LINES_ID = wx.NewId() +UNCOMMENT_LINES_ID = wx.NewId() +INDENT_LINES_ID = wx.NewId() +DEDENT_LINES_ID = wx.NewId() +USE_TABS_ID = wx.NewId() +SET_INDENT_WIDTH_ID = wx.NewId() +FOLDING_ID = wx.NewId() + + +class CodeDocument(STCTextEditor.TextDocument): + pass + + +class CodeView(STCTextEditor.TextView): + + + #---------------------------------------------------------------------------- + # Overridden methods + #---------------------------------------------------------------------------- + + + def GetCtrlClass(self): + """ Used in split window to instantiate new instances """ + return CodeCtrl + + + def ProcessEvent(self, event): + id = event.GetId() + if id == EXPAND_TEXT_ID: + self.GetCtrl().ToggleFold(self.GetCtrl().GetCurrentLine()) + return True + elif id == COLLAPSE_TEXT_ID: + self.GetCtrl().ToggleFold(self.GetCtrl().GetCurrentLine()) + return True + elif id == EXPAND_TOP_ID: + self.GetCtrl().ToggleFoldAll(expand = True, topLevelOnly = True) + return True + elif id == COLLAPSE_TOP_ID: + self.GetCtrl().ToggleFoldAll(expand = False, topLevelOnly = True) + return True + elif id == EXPAND_ALL_ID: + self.GetCtrl().ToggleFoldAll(expand = True) + return True + elif id == COLLAPSE_ALL_ID: + self.GetCtrl().ToggleFoldAll(expand = False) + return True + elif id == CHECK_CODE_ID: + self.OnCheckCode() + return True + elif id == AUTO_COMPLETE_ID: + self.OnAutoComplete() + return True + elif id == CLEAN_WHITESPACE: + self.OnCleanWhiteSpace() + return True + elif id == SET_INDENT_WIDTH_ID: + self.OnSetIndentWidth() + return True + elif id == USE_TABS_ID: + self.GetCtrl().SetUseTabs(not self.GetCtrl().GetUseTabs()) + return True + elif id == INDENT_LINES_ID: + self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_TAB) + return True + elif id == DEDENT_LINES_ID: + self.GetCtrl().CmdKeyExecute(wx.stc.STC_CMD_BACKTAB) + return True + elif id == COMMENT_LINES_ID: + self.OnCommentLines() + return True + elif id == UNCOMMENT_LINES_ID: + self.OnUncommentLines() + return True + else: + return STCTextEditor.TextView.ProcessEvent(self, event) + + + def ProcessUpdateUIEvent(self, event): + if not self.GetCtrl(): + return False + hasText = self.GetCtrl().GetTextLength() > 0 + id = event.GetId() + if id == EXPAND_TEXT_ID: + event.Enable(self.GetCtrl().CanLineExpand(self.GetCtrl().GetCurrentLine())) + return True + elif id == COLLAPSE_TEXT_ID: + event.Enable(self.GetCtrl().CanLineCollapse(self.GetCtrl().GetCurrentLine())) + return True + elif id == EXPAND_TOP_ID: + event.Enable(hasText) + return True + elif id == COLLAPSE_TOP_ID: + event.Enable(hasText) + return True + elif id == EXPAND_ALL_ID: + event.Enable(hasText) + return True + elif id == COLLAPSE_ALL_ID: + event.Enable(hasText) + return True + elif id == CHECK_CODE_ID: + event.Enable(False) + return True + elif id == AUTO_COMPLETE_ID: + event.Enable(hasText) + return True + elif id == CLEAN_WHITESPACE: + event.Enable(hasText) + return True + elif id == SET_INDENT_WIDTH_ID: + event.Enable(True) + return True + elif id == USE_TABS_ID: + event.Enable(True) + event.Check(self.GetCtrl().GetUseTabs()) + return True + elif id == INDENT_LINES_ID: + event.Enable(hasText) + return True + elif id == DEDENT_LINES_ID: + event.Enable(hasText) + return True + elif id == COMMENT_LINES_ID: + event.Enable(hasText) + return True + elif id == UNCOMMENT_LINES_ID: + event.Enable(hasText) + return True + elif id == FOLDING_ID: + event.Enable(True) + return True + else: + return STCTextEditor.TextView.ProcessUpdateUIEvent(self, event) + + + #---------------------------------------------------------------------------- + # Methods for OutlineService + #---------------------------------------------------------------------------- + + def OnChangeFilename(self): + wx.lib.docview.View.OnChangeFilename(self) + self.LoadOutline(force=True) + + + def ClearOutline(self): + outlineService = wx.GetApp().GetService(OutlineService.OutlineService) + if not outlineService: + return + + outlineView = outlineService.GetView() + if not outlineView: + return + + outlineView.ClearTreeCtrl() + + + def LoadOutline(self, force=False): + outlineService = wx.GetApp().GetService(OutlineService.OutlineService) + if not outlineService: + return + outlineService.LoadOutline(self, force=force) + + + def DoLoadOutlineCallback(self, force=False): + outlineService = wx.GetApp().GetService(OutlineService.OutlineService) + if not outlineService: + return False + + outlineView = outlineService.GetView() + if not outlineView: + return False + + treeCtrl = outlineView.GetTreeCtrl() + if not treeCtrl: + return False + + view = treeCtrl.GetCallbackView() + newCheckSum = self.GenCheckSum() + if not force: + if view and view is self: + if self._checkSum == newCheckSum: + return False + self._checkSum = newCheckSum + + treeCtrl.DeleteAllItems() + + document = self.GetDocument() + if not document: + return True + + filename = document.GetFilename() + if filename: + rootItem = treeCtrl.AddRoot(os.path.basename(filename)) + treeCtrl.SetDoSelectCallback(rootItem, self, None) + else: + return True + + text = self.GetValue() + if not text: + return True + + CLASS_PATTERN = 'class[ \t]+\w+.*?:' + DEF_PATTERN = 'def[ \t]+\w+\(.*?\)' + classPat = re.compile(CLASS_PATTERN, re.M|re.S) + defPat= re.compile(DEF_PATTERN, re.M|re.S) + pattern = re.compile('^[ \t]*((' + CLASS_PATTERN + ')|('+ DEF_PATTERN +'.*?:)).*?$', re.M|re.S) + + iter = pattern.finditer(text) + indentStack = [(0, rootItem)] + for pattern in iter: + line = pattern.string[pattern.start(0):pattern.end(0)] + classLine = classPat.search(line) + if classLine: + indent = classLine.start(0) + itemStr = classLine.string[classLine.start(0):classLine.end(0)-1] # don't take the closing ':' + else: + defLine = defPat.search(line) + if defLine: + indent = defLine.start(0) + itemStr = defLine.string[defLine.start(0):defLine.end(0)] + + if indent == 0: + parentItem = rootItem + else: + lastItem = indentStack.pop() + while lastItem[0] >= indent: + lastItem = indentStack.pop() + indentStack.append(lastItem) + parentItem = lastItem[1] + + item = treeCtrl.AppendItem(parentItem, itemStr) + 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 + indentStack.append((indent, item)) + + treeCtrl.Expand(rootItem) + + return True + + + def DoSelectCallback(self, data): + if data: + self.EnsureVisibleEnforcePolicy(self.LineFromPosition(data[0])) + # wxBug: need to select in reverse order (end, start) to place cursor at begining of line, + # otherwise, display is scrolled over to the right hard and is hard to view + self.SetSelection(data[1], data[0]) + + +## def checksum(self, bytes): +## def rotate_right(c): +## if c&1: +## return (c>>1)|0x8000 +## else: +## return c>>1 +## +## result = 0 +## for ch in bytes: +## ch = ord(ch) & 0xFF +## result = (rotate_right(result)+ch) & 0xFFFF +## return result +## + + def GenCheckSum(self): + """ Poor man's checksum. We'll assume most changes will change the length of the file. + """ + text = self.GetValue() + if text: + return len(text) + else: + return 0 + + + #---------------------------------------------------------------------------- + # Format methods + #---------------------------------------------------------------------------- + + def OnCheckCode(self): + """ Need to overshadow this for each specific subclass """ + if 0: + try: + code = self.GetCtrl().GetText() + codeObj = compile(code, self.GetDocument().GetFilename(), 'exec') + self._GetParentFrame().SetStatusText(_("The file successfully compiled")) + except SyntaxError, (message, (fileName, line, col, text)): + pos = self.GetCtrl().PositionFromLine(line - 1) + col - 1 + self.GetCtrl().SetSelection(pos, pos) + self._GetParentFrame().SetStatusText(_("Syntax Error: %s") % message) + except: + self._GetParentFrame().SetStatusText(sys.exc_info()[0]) + + + def OnAutoComplete(self): + self.GetCtrl().AutoCompCancel() + self.GetCtrl().AutoCompSetAutoHide(0) + self.GetCtrl().AutoCompSetChooseSingle(True) + self.GetCtrl().AutoCompSetIgnoreCase(True) + context, hint = self.GetAutoCompleteHint() + replaceList, replaceLen = self.GetAutoCompleteKeywordList(context, hint) + if replaceList and len(replaceList) != 0: + self.GetCtrl().AutoCompShow(replaceLen, replaceList) + + + def GetAutoCompleteHint(self): + """ Replace this method with Editor specific method """ + pos = self.GetCtrl().GetCurrentPos() + if pos == 0: + return None, None + if chr(self.GetCtrl().GetCharAt(pos - 1)) == '.': + pos = pos - 1 + hint = None + else: + hint = '' + + validLetters = string.letters + string.digits + '_.' + word = '' + while (True): + pos = pos - 1 + if pos < 0: + break + char = chr(self.GetCtrl().GetCharAt(pos)) + if char not in validLetters: + break + word = char + word + + context = word + if hint is not None: + lastDot = word.rfind('.') + if lastDot != -1: + context = word[0:lastDot] + hint = word[lastDot+1:] + + return context, hint + + + def GetAutoCompleteDefaultKeywords(self): + """ Replace this method with Editor specific keywords """ + return ['Put', 'Editor Specific', 'Keywords', 'Here'] + + + def CaseInsensitiveCompare(self, s1, s2): + """ GetAutoCompleteKeywordList() method used to show keywords in case insensitive order """ + s1L = s1.lower() + s2L = s2.lower() + if s1L == s2L: + return 0 + elif s1L < s2L: + return -1 + else: + return 1 + + + def GetAutoCompleteKeywordList(self, context, hint): + """ Replace this method with Editor specific keywords """ + kw = self.GetAutoCompleteDefaultKeywords() + + if hint and len(hint): + lowerHint = hint.lower() + filterkw = filter(lambda item: item.lower().startswith(lowerHint), kw) # remove variables and methods that don't match hint + kw = filterkw + + if hint: + replaceLen = len(hint) + else: + replaceLen = 0 + + kw.sort(self.CaseInsensitiveCompare) + return " ".join(kw), replaceLen + + + def OnCleanWhiteSpace(self): + newText = "" + for lineNo in self._GetSelectedLineNumbers(): + lineText = string.rstrip(self.GetCtrl().GetLine(lineNo)) + indent = 0 + lstrip = 0 + for char in lineText: + if char == '\t': + indent = indent + self.GetCtrl().GetIndent() + lstrip = lstrip + 1 + elif char in string.whitespace: + indent = indent + 1 + lstrip = lstrip + 1 + else: + break + if self.GetCtrl().GetUseTabs(): + indentText = (indent / self.GetCtrl().GetIndent()) * '\t' + (indent % self.GetCtrl().GetIndent()) * ' ' + else: + indentText = indent * ' ' + lineText = indentText + lineText[lstrip:] + '\n' + newText = newText + lineText + self._ReplaceSelectedLines(newText) + + + def OnSetIndentWidth(self): + dialog = wx.TextEntryDialog(self._GetParentFrame(), _("Enter new indent width (2-10):"), _("Set Indent Width"), "%i" % self.GetCtrl().GetIndent()) + if dialog.ShowModal() == wx.ID_OK: + try: + indent = int(dialog.GetValue()) + if indent >= 2 and indent <= 10: + self.GetCtrl().SetIndent(indent) + self.GetCtrl().SetTabWidth(indent) + except: + pass + dialog.Destroy() + + + def GetIndentWidth(self): + return self.GetCtrl().GetIndent() + + + def OnCommentLines(self): + newText = "" + for lineNo in self._GetSelectedLineNumbers(): + lineText = self.GetCtrl().GetLine(lineNo) + if (len(lineText) > 1 and lineText[0] == '#') or (len(lineText) > 2 and lineText[:2] == '##'): + newText = newText + lineText + else: + newText = newText + "##" + lineText + self._ReplaceSelectedLines(newText) + + + def OnUncommentLines(self): + newText = "" + for lineNo in self._GetSelectedLineNumbers(): + lineText = self.GetCtrl().GetLine(lineNo) + if len(lineText) >= 2 and lineText[:2] == "##": + lineText = lineText[2:] + elif len(lineText) >= 1 and lineText[:1] == "#": + lineText = lineText[1:] + newText = newText + lineText + self._ReplaceSelectedLines(newText) + + + def _GetSelectedLineNumbers(self): + selStart, selEnd = self._GetPositionsBoundingSelectedLines() + return range(self.GetCtrl().LineFromPosition(selStart), self.GetCtrl().LineFromPosition(selEnd)) + + + def _GetPositionsBoundingSelectedLines(self): + startPos = self.GetCtrl().GetCurrentPos() + endPos = self.GetCtrl().GetAnchor() + if startPos > endPos: + temp = endPos + endPos = startPos + startPos = temp + if endPos == self.GetCtrl().PositionFromLine(self.GetCtrl().LineFromPosition(endPos)): + endPos = endPos - 1 # If it's at the very beginning of a line, use the line above it as the ending line + selStart = self.GetCtrl().PositionFromLine(self.GetCtrl().LineFromPosition(startPos)) + selEnd = self.GetCtrl().PositionFromLine(self.GetCtrl().LineFromPosition(endPos) + 1) + return selStart, selEnd + + + def _ReplaceSelectedLines(self, text): + if len(text) == 0: + return + selStart, selEnd = self._GetPositionsBoundingSelectedLines() + self.GetCtrl().SetSelection(selStart, selEnd) + self.GetCtrl().ReplaceSelection(text) + self.GetCtrl().SetSelection(selStart + len(text), selStart) + + + def OnUpdate(self, sender = None, hint = None): + if hint == "ViewStuff": + self.GetCtrl().SetViewDefaults() + elif hint == "Font": + font, color = self.GetFontAndColorFromConfig() + self.GetCtrl().SetFont(font) + self.GetCtrl().SetFontColor(color) + else: + dbg_service = wx.GetApp().GetService(DebuggerService.DebuggerService) + if dbg_service: + dbg_service.SetCurrentBreakpointMarkers(self) + + +class CodeService(STCTextEditor.TextService): + + + def __init__(self): + STCTextEditor.TextService.__init__(self) + + + def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None): + # TODO NEED TO DO INSTANCEOF CHECK HERE FOR SDI + #if document and document.GetDocumentTemplate().GetDocumentType() != TextDocument: + # return + if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI: + return + + viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View"))) + isWindows = (wx.Platform == '__WXMSW__') + + if not menuBar.FindItemById(EXPAND_TEXT_ID): # check if below menu items have been already been installed + foldingMenu = wx.Menu() + if isWindows: + foldingMenu.Append(EXPAND_TEXT_ID, _("&Expand\tNumpad-Plus"), _("Expands a collapsed block of text")) + else: + foldingMenu.Append(EXPAND_TEXT_ID, _("&Expand"), _("Expands a collapsed block of text")) + + wx.EVT_MENU(frame, EXPAND_TEXT_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, EXPAND_TEXT_ID, frame.ProcessUpdateUIEvent) + + if isWindows: + foldingMenu.Append(COLLAPSE_TEXT_ID, _("&Collapse\tNumpad+Minus"), _("Collapse a block of text")) + else: + foldingMenu.Append(COLLAPSE_TEXT_ID, _("&Collapse"), _("Collapse a block of text")) + wx.EVT_MENU(frame, COLLAPSE_TEXT_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, COLLAPSE_TEXT_ID, frame.ProcessUpdateUIEvent) + + if isWindows: + foldingMenu.Append(EXPAND_TOP_ID, _("Expand &Top Level\tCtrl+Numpad+Plus"), _("Expands the top fold levels in the document")) + else: + foldingMenu.Append(EXPAND_TOP_ID, _("Expand &Top Level"), _("Expands the top fold levels in the document")) + wx.EVT_MENU(frame, EXPAND_TOP_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, EXPAND_TOP_ID, frame.ProcessUpdateUIEvent) + + if isWindows: + foldingMenu.Append(COLLAPSE_TOP_ID, _("Collapse Top &Level\tCtrl+Numpad+Minus"), _("Collapses the top fold levels in the document")) + else: + foldingMenu.Append(COLLAPSE_TOP_ID, _("Collapse Top &Level"), _("Collapses the top fold levels in the document")) + wx.EVT_MENU(frame, COLLAPSE_TOP_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, COLLAPSE_TOP_ID, frame.ProcessUpdateUIEvent) + + if isWindows: + foldingMenu.Append(EXPAND_ALL_ID, _("Expand &All\tShift+Numpad+Plus"), _("Expands all of the fold levels in the document")) + else: + foldingMenu.Append(EXPAND_ALL_ID, _("Expand &All"), _("Expands all of the fold levels in the document")) + wx.EVT_MENU(frame, EXPAND_ALL_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, EXPAND_ALL_ID, frame.ProcessUpdateUIEvent) + + if isWindows: + foldingMenu.Append(COLLAPSE_ALL_ID, _("Colla&pse All\tShift+Numpad+Minus"), _("Collapses all of the fold levels in the document")) + else: + foldingMenu.Append(COLLAPSE_ALL_ID, _("Colla&pse All"), _("Collapses all of the fold levels in the document")) + wx.EVT_MENU(frame, COLLAPSE_ALL_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, COLLAPSE_ALL_ID, frame.ProcessUpdateUIEvent) + + viewMenu.AppendMenu(FOLDING_ID, _("&Folding"), foldingMenu) + wx.EVT_UPDATE_UI(frame, FOLDING_ID, frame.ProcessUpdateUIEvent) + + formatMenuIndex = menuBar.FindMenu(_("&Format")) + if formatMenuIndex > -1: + formatMenu = menuBar.GetMenu(formatMenuIndex) + else: + formatMenu = wx.Menu() + if not menuBar.FindItemById(CHECK_CODE_ID): # check if below menu items have been already been installed + formatMenu.AppendSeparator() + formatMenu.Append(CHECK_CODE_ID, _("&Check Code"), _("Checks the document for syntax and indentation errors")) + wx.EVT_MENU(frame, CHECK_CODE_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, CHECK_CODE_ID, frame.ProcessUpdateUIEvent) + formatMenu.Append(AUTO_COMPLETE_ID, _("&Auto Complete\tCtrl+Space"), _("Provides suggestions on how to complete the current statement")) + wx.EVT_MENU(frame, AUTO_COMPLETE_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, AUTO_COMPLETE_ID, frame.ProcessUpdateUIEvent) + formatMenu.Append(CLEAN_WHITESPACE, _("Clean &Whitespace"), _("Converts leading spaces to tabs or vice versa per 'use tabs' and clears trailing spaces")) + wx.EVT_MENU(frame, CLEAN_WHITESPACE, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, CLEAN_WHITESPACE, frame.ProcessUpdateUIEvent) + formatMenu.AppendSeparator() + formatMenu.Append(INDENT_LINES_ID, _("&Indent Lines\tTab"), _("Indents the selected lines one indent width")) + wx.EVT_MENU(frame, INDENT_LINES_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, INDENT_LINES_ID, frame.ProcessUpdateUIEvent) + formatMenu.Append(DEDENT_LINES_ID, _("&Dedent Lines\tShift+Tab"), _("Dedents the selected lines one indent width")) + wx.EVT_MENU(frame, DEDENT_LINES_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, DEDENT_LINES_ID, frame.ProcessUpdateUIEvent) + formatMenu.Append(COMMENT_LINES_ID, _("Comment &Lines\tCtrl+Q"), _("Comments out the selected lines be prefixing each one with a comment indicator")) + wx.EVT_MENU(frame, COMMENT_LINES_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, COMMENT_LINES_ID, frame.ProcessUpdateUIEvent) + formatMenu.Append(UNCOMMENT_LINES_ID, _("&Uncomment Lines\tCtrl+Shift+Q"), _("Removes comment prefixes from each of the selected lines")) + wx.EVT_MENU(frame, UNCOMMENT_LINES_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, UNCOMMENT_LINES_ID, frame.ProcessUpdateUIEvent) + formatMenu.AppendSeparator() + formatMenu.AppendCheckItem(USE_TABS_ID, _("Use &Tabs"), _("Toggles use of tabs or whitespaces for indents")) + wx.EVT_MENU(frame, USE_TABS_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, USE_TABS_ID, frame.ProcessUpdateUIEvent) + formatMenu.Append(SET_INDENT_WIDTH_ID, _("&Set Indent Width..."), _("Sets the indent width")) + wx.EVT_MENU(frame, SET_INDENT_WIDTH_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, SET_INDENT_WIDTH_ID, frame.ProcessUpdateUIEvent) + if formatMenuIndex == -1: + viewMenuIndex = menuBar.FindMenu(_("&View")) + menuBar.Insert(viewMenuIndex + 1, formatMenu, _("&Format")) + +## accelTable = wx.AcceleratorTable([ +## (wx.ACCEL_NORMAL, wx.WXK_TAB, INDENT_LINES_ID), +## (wx.ACCEL_SHIFT, wx.WXK_TAB, DEDENT_LINES_ID), +## eval(_("wx.ACCEL_CTRL, ord('Q'), COMMENT_LINES_ID")), +## eval(_("wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord('Q'), UNCOMMENT_LINES_ID")) +## ]) +## frame.SetAcceleratorTable(accelTable) + + def ProcessUpdateUIEvent(self, event): + id = event.GetId() + if id == EXPAND_TEXT_ID: + event.Enable(False) + return True + elif id == COLLAPSE_TEXT_ID: + event.Enable(False) + return True + elif id == EXPAND_TOP_ID: + event.Enable(False) + return True + elif id == COLLAPSE_TOP_ID: + event.Enable(False) + return True + elif id == EXPAND_ALL_ID: + event.Enable(False) + return True + elif id == COLLAPSE_ALL_ID: + event.Enable(False) + return True + elif id == CHECK_CODE_ID: + event.Enable(False) + return True + elif id == AUTO_COMPLETE_ID: + event.Enable(False) + return True + elif id == CLEAN_WHITESPACE: + event.Enable(False) + return True + elif id == SET_INDENT_WIDTH_ID: + event.Enable(False) + return True + elif id == USE_TABS_ID: + event.Enable(False) + return True + elif id == INDENT_LINES_ID: + event.Enable(False) + return True + elif id == DEDENT_LINES_ID: + event.Enable(False) + return True + elif id == COMMENT_LINES_ID: + event.Enable(False) + return True + elif id == UNCOMMENT_LINES_ID: + event.Enable(False) + return True + elif id == FOLDING_ID: + event.Enable(False) + return True + else: + return STCTextEditor.TextService.ProcessUpdateUIEvent(self, event) + + +class CodeCtrl(STCTextEditor.TextCtrl): + CURRENT_LINE_MARKER_NUM = 2 + BREAKPOINT_MARKER_NUM = 1 + CURRENT_LINE_MARKER_MASK = 0x4 + BREAKPOINT_MARKER_MASK = 0x2 + + + def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE): + if ID == -1: + ID = wx.NewId() + STCTextEditor.TextCtrl.__init__(self, parent, ID, style) + + self.UsePopUp(False) + self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) + self.SetProperty("fold", "1") + + # Setup a margin to hold fold markers + #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER? + self.SetMarginType(2, wx.stc.STC_MARGIN_SYMBOL) + self.SetMarginMask(2, wx.stc.STC_MASK_FOLDERS) + self.SetMarginSensitive(2, True) + self.SetMarginWidth(2, 12) + + self.SetMarginSensitive(1, False) + self.SetMarginMask(1, 0x4) + + self.SetMarginSensitive(0, True) + self.SetMarginType(0, wx.stc.STC_MARGIN_SYMBOL) + self.SetMarginMask(0, 0x3) + self.SetMarginWidth(0, 12) + + self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEREND, wx.stc.STC_MARK_BOXPLUSCONNECTED, "white", "black") + self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPENMID, wx.stc.STC_MARK_BOXMINUSCONNECTED, "white", "black") + self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERMIDTAIL, wx.stc.STC_MARK_TCORNER, "white", "black") + self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERTAIL, wx.stc.STC_MARK_LCORNER, "white", "black") + self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDERSUB, wx.stc.STC_MARK_VLINE, "white", "black") + self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDER, wx.stc.STC_MARK_BOXPLUS, "white", "black") + self.MarkerDefine(wx.stc.STC_MARKNUM_FOLDEROPEN, wx.stc.STC_MARK_BOXMINUS, "white", "black") + # Define the current line marker + self.MarkerDefine(CodeCtrl.CURRENT_LINE_MARKER_NUM, wx.stc.STC_MARK_SHORTARROW, wx.BLACK, (255,255,128)) + # Define the breakpoint marker + self.MarkerDefine(CodeCtrl.BREAKPOINT_MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, (255,0,0)) + + if _WINDOWS: # should test to see if menu item exists, if it does, add this workaround + 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 + + wx.stc.EVT_STC_MARGINCLICK(self, ID, self.OnMarginClick) + wx.EVT_KEY_DOWN(self, self.OnKeyPressed) + if self.GetMatchingBraces(): + wx.stc.EVT_STC_UPDATEUI(self, ID, self.OnUpdateUI) + + self.StyleClearAll() + self.UpdateStyles() + + + def OnRightUp(self, event): + #Hold onto the current line number, no way to get it later. + self._rightClickPosition = self.PositionFromPoint(event.GetPosition()) + self._rightClickLine = self.LineFromPosition(self._rightClickPosition) + self.PopupMenu(self.CreatePopupMenu(), event.GetPosition()) + self._rightClickLine = -1 + self._rightClickPosition = -1 + + + def CreatePopupMenu(self): + TOGGLEBREAKPOINT_ID = wx.NewId() + TOGGLEMARKER_ID = wx.NewId() + SYNCTREE_ID = wx.NewId() + + menu = wx.Menu() + + self.Bind(wx.EVT_MENU, self.OnPopSyncOutline, id=SYNCTREE_ID) + item = wx.MenuItem(menu, SYNCTREE_ID, _("Find in Outline View")) + menu.AppendItem(item) + menu.AppendSeparator() + self.Bind(wx.EVT_MENU, self.OnPopToggleBP, id=TOGGLEBREAKPOINT_ID) + item = wx.MenuItem(menu, TOGGLEBREAKPOINT_ID, _("Toggle Breakpoint")) + menu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnPopToggleMarker, id=TOGGLEMARKER_ID) + item = wx.MenuItem(menu, TOGGLEMARKER_ID, _("Toggle Marker")) + menu.AppendItem(item) + menu.AppendSeparator() + + itemIDs = [wx.ID_UNDO, wx.ID_REDO, None, + wx.ID_CUT, wx.ID_COPY, wx.ID_PASTE, wx.ID_CLEAR, None, wx.ID_SELECTALL] + + menuBar = wx.GetApp().GetTopWindow().GetMenuBar() + for itemID in itemIDs: + if not itemID: + menu.AppendSeparator() + else: + item = menuBar.FindItemById(itemID) + if item: + menu.Append(itemID, item.GetLabel()) + + return menu + + + def OnPopToggleBP(self, event): + """ Toggle break point on right click line, not current line """ + wx.GetApp().GetService(DebuggerService.DebuggerService).OnToggleBreakpoint(event, line=self._rightClickLine) + + + def OnPopToggleMarker(self, event): + """ Toggle marker on right click line, not current line """ + wx.GetApp().GetDocumentManager().GetCurrentView().MarkerToggle(lineNum = self._rightClickLine) + + + def OnPopSyncOutline(self, event): + wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(wx.GetApp().GetDocumentManager().GetCurrentView(), position=self._rightClickPosition) + + + def HasSelection(self): + return self.GetSelectionStart() - self.GetSelectionEnd() != 0 + + + def ClearCurrentLineMarkers(self): + self.MarkerDeleteAll(CodeCtrl.CURRENT_LINE_MARKER_NUM) + + + def ClearCurrentBreakpoinMarkers(self): + self.MarkerDeleteAll(CodeCtrl.BREAKPOINT_MARKER_NUM) + + + def GetDefaultFont(self): + if wx.Platform == '__WXMSW__': + font = "Courier New" + else: + font = "Courier" + return wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font) + + + def GetMatchingBraces(self): + """ Overwrite this method for language specific braces """ + return "[]{}()" + + + def CanWordWrap(self): + return False + + + def SetFont(self, font): + self._font = font + + + def SetFontColor(self, fontColor): + self._fontColor = fontColor + + + def UpdateStyles(self): + + if not self.GetFont(): + return + + faces = { 'font' : self.GetFont().GetFaceName(), + 'size' : self.GetFont().GetPointSize(), + 'size2': self.GetFont().GetPointSize() - 2, + 'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue()) + } + + # Global default styles for all languages + self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(font)s,fore:#FFFFFF,size:%(size)d" % faces) + self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER, "face:%(font)s,back:#C0C0C0,face:%(font)s,size:%(size2)d" % faces) + self.StyleSetSpec(wx.stc.STC_STYLE_CONTROLCHAR, "face:%(font)s" % faces) + self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT, "face:%(font)s,fore:#000000,back:#70FFFF,size:%(size)d" % faces) + self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD, "face:%(font)s,fore:#000000,back:#FF0000,size:%(size)d" % faces) + + + def OnKeyPressed(self, event): + if self.CallTipActive(): + self.CallTipCancel() + key = event.KeyCode() + if False: # key == wx.WXK_SPACE and event.ControlDown(): + pos = self.GetCurrentPos() + # Tips + if event.ShiftDown(): + self.CallTipSetBackground("yellow") + self.CallTipShow(pos, 'param1, param2') + # Code completion + else: + #lst = [] + #for x in range(50000): + # lst.append('%05d' % x) + #st = string.join(lst) + #print len(st) + #self.AutoCompShow(0, st) + + kw = keyword.kwlist[:] + kw.append("zzzzzz") + kw.append("aaaaa") + kw.append("__init__") + kw.append("zzaaaaa") + kw.append("zzbaaaa") + kw.append("this_is_a_longer_value") + kw.append("this_is_a_much_much_much_much_much_much_much_longer_value") + + kw.sort() # Python sorts are case sensitive + self.AutoCompSetIgnoreCase(False) # so this needs to match + + self.AutoCompShow(0, string.join(kw)) + elif key == wx.WXK_RETURN: + self.DoIndent() + else: + STCTextEditor.TextCtrl.OnKeyPressed(self, event) + + + def DoIndent(self): + self.AddText('\n') + # Need to do a default one for all languges + + + def OnMarginClick(self, evt): + # fold and unfold as needed + if evt.GetMargin() == 2: + if evt.GetShift() and evt.GetControl(): + lineCount = self.GetLineCount() + expanding = True + + # find out if we are folding or unfolding + for lineNum in range(lineCount): + if self.GetFoldLevel(lineNum) & wx.stc.STC_FOLDLEVELHEADERFLAG: + expanding = not self.GetFoldExpanded(lineNum) + break; + + self.ToggleFoldAll(expanding) + else: + lineClicked = self.LineFromPosition(evt.GetPosition()) + if self.GetFoldLevel(lineClicked) & wx.stc.STC_FOLDLEVELHEADERFLAG: + if evt.GetShift(): + self.SetFoldExpanded(lineClicked, True) + self.Expand(lineClicked, True, True, 1) + elif evt.GetControl(): + if self.GetFoldExpanded(lineClicked): + self.SetFoldExpanded(lineClicked, False) + self.Expand(lineClicked, False, True, 0) + else: + self.SetFoldExpanded(lineClicked, True) + self.Expand(lineClicked, True, True, 100) + else: + self.ToggleFold(lineClicked) + + elif evt.GetMargin() == 0: + #This is used to toggle breakpoints via the debugger service. + db_service = wx.GetApp().GetService(DebuggerService.DebuggerService) + if db_service: + db_service.OnToggleBreakpoint(evt, line=self.LineFromPosition(evt.GetPosition())) + + + def OnUpdateUI(self, evt): + braces = self.GetMatchingBraces() + + # check for matching braces + braceAtCaret = -1 + braceOpposite = -1 + charBefore = None + caretPos = self.GetCurrentPos() + if caretPos > 0: + charBefore = self.GetCharAt(caretPos - 1) + styleBefore = self.GetStyleAt(caretPos - 1) + + # check before + if charBefore and chr(charBefore) in braces: + braceAtCaret = caretPos - 1 + + # check after + if braceAtCaret < 0: + charAfter = self.GetCharAt(caretPos) + styleAfter = self.GetStyleAt(caretPos) + if charAfter and chr(charAfter) in braces: + braceAtCaret = caretPos + + if braceAtCaret >= 0: + braceOpposite = self.BraceMatch(braceAtCaret) + + if braceAtCaret != -1 and braceOpposite == -1: + self.BraceBadLight(braceAtCaret) + else: + self.BraceHighlight(braceAtCaret, braceOpposite) + + evt.Skip() + + + def ToggleFoldAll(self, expand = True, topLevelOnly = False): + i = 0 + lineCount = self.GetLineCount() + while i < lineCount: + if not topLevelOnly or (topLevelOnly and self.GetFoldLevel(i) & wx.stc.STC_FOLDLEVELNUMBERMASK == wx.stc.STC_FOLDLEVELBASE): + if (expand and self.CanLineExpand(i)) or (not expand and self.CanLineCollapse(i)): + self.ToggleFold(i) + i = i + 1 + + + def CanLineExpand(self, line): + return not self.GetFoldExpanded(line) + + + def CanLineCollapse(self, line): + return self.GetFoldExpanded(line) and self.GetFoldLevel(line) & wx.stc.STC_FOLDLEVELHEADERFLAG + + + def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): + lastChild = self.GetLastChild(line, level) + line = line + 1 + while line <= lastChild: + if force: + if visLevels > 0: + self.ShowLines(line, line) + else: + self.HideLines(line, line) + else: + if doExpand: + self.ShowLines(line, line) + + if level == -1: + level = self.GetFoldLevel(line) + + if level & wx.stc.STC_FOLDLEVELHEADERFLAG: + if force: + if visLevels > 1: + self.SetFoldExpanded(line, True) + else: + self.SetFoldExpanded(line, False) + line = self.Expand(line, doExpand, force, visLevels-1) + + else: + if doExpand and self.GetFoldExpanded(line): + line = self.Expand(line, True, force, visLevels-1) + else: + line = self.Expand(line, False, force, visLevels-1) + else: + line = line + 1; + + return line + +