PythonSTC.py
changeset 366 cd90e4c10261
parent 365 a7f58414dea0
child 367 a76ee5307bb7
equal deleted inserted replaced
365:a7f58414dea0 366:cd90e4c10261
     1 import  keyword
       
     2 import  os
       
     3 import  wx
       
     4 import  wx.stc  as  stc
       
     5 
       
     6 #----------------------------------------------------------------------
       
     7 
       
     8 """
       
     9 This Python editor class comes from the wxPython demo, a bit tweaked
       
    10 """
       
    11 
       
    12 #----------------------------------------------------------------------
       
    13 
       
    14 
       
    15 if wx.Platform == '__WXMSW__':
       
    16     faces = { 'times': 'Times New Roman',
       
    17               'mono' : 'Courier New',
       
    18               'helv' : 'Arial',
       
    19               'other': 'Comic Sans MS',
       
    20               'size' : 10,
       
    21               'size2': 8,
       
    22              }
       
    23 elif wx.Platform == '__WXMAC__':
       
    24     faces = { 'times': 'Times New Roman',
       
    25               'mono' : 'Monaco',
       
    26               'helv' : 'Arial',
       
    27               'other': 'Comic Sans MS',
       
    28               'size' : 12,
       
    29               'size2': 10,
       
    30              }
       
    31 else:
       
    32     faces = { 'times': 'Times',
       
    33               'mono' : 'Courier',
       
    34               'helv' : 'Helvetica',
       
    35               'other': 'new century schoolbook',
       
    36               'size' : 12,
       
    37               'size2': 10,
       
    38              }
       
    39 
       
    40 
       
    41 #----------------------------------------------------------------------
       
    42 
       
    43 class PythonSTC(stc.StyledTextCtrl):
       
    44 
       
    45     fold_symbols = 2
       
    46     
       
    47     def __init__(self, parent, ID,
       
    48                  pos=wx.DefaultPosition, size=wx.DefaultSize,
       
    49                  style=0):
       
    50         stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
       
    51 
       
    52         self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
       
    53         self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
       
    54 
       
    55         self.SetLexer(stc.STC_LEX_PYTHON)
       
    56         self.SetKeyWords(0, " ".join(keyword.kwlist))
       
    57 
       
    58         self.SetProperty("fold", "1")
       
    59         self.SetProperty("tab.timmy.whinge.level", "1")
       
    60         self.SetMargins(0,0)
       
    61 
       
    62         self.SetViewWhiteSpace(False)
       
    63         #self.SetBufferedDraw(False)
       
    64         #self.SetViewEOL(True)
       
    65         #self.SetEOLMode(stc.STC_EOL_CRLF)
       
    66         #self.SetUseAntiAliasing(True)
       
    67         
       
    68         self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
       
    69         self.SetEdgeColumn(78)
       
    70 
       
    71         # Setup a margin to hold fold markers
       
    72         #self.SetFoldFlags(16)  ###  WHAT IS THIS VALUE?  WHAT ARE THE OTHER FLAGS?  DOES IT MATTER?
       
    73         self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
       
    74         self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
       
    75         self.SetMarginSensitive(2, True)
       
    76         self.SetMarginWidth(2, 12)
       
    77 
       
    78         if self.fold_symbols == 0:
       
    79             # Arrow pointing right for contracted folders, arrow pointing down for expanded
       
    80             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_ARROWDOWN, "black", "black")
       
    81             self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_ARROW, "black", "black")
       
    82             self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_EMPTY, "black", "black")
       
    83             self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_EMPTY, "black", "black")
       
    84             self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_EMPTY,     "white", "black")
       
    85             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY,     "white", "black")
       
    86             self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY,     "white", "black")
       
    87             
       
    88         elif self.fold_symbols == 1:
       
    89             # Plus for contracted folders, minus for expanded
       
    90             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_MINUS, "white", "black")
       
    91             self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_PLUS,  "white", "black")
       
    92             self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_EMPTY, "white", "black")
       
    93             self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_EMPTY, "white", "black")
       
    94             self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_EMPTY, "white", "black")
       
    95             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
       
    96             self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
       
    97 
       
    98         elif self.fold_symbols == 2:
       
    99             # Like a flattened tree control using circular headers and curved joins
       
   100             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_CIRCLEMINUS,          "white", "#404040")
       
   101             self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_CIRCLEPLUS,           "white", "#404040")
       
   102             self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,                "white", "#404040")
       
   103             self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNERCURVE,         "white", "#404040")
       
   104             self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_CIRCLEPLUSCONNECTED,  "white", "#404040")
       
   105             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
       
   106             self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE,         "white", "#404040")
       
   107 
       
   108         elif self.fold_symbols == 3:
       
   109             # Like a flattened tree control using square headers
       
   110             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_BOXMINUS,          "white", "#808080")
       
   111             self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_BOXPLUS,           "white", "#808080")
       
   112             self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,             "white", "#808080")
       
   113             self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNER,           "white", "#808080")
       
   114             self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_BOXPLUSCONNECTED,  "white", "#808080")
       
   115             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
       
   116             self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER,           "white", "#808080")
       
   117 
       
   118 
       
   119         self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
       
   120         self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
       
   121         self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
       
   122 
       
   123         # Make some styles,  The lexer defines what each style is used for, we
       
   124         # just have to define what each style looks like.  This set is adapted from
       
   125         # Scintilla sample property files.
       
   126 
       
   127         # Global default styles for all languages
       
   128         self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "face:%(helv)s,size:%(size)d" % faces)
       
   129         self.StyleClearAll()  # Reset all to be like the default
       
   130 
       
   131         # Global default styles for all languages
       
   132         self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "face:%(helv)s,size:%(size)d" % faces)
       
   133         self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,  "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
       
   134         self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
       
   135         self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,  "fore:#FFFFFF,back:#0000FF,bold")
       
   136         self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,    "fore:#000000,back:#FF0000,bold")
       
   137 
       
   138         # Python styles
       
   139         # Default 
       
   140         self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
       
   141         # Comments
       
   142         self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
       
   143         # Number
       
   144         self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
       
   145         # String
       
   146         self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
       
   147         # Single quoted string
       
   148         self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
       
   149         # Keyword
       
   150         self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
       
   151         # Triple quotes
       
   152         self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
       
   153         # Triple double quotes
       
   154         self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
       
   155         # Class name definition
       
   156         self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
       
   157         # Function or method name definition
       
   158         self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
       
   159         # Operators
       
   160         self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
       
   161         # Identifiers
       
   162         self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
       
   163         # Comment-blocks
       
   164         self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
       
   165         # End of line where string is not closed
       
   166         self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)
       
   167 
       
   168         self.SetCaretForeground("BLUE")
       
   169 
       
   170     def OnKeyPressed(self, event):
       
   171         if self.CallTipActive():
       
   172             self.CallTipCancel()
       
   173 
       
   174         event.Skip()
       
   175 ## For later use
       
   176 #        key = event.GetKeyCode()
       
   177 #        if key == 32 and event.ControlDown():
       
   178 #            pos = self.GetCurrentPos()
       
   179 #
       
   180 #            # Tips
       
   181 #            if event.ShiftDown():
       
   182 #                self.CallTipSetBackground("yellow")
       
   183 #                self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
       
   184 #                                 'show some suff, maybe parameters..\n\n'
       
   185 #                                 'fubar(param1, param2)')
       
   186 #            # Code completion
       
   187 #            else:
       
   188 #                #lst = []
       
   189 #                #for x in range(50000):
       
   190 #                #    lst.append('%05d' % x)
       
   191 #                #st = " ".join(lst)
       
   192 #                #print len(st)
       
   193 #                #self.AutoCompShow(0, st)
       
   194 #
       
   195 #                kw = keyword.kwlist[:]
       
   196 #                kw.append("zzzzzz?2")
       
   197 #                kw.append("aaaaa?2")
       
   198 #                kw.append("__init__?3")
       
   199 #                kw.append("zzaaaaa?2")
       
   200 #                kw.append("zzbaaaa?2")
       
   201 #                kw.append("this_is_a_longer_value")
       
   202 #                #kw.append("this_is_a_much_much_much_much_much_much_much_longer_value")
       
   203 #
       
   204 #                kw.sort()  # Python sorts are case sensitive
       
   205 #                self.AutoCompSetIgnoreCase(False)  # so this needs to match
       
   206 #
       
   207 #                # Images are specified with a appended "?type"
       
   208 #                for i in range(len(kw)):
       
   209 #                    if kw[i] in keyword.kwlist:
       
   210 #                        kw[i] = kw[i] + "?1"
       
   211 #
       
   212 #                self.AutoCompShow(0, " ".join(kw))
       
   213 #        else:
       
   214 #            event.Skip()
       
   215 
       
   216 
       
   217     def OnUpdateUI(self, evt):
       
   218         # check for matching braces
       
   219         braceAtCaret = -1
       
   220         braceOpposite = -1
       
   221         charBefore = None
       
   222         caretPos = self.GetCurrentPos()
       
   223 
       
   224         if caretPos > 0:
       
   225             charBefore = self.GetCharAt(caretPos - 1)
       
   226             styleBefore = self.GetStyleAt(caretPos - 1)
       
   227 
       
   228         # check before
       
   229         if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
       
   230             braceAtCaret = caretPos - 1
       
   231 
       
   232         # check after
       
   233         if braceAtCaret < 0:
       
   234             charAfter = self.GetCharAt(caretPos)
       
   235             styleAfter = self.GetStyleAt(caretPos)
       
   236 
       
   237             if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
       
   238                 braceAtCaret = caretPos
       
   239 
       
   240         if braceAtCaret >= 0:
       
   241             braceOpposite = self.BraceMatch(braceAtCaret)
       
   242 
       
   243         if braceAtCaret != -1  and braceOpposite == -1:
       
   244             self.BraceBadLight(braceAtCaret)
       
   245         else:
       
   246             self.BraceHighlight(braceAtCaret, braceOpposite)
       
   247             #pt = self.PointFromPosition(braceOpposite)
       
   248             #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
       
   249             #print pt
       
   250             #self.Refresh(False)
       
   251 
       
   252 
       
   253     def OnMarginClick(self, evt):
       
   254         # fold and unfold as needed
       
   255         if evt.GetMargin() == 2:
       
   256             if evt.GetShift() and evt.GetControl():
       
   257                 self.FoldAll()
       
   258             else:
       
   259                 lineClicked = self.LineFromPosition(evt.GetPosition())
       
   260 
       
   261                 if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
       
   262                     if evt.GetShift():
       
   263                         self.SetFoldExpanded(lineClicked, True)
       
   264                         self.Expand(lineClicked, True, True, 1)
       
   265                     elif evt.GetControl():
       
   266                         if self.GetFoldExpanded(lineClicked):
       
   267                             self.SetFoldExpanded(lineClicked, False)
       
   268                             self.Expand(lineClicked, False, True, 0)
       
   269                         else:
       
   270                             self.SetFoldExpanded(lineClicked, True)
       
   271                             self.Expand(lineClicked, True, True, 100)
       
   272                     else:
       
   273                         self.ToggleFold(lineClicked)
       
   274 
       
   275 
       
   276     def FoldAll(self):
       
   277         lineCount = self.GetLineCount()
       
   278         expanding = True
       
   279 
       
   280         # find out if we are folding or unfolding
       
   281         for lineNum in range(lineCount):
       
   282             if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
       
   283                 expanding = not self.GetFoldExpanded(lineNum)
       
   284                 break
       
   285 
       
   286         lineNum = 0
       
   287 
       
   288         while lineNum < lineCount:
       
   289             level = self.GetFoldLevel(lineNum)
       
   290             if level & stc.STC_FOLDLEVELHEADERFLAG and \
       
   291                (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
       
   292 
       
   293                 if expanding:
       
   294                     self.SetFoldExpanded(lineNum, True)
       
   295                     lineNum = self.Expand(lineNum, True)
       
   296                     lineNum = lineNum - 1
       
   297                 else:
       
   298                     lastChild = self.GetLastChild(lineNum, -1)
       
   299                     self.SetFoldExpanded(lineNum, False)
       
   300 
       
   301                     if lastChild > lineNum:
       
   302                         self.HideLines(lineNum+1, lastChild)
       
   303 
       
   304             lineNum = lineNum + 1
       
   305 
       
   306 
       
   307 
       
   308     def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
       
   309         lastChild = self.GetLastChild(line, level)
       
   310         line = line + 1
       
   311 
       
   312         while line <= lastChild:
       
   313             if force:
       
   314                 if visLevels > 0:
       
   315                     self.ShowLines(line, line)
       
   316                 else:
       
   317                     self.HideLines(line, line)
       
   318             else:
       
   319                 if doExpand:
       
   320                     self.ShowLines(line, line)
       
   321 
       
   322             if level == -1:
       
   323                 level = self.GetFoldLevel(line)
       
   324 
       
   325             if level & stc.STC_FOLDLEVELHEADERFLAG:
       
   326                 if force:
       
   327                     if visLevels > 1:
       
   328                         self.SetFoldExpanded(line, True)
       
   329                     else:
       
   330                         self.SetFoldExpanded(line, False)
       
   331 
       
   332                     line = self.Expand(line, doExpand, force, visLevels-1)
       
   333 
       
   334                 else:
       
   335                     if doExpand and self.GetFoldExpanded(line):
       
   336                         line = self.Expand(line, True, force, visLevels-1)
       
   337                     else:
       
   338                         line = self.Expand(line, False, force, visLevels-1)
       
   339             else:
       
   340                 line = line + 1
       
   341 
       
   342         return line
       
   343 
       
   344 
       
   345 #----------------------------------------------------------------------
       
   346 class PythonCodeEditor(PythonSTC):
       
   347     def __init__(self, parent):
       
   348         PythonSTC.__init__(self, parent, -1, style=wx.BORDER_NONE)
       
   349         self.SetUpEditor()
       
   350 
       
   351     # Some methods to make it compatible with how the wxTextCtrl is used
       
   352     def SetValue(self, value):
       
   353         if wx.USE_UNICODE:
       
   354             value = value.decode('utf-8')
       
   355         self.SetText(value)
       
   356         self.EmptyUndoBuffer()
       
   357         self.SetSavePoint()
       
   358 
       
   359     def IsModified(self):
       
   360         return self.GetModify()
       
   361 
       
   362     def Clear(self):
       
   363         self.ClearAll()
       
   364 
       
   365     def SetInsertionPoint(self, pos):
       
   366         self.SetCurrentPos(pos)
       
   367         self.SetAnchor(pos)
       
   368 
       
   369     def ShowPosition(self, pos):
       
   370         line = self.LineFromPosition(pos)
       
   371         #self.EnsureVisible(line)
       
   372         self.GotoLine(line)
       
   373 
       
   374     def GetLastPosition(self):
       
   375         return self.GetLength()
       
   376 
       
   377     def GetPositionFromLine(self, line):
       
   378         return self.PositionFromLine(line)
       
   379 
       
   380     def GetRange(self, start, end):
       
   381         return self.GetTextRange(start, end)
       
   382 
       
   383     def GetSelection(self):
       
   384         return self.GetAnchor(), self.GetCurrentPos()
       
   385 
       
   386     def SetSelection(self, start, end):
       
   387         self.SetSelectionStart(start)
       
   388         self.SetSelectionEnd(end)
       
   389 
       
   390     def SelectLine(self, line):
       
   391         start = self.PositionFromLine(line)
       
   392         end = self.GetLineEndPosition(line)
       
   393         self.SetSelection(start, end)
       
   394         
       
   395     def SetUpEditor(self):
       
   396         """
       
   397         This method carries out the work of setting up the demo editor.            
       
   398         It's seperate so as not to clutter up the init code.
       
   399         """
       
   400         import keyword
       
   401         
       
   402         self.SetLexer(stc.STC_LEX_PYTHON)
       
   403         self.SetKeyWords(0, " ".join(keyword.kwlist))
       
   404 
       
   405         # Enable folding
       
   406         self.SetProperty("fold", "1" ) 
       
   407 
       
   408         # Highlight tab/space mixing (shouldn't be any)
       
   409         self.SetProperty("tab.timmy.whinge.level", "1")
       
   410 
       
   411         # Set left and right margins
       
   412         self.SetMargins(2,2)
       
   413 
       
   414         # Set up the numbers in the margin for margin #1
       
   415         self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
       
   416         # Reasonable value for, say, 4-5 digits using a mono font (40 pix)
       
   417         self.SetMarginWidth(1, 40)
       
   418 
       
   419         # Indentation and tab stuff
       
   420         self.SetIndent(4)               # Proscribed indent size for wx
       
   421         self.SetIndentationGuides(True) # Show indent guides
       
   422         self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
       
   423         self.SetTabIndents(True)        # Tab key indents
       
   424         self.SetTabWidth(4)             # Proscribed tab size for wx
       
   425         self.SetUseTabs(False)          # Use spaces rather than tabs, or
       
   426                                         # TabTimmy will complain!    
       
   427         # White space
       
   428         self.SetViewWhiteSpace(False)   # Don't view white space
       
   429 
       
   430         # EOL: Since we are loading/saving ourselves, and the
       
   431         # strings will always have \n's in them, set the STC to
       
   432         # edit them that way.            
       
   433         self.SetEOLMode(wx.stc.STC_EOL_LF)
       
   434         self.SetViewEOL(False)
       
   435         
       
   436         # No right-edge mode indicator
       
   437         self.SetEdgeMode(stc.STC_EDGE_NONE)
       
   438 
       
   439         # Setup a margin to hold fold markers
       
   440         self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
       
   441         self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
       
   442         self.SetMarginSensitive(2, True)
       
   443         self.SetMarginWidth(2, 12)
       
   444 
       
   445         # and now set up the fold markers
       
   446         self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_BOXPLUSCONNECTED,  "white", "black")
       
   447         self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "black")
       
   448         self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER,  "white", "black")
       
   449         self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNER,  "white", "black")
       
   450         self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,    "white", "black")
       
   451         self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_BOXPLUS,  "white", "black")
       
   452         self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_BOXMINUS, "white", "black")
       
   453 
       
   454         # Global default style
       
   455         if wx.Platform == '__WXMSW__':
       
   456             self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 
       
   457                               'fore:#000000,back:#FFFFFF,face:Courier New')
       
   458         elif wx.Platform == '__WXMAC__':
       
   459             # TODO: if this looks fine on Linux too, remove the Mac-specific case 
       
   460             # and use this whenever OS != MSW.
       
   461             self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 
       
   462                               'fore:#000000,back:#FFFFFF,face:Monaco')
       
   463         else:
       
   464             defsize = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT).GetPointSize()
       
   465             self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 
       
   466                               'fore:#000000,back:#FFFFFF,face:Courier,size:%d'%defsize)
       
   467 
       
   468         # Clear styles and revert to default.
       
   469         self.StyleClearAll()
       
   470 
       
   471         # Following style specs only indicate differences from default.
       
   472         # The rest remains unchanged.
       
   473 
       
   474         # Line numbers in margin
       
   475         self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2')    
       
   476         # Highlighted brace
       
   477         self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
       
   478         # Unmatched brace
       
   479         self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
       
   480         # Indentation guide
       
   481         self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
       
   482 
       
   483         # Python styles
       
   484         self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
       
   485         # Comments
       
   486         self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE,  'fore:#008000,back:#F0FFF0')
       
   487         self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
       
   488         # Numbers
       
   489         self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
       
   490         # Strings and characters
       
   491         self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080')
       
   492         self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080')
       
   493         # Keywords
       
   494         self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
       
   495         # Triple quotes
       
   496         self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
       
   497         self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
       
   498         # Class names
       
   499         self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
       
   500         # Function names
       
   501         self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
       
   502         # Operators
       
   503         self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
       
   504         # Identifiers. I leave this as not bold because everything seems
       
   505         # to be an identifier if it doesn't match the above criterae
       
   506         self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')
       
   507 
       
   508         # Caret color
       
   509         self.SetCaretForeground("BLUE")
       
   510         # Selection background
       
   511         self.SetSelBackground(1, '#66CCFF')
       
   512 
       
   513         self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
       
   514         self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
       
   515 
       
   516     def RegisterModifiedEvent(self, eventHandler):
       
   517         self.Bind(wx.stc.EVT_STC_CHANGE, eventHandler)
       
   518 
       
   519 
       
   520 class PythonCodePanel(wx.Panel):
       
   521     """Panel for the 'Demo Code' tab"""
       
   522     def __init__(self, parent, mainFrame):
       
   523         wx.Panel.__init__(self, parent, size=(1,1))
       
   524         self.mainFrame = mainFrame
       
   525         self.editor = PythonCodeEditor(self)
       
   526         self.editor.RegisterModifiedEvent(self.OnCodeModified)
       
   527 
       
   528         self.btnSave = wx.Button(self, -1, "Save")
       
   529         self.btnRestore = wx.Button(self, -1, "Restore")
       
   530         self.btnSave.Enable(False)
       
   531         self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
       
   532         self.btnRestore.Bind(wx.EVT_BUTTON, self.OnRestore)
       
   533 
       
   534         self.controlBox = wx.BoxSizer(wx.HORIZONTAL)
       
   535         self.controlBox.Add(self.btnSave, 0, wx.RIGHT, 5)
       
   536         self.controlBox.Add(self.btnRestore, 0)
       
   537 
       
   538         self.box = wx.BoxSizer(wx.VERTICAL)
       
   539         self.box.Add(self.controlBox, 0, wx.EXPAND)
       
   540         self.box.Add(wx.StaticLine(self), 0, wx.EXPAND)
       
   541         self.box.Add(self.editor, 1, wx.EXPAND)
       
   542         
       
   543         self.box.Fit(self)
       
   544         self.SetSizer(self.box)
       
   545         
       
   546         self.sourceFile = None
       
   547         
       
   548         self.Bind(wx.EVT_MENU, self.OnSave, id=wx.ID_SAVE)
       
   549         accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL, 83, wx.ID_SAVE)])
       
   550         self.SetAcceleratorTable(accel)
       
   551 
       
   552     # Loads from a file object
       
   553     def LoadSourceFile(self, filename):
       
   554         self.sourceFile = filename
       
   555         if os.path.exists(filename):
       
   556             self.LoadSource(file(filename).read())
       
   557 
       
   558     def LoadSource(self, source):
       
   559         self.editor.Clear()
       
   560         self.editor.SetValue(source)
       
   561         self.JumpToLine(0)
       
   562         self.btnSave.Enable(False)
       
   563 
       
   564     def JumpToLine(self, line, highlight=False):
       
   565         self.editor.GotoLine(line)
       
   566         self.editor.SetFocus()
       
   567         if highlight:
       
   568             self.editor.SelectLine(line)
       
   569                     
       
   570     def OnCodeModified(self, event):
       
   571         self.btnSave.Enable(self.editor.IsModified())
       
   572         # TODO : add callback
       
   573         
       
   574     def OnSave(self, event):
       
   575         overwriteMsg = "You are about to overwrite that file\n" + \
       
   576                        "Do you want to continue?"
       
   577         dlg = wx.MessageDialog(self, overwriteMsg, "wxPython Demo",
       
   578                                wx.YES_NO | wx.NO_DEFAULT| wx.ICON_EXCLAMATION)
       
   579         result = dlg.ShowModal()
       
   580         if result == wx.ID_NO:
       
   581             return
       
   582         dlg.Destroy()
       
   583 
       
   584         source = self.editor.GetText().encode("utf-8")
       
   585 
       
   586         f = file(self.sourceFile, "w")
       
   587         f.write(source)
       
   588         f.close()
       
   589          
       
   590         # TODO
       
   591         #self.mainFrame.SetTreeModified(True)
       
   592 
       
   593 
       
   594     def OnRestore(self, event):
       
   595         self.LoadSourceFile(self.sourceFile)
       
   596         self.btnSave.Enable(self.editor.IsModified())