c_ext/CFileEditor.py
branch1.1 Korean release
changeset 1280 72a826dfcfbb
parent 1138 cf2a6a7c87e8
child 1511 91538d0c242c
equal deleted inserted replaced
977:c8e008b8cefe 1280:72a826dfcfbb
     1 import keyword
       
     2 
     1 
     3 import wx
       
     4 import wx.grid
       
     5 import wx.stc as stc
     2 import wx.stc as stc
     6 import wx.lib.buttons
       
     7 
     3 
     8 from controls import CustomGrid, CustomTable
     4 from controls.CustomStyledTextCtrl import faces
     9 from editors.ConfTreeNodeEditor import ConfTreeNodeEditor, SCROLLBAR_UNIT
     5 from editors.CodeFileEditor import CodeFileEditor, CodeEditor
    10 from util.BitmapLibrary import GetBitmap
       
    11 
     6 
    12 if wx.Platform == '__WXMSW__':
     7 class CppEditor(CodeEditor):
    13     faces = { 'times': 'Times New Roman',
       
    14               'mono' : 'Courier New',
       
    15               'helv' : 'Arial',
       
    16               'other': 'Comic Sans MS',
       
    17               'size' : 10,
       
    18               'size2': 8,
       
    19              }
       
    20 else:
       
    21     faces = { 'times': 'Times',
       
    22               'mono' : 'Courier',
       
    23               'helv' : 'Helvetica',
       
    24               'other': 'new century schoolbook',
       
    25               'size' : 12,
       
    26               'size2': 10,
       
    27              }
       
    28 
     8 
       
     9     KEYWORDS = ["asm", "auto", "bool", "break", "case", "catch", "char", "class", 
       
    10         "const", "const_cast", "continue", "default", "delete", "do", "double", 
       
    11         "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", 
       
    12         "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", 
       
    13         "namespace", "new", "operator", "private", "protected", "public", "register", 
       
    14         "reinterpret_cast", "return", "short", "signed", "sizeof", "static", 
       
    15         "static_cast", "struct", "switch", "template", "this", "throw", "true", "try",
       
    16         "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", 
       
    17         "void", "volatile", "wchar_t", "while"]
       
    18     COMMENT_HEADER = "/"
       
    19     
       
    20     def SetCodeLexer(self):
       
    21         self.SetLexer(stc.STC_LEX_CPP)
       
    22         
       
    23         self.StyleSetSpec(stc.STC_C_COMMENT, 'fore:#408060,size:%(size)d' % faces)
       
    24         self.StyleSetSpec(stc.STC_C_COMMENTLINE, 'fore:#408060,size:%(size)d' % faces)
       
    25         self.StyleSetSpec(stc.STC_C_COMMENTDOC, 'fore:#408060,size:%(size)d' % faces)
       
    26         self.StyleSetSpec(stc.STC_C_NUMBER, 'fore:#0076AE,size:%(size)d' % faces)
       
    27         self.StyleSetSpec(stc.STC_C_WORD, 'bold,fore:#800056,size:%(size)d' % faces)
       
    28         self.StyleSetSpec(stc.STC_C_STRING, 'fore:#2a00ff,size:%(size)d' % faces)
       
    29         self.StyleSetSpec(stc.STC_C_PREPROCESSOR, 'bold,fore:#800056,size:%(size)d' % faces)
       
    30         self.StyleSetSpec(stc.STC_C_OPERATOR, 'bold,size:%(size)d' % faces)
       
    31         self.StyleSetSpec(stc.STC_C_STRINGEOL, 'back:#FFD5FF,size:%(size)d' % faces)
    29 
    32 
    30 def AppendMenu(parent, help, id, kind, text):
    33 #-------------------------------------------------------------------------------
    31     if wx.VERSION >= (2, 6, 0):
    34 #                          CFileEditor Main Frame Class
    32         parent.Append(help=help, id=id, kind=kind, text=text)
    35 #-------------------------------------------------------------------------------
    33     else:
       
    34         parent.Append(helpString=help, id=id, kind=kind, item=text)
       
    35 
    36 
    36 
    37 class CFileEditor(CodeFileEditor):
    37 [ID_CPPEDITOR,
       
    38 ] = [wx.NewId() for _init_ctrls in range(1)]
       
    39 
       
    40 CPP_KEYWORDS = ["asm", "auto", "bool", "break", "case", "catch", "char", "class", 
       
    41     "const", "const_cast", "continue", "default", "delete", "do", "double", 
       
    42     "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", 
       
    43     "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", 
       
    44     "namespace", "new", "operator", "private", "protected", "public", "register", 
       
    45     "reinterpret_cast", "return", "short", "signed", "sizeof", "static", 
       
    46     "static_cast", "struct", "switch", "template", "this", "throw", "true", "try",
       
    47     "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", 
       
    48     "void", "volatile", "wchar_t", "while"]
       
    49 
       
    50 def GetCursorPos(old, new):
       
    51     old_length = len(old)
       
    52     new_length = len(new)
       
    53     common_length = min(old_length, new_length)
       
    54     i = 0
       
    55     for i in xrange(common_length):
       
    56         if old[i] != new[i]:
       
    57             break
       
    58     if old_length < new_length:
       
    59         if common_length > 0 and old[i] != new[i]:
       
    60             return i + new_length - old_length
       
    61         else:
       
    62             return i + new_length - old_length + 1
       
    63     elif old_length > new_length or i < min(old_length, new_length) - 1:
       
    64         if common_length > 0 and old[i] != new[i]:
       
    65             return i
       
    66         else:
       
    67             return i + 1
       
    68     else:
       
    69         return None
       
    70 
       
    71 class CppEditor(stc.StyledTextCtrl):
       
    72 
       
    73     fold_symbols = 3
       
    74     
    38     
    75     def __init__(self, parent, name, window, controler):
    39     CONFNODEEDITOR_TABS = [
    76         stc.StyledTextCtrl.__init__(self, parent, ID_CPPEDITOR, wx.DefaultPosition, 
    40         (_("C code"), "_create_CodePanel")]
    77                  wx.Size(-1, 300), 0)
    41     CODE_EDITOR = CppEditor
    78 
       
    79         self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
       
    80         self.SetMarginWidth(1, 25)
       
    81 
       
    82         self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
       
    83         self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
       
    84 
       
    85         self.SetLexer(stc.STC_LEX_CPP)
       
    86         self.SetKeyWords(0, " ".join(CPP_KEYWORDS))
       
    87 
       
    88         self.SetProperty("fold", "1")
       
    89         self.SetProperty("tab.timmy.whinge.level", "1")
       
    90         self.SetMargins(0,0)
       
    91 
       
    92         self.SetViewWhiteSpace(False)
       
    93         #self.SetBufferedDraw(False)
       
    94         #self.SetViewEOL(True)
       
    95         #self.SetEOLMode(stc.STC_EOL_CRLF)
       
    96         #self.SetUseAntiAliasing(True)
       
    97         
       
    98         self.SetEdgeMode(stc.STC_EDGE_BACKGROUND)
       
    99         self.SetEdgeColumn(78)
       
   100 
       
   101         # Setup a margin to hold fold markers
       
   102         #self.SetFoldFlags(16)  ###  WHAT IS THIS VALUE?  WHAT ARE THE OTHER FLAGS?  DOES IT MATTER?
       
   103         self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
       
   104         self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
       
   105         self.SetMarginSensitive(2, True)
       
   106         self.SetMarginWidth(2, 12)
       
   107 
       
   108         if self.fold_symbols == 0:
       
   109             # Arrow pointing right for contracted folders, arrow pointing down for expanded
       
   110             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_ARROWDOWN, "black", "black")
       
   111             self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_ARROW, "black", "black")
       
   112             self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_EMPTY, "black", "black")
       
   113             self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_EMPTY, "black", "black")
       
   114             self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_EMPTY,     "white", "black")
       
   115             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY,     "white", "black")
       
   116             self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY,     "white", "black")
       
   117             
       
   118         elif self.fold_symbols == 1:
       
   119             # Plus for contracted folders, minus for expanded
       
   120             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_MINUS, "white", "black")
       
   121             self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_PLUS,  "white", "black")
       
   122             self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_EMPTY, "white", "black")
       
   123             self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_EMPTY, "white", "black")
       
   124             self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_EMPTY, "white", "black")
       
   125             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
       
   126             self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
       
   127 
       
   128         elif self.fold_symbols == 2:
       
   129             # Like a flattened tree control using circular headers and curved joins
       
   130             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_CIRCLEMINUS,          "white", "#404040")
       
   131             self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_CIRCLEPLUS,           "white", "#404040")
       
   132             self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,                "white", "#404040")
       
   133             self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNERCURVE,         "white", "#404040")
       
   134             self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_CIRCLEPLUSCONNECTED,  "white", "#404040")
       
   135             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
       
   136             self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE,         "white", "#404040")
       
   137 
       
   138         elif self.fold_symbols == 3:
       
   139             # Like a flattened tree control using square headers
       
   140             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN,    stc.STC_MARK_BOXMINUS,          "white", "#808080")
       
   141             self.MarkerDefine(stc.STC_MARKNUM_FOLDER,        stc.STC_MARK_BOXPLUS,           "white", "#808080")
       
   142             self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB,     stc.STC_MARK_VLINE,             "white", "#808080")
       
   143             self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL,    stc.STC_MARK_LCORNER,           "white", "#808080")
       
   144             self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND,     stc.STC_MARK_BOXPLUSCONNECTED,  "white", "#808080")
       
   145             self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
       
   146             self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER,           "white", "#808080")
       
   147 
       
   148 
       
   149         self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
       
   150         self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
       
   151         self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
       
   152 
       
   153         # Make some styles,  The lexer defines what each style is used for, we
       
   154         # just have to define what each style looks like.  This set is adapted from
       
   155         # Scintilla sample property files.
       
   156 
       
   157         # Global default styles for all languages
       
   158         self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "face:%(mono)s,size:%(size)d" % faces)
       
   159         self.StyleClearAll()  # Reset all to be like the default
       
   160 
       
   161         # Global default styles for all languages
       
   162         self.StyleSetSpec(stc.STC_STYLE_DEFAULT,     "face:%(mono)s,size:%(size)d" % faces)
       
   163         self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,  "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
       
   164         self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
       
   165         self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,  "fore:#FFFFFF,back:#0000FF,bold")
       
   166         self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,    "fore:#000000,back:#FF0000,bold")
       
   167 
       
   168         self.StyleSetSpec(stc.STC_C_COMMENT, 'fore:#408060')
       
   169         self.StyleSetSpec(stc.STC_C_COMMENTLINE, 'fore:#408060')
       
   170         self.StyleSetSpec(stc.STC_C_COMMENTDOC, 'fore:#408060')
       
   171         self.StyleSetSpec(stc.STC_C_NUMBER, 'fore:#0076AE')
       
   172         self.StyleSetSpec(stc.STC_C_WORD, 'bold,fore:#800056')
       
   173         self.StyleSetSpec(stc.STC_C_STRING, 'fore:#2a00ff')
       
   174         self.StyleSetSpec(stc.STC_C_PREPROCESSOR, 'bold,fore:#800056')
       
   175         self.StyleSetSpec(stc.STC_C_OPERATOR, 'bold')
       
   176         self.StyleSetSpec(stc.STC_C_STRINGEOL, 'back:#FFD5FF')
       
   177         
       
   178         # register some images for use in the AutoComplete box.
       
   179         #self.RegisterImage(1, images.getSmilesBitmap())
       
   180         self.RegisterImage(1, 
       
   181             wx.ArtProvider.GetBitmap(wx.ART_DELETE, size=(16,16)))
       
   182         self.RegisterImage(2, 
       
   183             wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
       
   184         self.RegisterImage(3, 
       
   185             wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
       
   186 
       
   187         # Indentation size
       
   188         self.SetTabWidth(2)
       
   189         self.SetUseTabs(0)
       
   190 
       
   191         self.Controler = controler
       
   192         self.ParentWindow = window
       
   193         
       
   194         self.DisableEvents = True
       
   195         self.Name = name
       
   196         self.CurrentAction = None
       
   197         
       
   198         self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|wx.stc.STC_MOD_BEFOREDELETE)
       
   199 
       
   200         self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_CPPEDITOR)
       
   201         self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
       
   202         self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_CPPEDITOR)
       
   203     
       
   204     def OnModification(self, event):
       
   205         if not self.DisableEvents:
       
   206             mod_type = event.GetModificationType()
       
   207             if not (mod_type&wx.stc.STC_PERFORMED_UNDO or mod_type&wx.stc.STC_PERFORMED_REDO):
       
   208                 if mod_type&wx.stc.STC_MOD_BEFOREINSERT:
       
   209                     if self.CurrentAction == None:
       
   210                         self.StartBuffering()
       
   211                     elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1:
       
   212                         self.Controler.EndBuffering()
       
   213                         self.StartBuffering()
       
   214                     self.CurrentAction = ("Add", event.GetPosition())
       
   215                     wx.CallAfter(self.RefreshModel)
       
   216                 elif mod_type&wx.stc.STC_MOD_BEFOREDELETE:
       
   217                     if self.CurrentAction == None:
       
   218                         self.StartBuffering()
       
   219                     elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1:
       
   220                         self.Controler.EndBuffering()
       
   221                         self.StartBuffering()
       
   222                     self.CurrentAction = ("Delete", event.GetPosition())
       
   223                     wx.CallAfter(self.RefreshModel)
       
   224         event.Skip()
       
   225     
       
   226     def OnDoDrop(self, event):
       
   227         self.ResetBuffer()
       
   228         wx.CallAfter(self.RefreshModel)
       
   229         event.Skip()
       
   230 
       
   231     # Buffer the last model state
       
   232     def RefreshBuffer(self):
       
   233         self.Controler.BufferCFile()
       
   234         if self.ParentWindow is not None:
       
   235             self.ParentWindow.RefreshTitle()
       
   236             self.ParentWindow.RefreshFileMenu()
       
   237             self.ParentWindow.RefreshEditMenu()
       
   238             self.ParentWindow.RefreshPageTitles()
       
   239     
       
   240     def StartBuffering(self):
       
   241         self.Controler.StartBuffering()
       
   242         if self.ParentWindow is not None:
       
   243             self.ParentWindow.RefreshTitle()
       
   244             self.ParentWindow.RefreshFileMenu()
       
   245             self.ParentWindow.RefreshEditMenu()
       
   246             self.ParentWindow.RefreshPageTitles()
       
   247     
       
   248     def ResetBuffer(self):
       
   249         if self.CurrentAction != None:
       
   250             self.Controler.EndBuffering()
       
   251             self.CurrentAction = None
       
   252 
       
   253     def RefreshView(self):
       
   254         self.ResetBuffer()
       
   255         self.DisableEvents = True
       
   256         old_cursor_pos = self.GetCurrentPos()
       
   257         old_text = self.GetText()
       
   258         new_text = self.Controler.GetPartText(self.Name)
       
   259         self.SetText(new_text)
       
   260         new_cursor_pos = GetCursorPos(old_text, new_text)
       
   261         if new_cursor_pos != None:
       
   262             self.GotoPos(new_cursor_pos)
       
   263         else:
       
   264             self.GotoPos(old_cursor_pos)
       
   265         self.ScrollToColumn(0)
       
   266         self.EmptyUndoBuffer()
       
   267         self.DisableEvents = False
       
   268         
       
   269         self.Colourise(0, -1)
       
   270 
       
   271     def DoGetBestSize(self):
       
   272         return self.ParentWindow.GetPanelBestSize()
       
   273 
       
   274     def RefreshModel(self):
       
   275         self.Controler.SetPartText(self.Name, self.GetText())
       
   276 
       
   277     def OnKeyPressed(self, event):
       
   278         if self.CallTipActive():
       
   279             self.CallTipCancel()
       
   280         key = event.GetKeyCode()
       
   281 
       
   282         if key == 32 and event.ControlDown():
       
   283             pos = self.GetCurrentPos()
       
   284 
       
   285             # Tips
       
   286             if event.ShiftDown():
       
   287                 pass
       
   288 ##                self.CallTipSetBackground("yellow")
       
   289 ##                self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
       
   290 ##                                 'show some suff, maybe parameters..\n\n'
       
   291 ##                                 'fubar(param1, param2)')
       
   292             # Code completion
       
   293             else:
       
   294                 self.AutoCompSetIgnoreCase(False)  # so this needs to match
       
   295 
       
   296                 # Images are specified with a appended "?type"
       
   297                 self.AutoCompShow(0, " ".join([word + "?1" for word in CPP_KEYWORDS]))
       
   298         else:
       
   299             event.Skip()
       
   300 
       
   301     def OnKillFocus(self, event):
       
   302         self.AutoCompCancel()
       
   303         event.Skip()
       
   304 
       
   305     def OnUpdateUI(self, evt):
       
   306         # check for matching braces
       
   307         braceAtCaret = -1
       
   308         braceOpposite = -1
       
   309         charBefore = None
       
   310         caretPos = self.GetCurrentPos()
       
   311 
       
   312         if caretPos > 0:
       
   313             charBefore = self.GetCharAt(caretPos - 1)
       
   314             styleBefore = self.GetStyleAt(caretPos - 1)
       
   315 
       
   316         # check before
       
   317         if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
       
   318             braceAtCaret = caretPos - 1
       
   319 
       
   320         # check after
       
   321         if braceAtCaret < 0:
       
   322             charAfter = self.GetCharAt(caretPos)
       
   323             styleAfter = self.GetStyleAt(caretPos)
       
   324 
       
   325             if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
       
   326                 braceAtCaret = caretPos
       
   327 
       
   328         if braceAtCaret >= 0:
       
   329             braceOpposite = self.BraceMatch(braceAtCaret)
       
   330 
       
   331         if braceAtCaret != -1  and braceOpposite == -1:
       
   332             self.BraceBadLight(braceAtCaret)
       
   333         else:
       
   334             self.BraceHighlight(braceAtCaret, braceOpposite)
       
   335             #pt = self.PointFromPosition(braceOpposite)
       
   336             #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
       
   337             #print pt
       
   338             #self.Refresh(False)
       
   339 
       
   340 
       
   341     def OnMarginClick(self, evt):
       
   342         # fold and unfold as needed
       
   343         if evt.GetMargin() == 2:
       
   344             if evt.GetShift() and evt.GetControl():
       
   345                 self.FoldAll()
       
   346             else:
       
   347                 lineClicked = self.LineFromPosition(evt.GetPosition())
       
   348 
       
   349                 if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
       
   350                     if evt.GetShift():
       
   351                         self.SetFoldExpanded(lineClicked, True)
       
   352                         self.Expand(lineClicked, True, True, 1)
       
   353                     elif evt.GetControl():
       
   354                         if self.GetFoldExpanded(lineClicked):
       
   355                             self.SetFoldExpanded(lineClicked, False)
       
   356                             self.Expand(lineClicked, False, True, 0)
       
   357                         else:
       
   358                             self.SetFoldExpanded(lineClicked, True)
       
   359                             self.Expand(lineClicked, True, True, 100)
       
   360                     else:
       
   361                         self.ToggleFold(lineClicked)
       
   362 
       
   363 
       
   364     def FoldAll(self):
       
   365         lineCount = self.GetLineCount()
       
   366         expanding = True
       
   367 
       
   368         # find out if we are folding or unfolding
       
   369         for lineNum in range(lineCount):
       
   370             if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
       
   371                 expanding = not self.GetFoldExpanded(lineNum)
       
   372                 break
       
   373 
       
   374         lineNum = 0
       
   375 
       
   376         while lineNum < lineCount:
       
   377             level = self.GetFoldLevel(lineNum)
       
   378             if level & stc.STC_FOLDLEVELHEADERFLAG and \
       
   379                (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
       
   380 
       
   381                 if expanding:
       
   382                     self.SetFoldExpanded(lineNum, True)
       
   383                     lineNum = self.Expand(lineNum, True)
       
   384                     lineNum = lineNum - 1
       
   385                 else:
       
   386                     lastChild = self.GetLastChild(lineNum, -1)
       
   387                     self.SetFoldExpanded(lineNum, False)
       
   388 
       
   389                     if lastChild > lineNum:
       
   390                         self.HideLines(lineNum+1, lastChild)
       
   391 
       
   392             lineNum = lineNum + 1
       
   393 
    42 
   394 
    43 
   395 
    44 
   396     def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
       
   397         lastChild = self.GetLastChild(line, level)
       
   398         line = line + 1
       
   399 
       
   400         while line <= lastChild:
       
   401             if force:
       
   402                 if visLevels > 0:
       
   403                     self.ShowLines(line, line)
       
   404                 else:
       
   405                     self.HideLines(line, line)
       
   406             else:
       
   407                 if doExpand:
       
   408                     self.ShowLines(line, line)
       
   409 
       
   410             if level == -1:
       
   411                 level = self.GetFoldLevel(line)
       
   412 
       
   413             if level & stc.STC_FOLDLEVELHEADERFLAG:
       
   414                 if force:
       
   415                     if visLevels > 1:
       
   416                         self.SetFoldExpanded(line, True)
       
   417                     else:
       
   418                         self.SetFoldExpanded(line, False)
       
   419 
       
   420                     line = self.Expand(line, doExpand, force, visLevels-1)
       
   421 
       
   422                 else:
       
   423                     if doExpand and self.GetFoldExpanded(line):
       
   424                         line = self.Expand(line, True, force, visLevels-1)
       
   425                     else:
       
   426                         line = self.Expand(line, False, force, visLevels-1)
       
   427             else:
       
   428                 line = line + 1
       
   429 
       
   430         return line
       
   431 
       
   432     def Cut(self):
       
   433         self.ResetBuffer()
       
   434         self.DisableEvents = True
       
   435         self.CmdKeyExecute(wx.stc.STC_CMD_CUT)
       
   436         self.DisableEvents = False
       
   437         self.RefreshModel()
       
   438         self.RefreshBuffer()
       
   439     
       
   440     def Copy(self):
       
   441         self.CmdKeyExecute(wx.stc.STC_CMD_COPY)
       
   442     
       
   443     def Paste(self):
       
   444         self.ResetBuffer()
       
   445         self.DisableEvents = True
       
   446         self.CmdKeyExecute(wx.stc.STC_CMD_PASTE)
       
   447         self.DisableEvents = False
       
   448         self.RefreshModel()
       
   449         self.RefreshBuffer()
       
   450 
       
   451 
       
   452 #-------------------------------------------------------------------------------
       
   453 #                         Helper for VariablesGrid values
       
   454 #-------------------------------------------------------------------------------
       
   455 
       
   456 class VariablesTable(CustomTable):
       
   457     
       
   458     def GetValue(self, row, col):
       
   459         if row < self.GetNumberRows():
       
   460             if col == 0:
       
   461                 return row + 1
       
   462             else:
       
   463                 return str(self.data[row].get(self.GetColLabelValue(col, False), ""))
       
   464     
       
   465     def _updateColAttrs(self, grid):
       
   466         """
       
   467         wxGrid -> update the column attributes to add the
       
   468         appropriate renderer given the column name.
       
   469 
       
   470         Otherwise default to the default renderer.
       
   471         """
       
   472         
       
   473         typelist = None
       
   474         accesslist = None
       
   475         for row in range(self.GetNumberRows()):
       
   476             for col in range(self.GetNumberCols()):
       
   477                 editor = None
       
   478                 renderer = None
       
   479                 colname = self.GetColLabelValue(col, False)
       
   480                 
       
   481                 if colname == "Name":
       
   482                     editor = wx.grid.GridCellTextEditor()
       
   483                 elif colname == "Class":
       
   484                     editor = wx.grid.GridCellChoiceEditor()
       
   485                     editor.SetParameters("input,memory,output")
       
   486                 elif colname == "Type":
       
   487                     pass
       
   488                 else:
       
   489                     grid.SetReadOnly(row, col, True)
       
   490                 
       
   491                 grid.SetCellEditor(row, col, editor)
       
   492                 grid.SetCellRenderer(row, col, renderer)
       
   493                 
       
   494                 grid.SetCellBackgroundColour(row, col, wx.WHITE)
       
   495             self.ResizeRow(grid, row)
       
   496 
       
   497 
       
   498 class VariablesEditor(wx.Panel):
       
   499     
       
   500     def __init__(self, parent, window, controler):
       
   501         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
       
   502         
       
   503         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=4)
       
   504         main_sizer.AddGrowableCol(0)
       
   505         main_sizer.AddGrowableRow(0)
       
   506         
       
   507         self.VariablesGrid = CustomGrid(self, size=wx.Size(-1, 300), style=wx.VSCROLL)
       
   508         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange)
       
   509         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
       
   510         self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown)
       
   511         main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
       
   512         
       
   513         controls_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   514         main_sizer.AddSizer(controls_sizer, border=5, flag=wx.TOP|wx.ALIGN_RIGHT)
       
   515         
       
   516         for name, bitmap, help in [
       
   517                 ("AddVariableButton", "add_element", _("Add variable")),
       
   518                 ("DeleteVariableButton", "remove_element", _("Remove variable")),
       
   519                 ("UpVariableButton", "up", _("Move variable up")),
       
   520                 ("DownVariableButton", "down", _("Move variable down"))]:
       
   521             button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), 
       
   522                   size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   523             button.SetToolTipString(help)
       
   524             setattr(self, name, button)
       
   525             controls_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
   526         
       
   527         self.SetSizer(main_sizer)
       
   528                 
       
   529         self.ParentWindow = window
       
   530         self.Controler = controler
       
   531         
       
   532         self.VariablesDefaultValue = {"Name" : "", "Class" : "input", "Type" : ""}
       
   533         self.Table = VariablesTable(self, [], ["#", "Name", "Class", "Type"])
       
   534         self.ColAlignements = [wx.ALIGN_RIGHT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
       
   535         self.ColSizes = [40, 200, 150, 150]
       
   536         self.VariablesGrid.SetTable(self.Table)
       
   537         self.VariablesGrid.SetButtons({"Add": self.AddVariableButton,
       
   538                                        "Delete": self.DeleteVariableButton,
       
   539                                        "Up": self.UpVariableButton,
       
   540                                        "Down": self.DownVariableButton})
       
   541         
       
   542         def _AddVariable(new_row):
       
   543             self.Table.InsertRow(new_row, self.VariablesDefaultValue.copy())
       
   544             self.RefreshModel()
       
   545             self.RefreshView()
       
   546             return new_row
       
   547         setattr(self.VariablesGrid, "_AddRow", _AddVariable)
       
   548         
       
   549         def _DeleteVariable(row):
       
   550             self.Table.RemoveRow(row)
       
   551             self.RefreshModel()
       
   552             self.RefreshView()
       
   553         setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
       
   554         
       
   555         def _MoveVariable(row, move):
       
   556             new_row = self.Table.MoveRow(row, move)
       
   557             if new_row != row:
       
   558                 self.RefreshModel()
       
   559                 self.RefreshView()
       
   560             return new_row
       
   561         setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
       
   562         
       
   563         self.VariablesGrid.SetRowLabelSize(0)
       
   564         for col in range(self.Table.GetNumberCols()):
       
   565             attr = wx.grid.GridCellAttr()
       
   566             attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE)
       
   567             self.VariablesGrid.SetColAttr(col, attr)
       
   568             self.VariablesGrid.SetColSize(col, self.ColSizes[col])
       
   569         self.Table.ResetView(self.VariablesGrid)
       
   570 
       
   571     def RefreshModel(self):
       
   572         self.Controler.SetVariables(self.Table.GetData())
       
   573         self.RefreshBuffer()
       
   574         
       
   575     # Buffer the last model state
       
   576     def RefreshBuffer(self):
       
   577         self.Controler.BufferCFile()
       
   578         self.ParentWindow.RefreshTitle()
       
   579         self.ParentWindow.RefreshFileMenu()
       
   580         self.ParentWindow.RefreshEditMenu()
       
   581         self.ParentWindow.RefreshPageTitles()
       
   582 
       
   583     def RefreshView(self):
       
   584         self.Table.SetData(self.Controler.GetVariables())
       
   585         self.Table.ResetView(self.VariablesGrid)
       
   586         self.VariablesGrid.RefreshButtons()
       
   587     
       
   588     def DoGetBestSize(self):
       
   589         return self.ParentWindow.GetPanelBestSize()
       
   590     
       
   591     def OnVariablesGridCellChange(self, event):
       
   592         self.RefreshModel()
       
   593         wx.CallAfter(self.RefreshView)
       
   594         event.Skip()
       
   595 
       
   596     def OnVariablesGridEditorShown(self, event):
       
   597         row, col = event.GetRow(), event.GetCol() 
       
   598         if self.Table.GetColLabelValue(col, False) == "Type":
       
   599             type_menu = wx.Menu(title='')
       
   600             base_menu = wx.Menu(title='')
       
   601             for base_type in self.Controler.GetBaseTypes():
       
   602                 new_id = wx.NewId()
       
   603                 AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type)
       
   604                 self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), id=new_id)
       
   605             type_menu.AppendMenu(wx.NewId(), "Base Types", base_menu)
       
   606             datatype_menu = wx.Menu(title='')
       
   607             for datatype in self.Controler.GetDataTypes(basetypes=False, only_locatables=True):
       
   608                 new_id = wx.NewId()
       
   609                 AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
       
   610                 self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id)
       
   611             type_menu.AppendMenu(wx.NewId(), "User Data Types", datatype_menu)
       
   612             rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col))
       
   613             
       
   614             self.VariablesGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.VariablesGrid.GetColLabelSize())
       
   615             type_menu.Destroy()
       
   616             event.Veto()
       
   617         else:
       
   618             event.Skip()
       
   619 
       
   620     def GetVariableTypeFunction(self, base_type):
       
   621         def VariableTypeFunction(event):
       
   622             row = self.VariablesGrid.GetGridCursorRow()
       
   623             self.Table.SetValueByName(row, "Type", base_type)
       
   624             self.Table.ResetView(self.VariablesGrid)
       
   625             self.RefreshModel()
       
   626             self.RefreshView()
       
   627             event.Skip()
       
   628         return VariableTypeFunction
       
   629 
       
   630     def OnVariablesGridCellLeftClick(self, event):
       
   631         if event.GetCol() == 0:
       
   632             row = event.GetRow()
       
   633             num = 0
       
   634             if self.Table.GetValueByName(row, "Class") == "input":
       
   635                 dir = "%I"
       
   636                 for i in xrange(row):
       
   637                     if self.Table.GetValueByName(i, "Class") == "input":
       
   638                         num += 1
       
   639             elif self.Table.GetValueByName(row, "Class") == "memory":
       
   640                 dir = "%M"
       
   641                 for i in xrange(row):
       
   642                     if self.Table.GetValueByName(i, "Class") == "memory":
       
   643                         num += 1
       
   644             else:
       
   645                 dir = "%Q"
       
   646                 for i in xrange(row):
       
   647                     if self.Table.GetValueByName(i, "Class") == "output":
       
   648                         num += 1
       
   649             data_type = self.Table.GetValueByName(row, "Type")
       
   650             var_name = self.Table.GetValueByName(row, "Name")
       
   651             base_location = ".".join(map(lambda x:str(x), self.Controler.GetCurrentLocation()))
       
   652             location = "%s%s%s.%d"%(dir, self.Controler.GetSizeOfType(data_type), base_location, num)
       
   653             data = wx.TextDataObject(str((location, "location", data_type, var_name, "")))
       
   654             dragSource = wx.DropSource(self.VariablesGrid)
       
   655             dragSource.SetData(data)
       
   656             dragSource.DoDragDrop()
       
   657             return
       
   658         event.Skip()
       
   659     
       
   660 
       
   661 #-------------------------------------------------------------------------------
       
   662 #                          SVGUIEditor Main Frame Class
       
   663 #-------------------------------------------------------------------------------
       
   664 
       
   665 CFILE_PARTS = [
       
   666     ("Includes", CppEditor), 
       
   667     ("Variables", VariablesEditor), 
       
   668     ("Globals", CppEditor), 
       
   669     ("Init", CppEditor), 
       
   670     ("CleanUp", CppEditor), 
       
   671     ("Retrieve", CppEditor), 
       
   672     ("Publish", CppEditor),
       
   673 ]
       
   674 
       
   675 class FoldPanelCaption(wx.lib.buttons.GenBitmapTextToggleButton):
       
   676     
       
   677     def GetBackgroundBrush(self, dc):
       
   678         colBg = self.GetBackgroundColour()
       
   679         brush = wx.Brush(colBg, wx.SOLID)
       
   680         if self.style & wx.BORDER_NONE:
       
   681             myAttr = self.GetDefaultAttributes()
       
   682             parAttr = self.GetParent().GetDefaultAttributes()
       
   683             myDef = colBg == myAttr.colBg
       
   684             parDef = self.GetParent().GetBackgroundColour() == parAttr.colBg
       
   685             if myDef and parDef:
       
   686                 if wx.Platform == "__WXMAC__":
       
   687                     brush.MacSetTheme(1) # 1 == kThemeBrushDialogBackgroundActive
       
   688                 elif wx.Platform == "__WXMSW__":
       
   689                     if self.DoEraseBackground(dc):
       
   690                         brush = None
       
   691             elif myDef and not parDef:
       
   692                 colBg = self.GetParent().GetBackgroundColour()
       
   693                 brush = wx.Brush(colBg, wx.SOLID)
       
   694         return brush
       
   695     
       
   696     def DrawLabel(self, dc, width, height, dx=0, dy=0):
       
   697         bmp = self.bmpLabel
       
   698         if bmp is not None:     # if the bitmap is used
       
   699             if self.bmpDisabled and not self.IsEnabled():
       
   700                 bmp = self.bmpDisabled
       
   701             if self.bmpFocus and self.hasFocus:
       
   702                 bmp = self.bmpFocus
       
   703             if self.bmpSelected and not self.up:
       
   704                 bmp = self.bmpSelected
       
   705             bw,bh = bmp.GetWidth(), bmp.GetHeight()
       
   706             hasMask = bmp.GetMask() is not None
       
   707         else:
       
   708             bw = bh = 0     # no bitmap -> size is zero
       
   709         
       
   710         dc.SetFont(self.GetFont())
       
   711         if self.IsEnabled():
       
   712             dc.SetTextForeground(self.GetForegroundColour())
       
   713         else:
       
   714             dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
       
   715 
       
   716         label = self.GetLabel()
       
   717         tw, th = dc.GetTextExtent(label)        # size of text
       
   718         
       
   719         if bmp is not None:
       
   720             dc.DrawBitmap(bmp, width - bw - 2, (height-bh)/2, hasMask) # draw bitmap if available
       
   721         
       
   722         dc.DrawText(label, 2, (height-th)/2)      # draw the text
       
   723 
       
   724         dc.SetPen(wx.Pen(self.GetForegroundColour()))
       
   725         dc.SetBrush(wx.TRANSPARENT_BRUSH)
       
   726         dc.DrawRectangle(0, 0, width, height)
       
   727 
       
   728 class CFileEditor(ConfTreeNodeEditor):
       
   729     
       
   730     CONFNODEEDITOR_TABS = [
       
   731         (_("C code"), "_create_CCodeEditor")]
       
   732     
       
   733     def _create_CCodeEditor(self, prnt):
       
   734         self.CCodeEditor = wx.ScrolledWindow(prnt, 
       
   735               style=wx.TAB_TRAVERSAL|wx.HSCROLL|wx.VSCROLL)
       
   736         self.CCodeEditor.Bind(wx.EVT_SIZE, self.OnCCodeEditorResize)
       
   737         
       
   738         self.Panels = {}
       
   739         self.MainSizer = wx.BoxSizer(wx.VERTICAL)
       
   740         
       
   741         for idx, (name, panel_class) in enumerate(CFILE_PARTS):
       
   742             button_id = wx.NewId()
       
   743             button = FoldPanelCaption(id=button_id, name='FoldPanelCaption_%s' % name, 
       
   744                   label=name, bitmap=GetBitmap("CollapsedIconData"), 
       
   745                   parent=self.CCodeEditor, pos=wx.Point(0, 0),
       
   746                   size=wx.Size(0, 20), style=wx.NO_BORDER|wx.ALIGN_LEFT)
       
   747             button.SetBitmapSelected(GetBitmap("ExpandedIconData"))
       
   748             button.Bind(wx.EVT_BUTTON, self.GenPanelButtonCallback(name), id=button_id)
       
   749             self.MainSizer.AddWindow(button, 0, border=0, flag=wx.TOP|wx.GROW)
       
   750             
       
   751             if panel_class == VariablesEditor:
       
   752                 panel = VariablesEditor(self.CCodeEditor, self.ParentWindow, self.Controler)
       
   753             else:
       
   754                 panel = panel_class(self.CCodeEditor, name, self.ParentWindow, self.Controler)
       
   755             self.MainSizer.AddWindow(panel, 0, border=0, flag=wx.BOTTOM|wx.GROW)
       
   756             panel.Hide()
       
   757             
       
   758             self.Panels[name] = {"button": button, "panel": panel, "expanded": False, "row": 2 * idx + 1}
       
   759         
       
   760         self.CCodeEditor.SetSizer(self.MainSizer)
       
   761         
       
   762         return self.CCodeEditor
       
   763     
       
   764     def __init__(self, parent, controler, window):
       
   765         ConfTreeNodeEditor.__init__(self, parent, controler, window)
       
   766     
       
   767     def GetBufferState(self):
       
   768         return self.Controler.GetBufferState()
       
   769         
       
   770     def Undo(self):
       
   771         self.Controler.LoadPrevious()
       
   772         self.RefreshView()
       
   773             
       
   774     def Redo(self):
       
   775         self.Controler.LoadNext()
       
   776         self.RefreshView()
       
   777     
       
   778     def RefreshView(self):
       
   779         ConfTreeNodeEditor.RefreshView(self)
       
   780         
       
   781         for infos in self.Panels.itervalues():
       
   782             infos["panel"].RefreshView()
       
   783         
       
   784         self.RefreshCCodeEditorScrollbars()
       
   785 
       
   786     def GenPanelButtonCallback(self, name):
       
   787         def PanelButtonCallback(event):
       
   788             self.TogglePanel(name)
       
   789         return PanelButtonCallback
       
   790 
       
   791     def ExpandPanel(self, name):
       
   792         infos = self.Panels.get(name, None)
       
   793         if infos is not None and not infos["expanded"]:
       
   794             infos["expanded"] = True
       
   795             infos["button"].SetToggle(True)
       
   796             infos["panel"].Show()
       
   797             
       
   798             self.RefreshSizerLayout()
       
   799     
       
   800     def CollapsePanel(self, name):
       
   801         infos = self.Panels.get(name, None)
       
   802         if infos is not None and infos["expanded"]:
       
   803             infos["expanded"] = False
       
   804             infos["button"].SetToggle(False)
       
   805             infos["panel"].Hide()
       
   806             
       
   807             self.RefreshSizerLayout()
       
   808         
       
   809     def TogglePanel(self, name):
       
   810         infos = self.Panels.get(name, None)
       
   811         if infos is not None:
       
   812             infos["expanded"] = not infos["expanded"]
       
   813             infos["button"].SetToggle(infos["expanded"])
       
   814             if infos["expanded"]:
       
   815                 infos["panel"].Show()
       
   816             else:
       
   817                 infos["panel"].Hide()
       
   818             
       
   819             self.RefreshSizerLayout()
       
   820     
       
   821     def RefreshSizerLayout(self):
       
   822         self.MainSizer.Layout()
       
   823         self.RefreshCCodeEditorScrollbars()
       
   824     
       
   825     def RefreshCCodeEditorScrollbars(self):
       
   826         self.CCodeEditor.GetBestSize()
       
   827         xstart, ystart = self.CCodeEditor.GetViewStart()
       
   828         window_size = self.CCodeEditor.GetClientSize()
       
   829         maxx, maxy = self.MainSizer.GetMinSize()
       
   830         posx = max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT))
       
   831         posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT))
       
   832         self.CCodeEditor.Scroll(posx, posy)
       
   833         self.CCodeEditor.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, 
       
   834                 maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy)
       
   835     
       
   836     def OnCCodeEditorResize(self, event):
       
   837         self.RefreshCCodeEditorScrollbars()
       
   838         event.Skip()
       
   839