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 |
|