]>
Commit | Line | Data |
---|---|---|
09f3d4e6 RD |
1 | # Name: xrced.py |
2 | # Purpose: XRC editor, main module | |
3 | # Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be> | |
4 | # Created: 20.08.2001 | |
a40b5e84 | 5 | # RCS-ID: $Id$ |
09f3d4e6 RD |
6 | |
7 | from wxPython.wx import * | |
8 | from wxPython.xrc import * | |
9 | from wxPython.html import * | |
10 | import wxPython.lib.wxpTag | |
11 | from xml.dom import minidom | |
12 | import os | |
13 | import tempfile | |
14 | ||
15 | import images | |
16 | ||
17 | # String constants | |
a40b5e84 RD |
18 | |
19 | faceColour = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_3DFACE) | |
32ce09e2 RD |
20 | # Background colour problem on wxGTK |
21 | if wxGetOsVersion()[1] == 1: | |
22 | bgcolor = (faceColour.Red()-1, faceColour.Green()-1, faceColour.Blue()-1) | |
23 | else: | |
24 | bgcolor = (faceColour.Red(), faceColour.Green(), faceColour.Blue()) | |
a40b5e84 | 25 | htmlHeader = '<html><body bgcolor="#%02x%02x%02x">\n' % bgcolor |
09f3d4e6 RD |
26 | htmlFooter = '</body></html>\n' |
27 | progname = 'XRCed' | |
32ce09e2 | 28 | version = '0.0.6' |
09f3d4e6 RD |
29 | |
30 | # Local modules | |
31 | from xxx import * | |
32 | ||
33 | # Globals | |
34 | testWin = None | |
35 | testWinPos = wxDefaultPosition | |
36 | ||
37 | # 1 adds CMD command to Help menu | |
38 | debug = 1 | |
39 | ||
40 | if debug: | |
41 | import traceback | |
42 | import time | |
43 | ||
44 | # Set menu to list items. | |
45 | # Each menu command is a tuple (id, label, help) | |
46 | # submenus are lists [id, label, help, submenu] | |
47 | # and separators are any other type | |
48 | def SetMenu(m, list): | |
49 | for l in list: | |
50 | if type(l) == types.TupleType: | |
51 | apply(m.Append, l) | |
52 | elif type(l) == types.ListType: | |
53 | subMenu = wxMenu() | |
54 | SetMenu(subMenu, l[2:]) | |
55 | m.AppendMenu(wxNewId(), l[0], subMenu, l[1]) | |
56 | else: # separator | |
57 | m.AppendSeparator() | |
58 | ||
a40b5e84 RD |
59 | ################################################################################ |
60 | ||
61 | # Properties panel containing notebook | |
62 | class Panel(wxNotebook): | |
63 | def __init__(self, parent, id = -1): | |
64 | wxNotebook.__init__(self, parent, id, style=wxNB_BOTTOM) | |
a40b5e84 RD |
65 | sys.modules['params'].panel = self |
66 | self.page1 = HtmlPage(self) | |
67 | self.AddPage(self.page1, 'Properties') | |
68 | self.page2 = None | |
32ce09e2 RD |
69 | # Cache for already used panels |
70 | self.styleCache = {} | |
a40b5e84 | 71 | def SetData(self, xxx): |
32ce09e2 | 72 | # First page |
a40b5e84 RD |
73 | self.page1.SetPageData(xxx) |
74 | # Replace/remove style page | |
32ce09e2 | 75 | curPage = self.GetSelection() |
a40b5e84 | 76 | if self.page2: |
32ce09e2 RD |
77 | # If style page for a same class, don't need to recreate |
78 | if xxx and self.page2.cacheId == xxx.treeObject().__class__: | |
79 | self.page2.SetData(xxx.treeObject()) | |
80 | return | |
81 | # Else remove and then add if needed | |
a40b5e84 | 82 | self.RemovePage(1) |
32ce09e2 RD |
83 | if not self.styleCache.has_key(self.page2.cacheId): |
84 | self.styleCache[self.page2.cacheId] = self.page2 | |
85 | self.Freeze() | |
a40b5e84 | 86 | if xxx and xxx.treeObject().hasStyle: |
32ce09e2 RD |
87 | cacheId = xxx.treeObject().__class__ |
88 | if self.styleCache.has_key(cacheId): | |
89 | self.page2 = self.styleCache[cacheId] | |
90 | self.page2.SetData(xxx.treeObject()) | |
91 | else: | |
92 | self.page2 = StylePage(self, xxx.treeObject()) | |
a40b5e84 RD |
93 | self.AddPage(self.page2, 'Style') |
94 | else: | |
95 | self.page2 = None | |
32ce09e2 RD |
96 | # Keep page selected |
97 | if self.page2 and curPage == 1: | |
98 | self.SetSelection(curPage) | |
99 | self.Thaw() | |
09f3d4e6 | 100 | def Clear(self): |
a40b5e84 RD |
101 | self.page1.Clear() |
102 | if self.page2: | |
103 | self.RemovePage(1) | |
104 | self.page2.Destroy() | |
105 | self.page2 = None | |
106 | # If some parameter on some page has changed | |
107 | def IsModified(self): | |
108 | return self.page1.IsModified() or self.page2 and self.page2.IsModified() | |
109 | def SetModified(self, value): | |
110 | self.page1.SetModified(value) | |
111 | if self.page2: | |
112 | self.page2.SetModified(value) | |
113 | ||
114 | ################################################################################ | |
115 | ||
116 | # General class for notebook pages | |
117 | class ParamPage: | |
118 | def __init__(self): | |
119 | # Register event handlers | |
120 | for id in paramIDs.values(): | |
121 | EVT_CHECKBOX(self, id, self.OnCheckParams) | |
09f3d4e6 RD |
122 | def OnCheckParams(self, evt): |
123 | selected = tree.GetSelection() | |
124 | xxx = tree.GetPyData(selected) | |
a40b5e84 RD |
125 | winName = evt.GetEventObject().GetName() |
126 | sizerParam = false | |
127 | if winName[0] == '_': | |
128 | param = winName[7:] | |
129 | sizerParam = true | |
130 | else: | |
131 | if xxx.hasChild: xxx = xxx.child | |
132 | param = winName[6:] | |
09f3d4e6 | 133 | # Set current object |
a40b5e84 RD |
134 | if sizerParam: |
135 | w = self.FindWindowByName('_data_' + param) | |
09f3d4e6 | 136 | else: |
a40b5e84 | 137 | w = self.FindWindowByName('data_' + param) |
32ce09e2 | 138 | objElem = xxx.element |
09f3d4e6 RD |
139 | if evt.IsChecked(): |
140 | # Ad new text node in order of allParams | |
a40b5e84 | 141 | w.SetValue('') # set empty (default) value |
32ce09e2 RD |
142 | w.SetModified() # mark as changed |
143 | elem = tree.dom.createElement(param) | |
144 | xxx.params[param] = xxxParam(elem) | |
09f3d4e6 RD |
145 | # Find place to put new element: first present element after param |
146 | found = false | |
a40b5e84 RD |
147 | paramStyles = xxx.allParams + xxx.styles |
148 | for p in paramStyles[paramStyles.index(param) + 1:]: | |
09f3d4e6 RD |
149 | # Content params don't have same type |
150 | if xxx.params.has_key(p) and p != 'content': | |
151 | found = true | |
152 | break | |
153 | if found: | |
32ce09e2 RD |
154 | nextTextElem = xxx.params[p].node |
155 | objElem.insertBefore(elem, nextTextElem) | |
09f3d4e6 | 156 | else: |
32ce09e2 | 157 | objElem.appendChild(elem) |
09f3d4e6 | 158 | else: |
32ce09e2 RD |
159 | # Remove parameter |
160 | xxx.params[param].remove() | |
09f3d4e6 RD |
161 | del xxx.params[param] |
162 | w.SetValue('') | |
32ce09e2 RD |
163 | # Set modified flas |
164 | self.SetModified(true) | |
09f3d4e6 | 165 | w.Enable(evt.IsChecked()) |
09f3d4e6 | 166 | |
a40b5e84 RD |
167 | |
168 | ################################################################################ | |
169 | ||
170 | # Properties panel notebook page | |
171 | class HtmlPage(wxHtmlWindow, ParamPage): | |
172 | def __init__(self, parent, id = -1): | |
173 | wxHtmlWindow.__init__(self, parent, id) | |
174 | ParamPage.__init__(self) | |
175 | self.SetBorders(5) | |
176 | if wxGetOsVersion()[1] == 1: | |
177 | self.SetFonts('', '', [8, 10, 12, 14, 16, 19, 24]) | |
178 | else: | |
179 | self.SetFonts("", "", [7, 8, 10, 12, 16, 22, 30]) | |
180 | self.modified = false | |
32ce09e2 RD |
181 | # Previous type |
182 | self.xxxClass = self.xxxChildClass = None | |
a40b5e84 RD |
183 | def Clear(self): |
184 | self.SetPage(htmlHeader + 'select a tree item on the left' + htmlFooter) | |
185 | def SetPageData(self, xxx): | |
186 | if not xxx: | |
187 | self.SetPage(htmlHeader + 'this item has no properties' + htmlFooter) | |
188 | return | |
32ce09e2 RD |
189 | self.Freeze() # doesn't seem to help |
190 | # Don't change interface if same class | |
191 | compare = false | |
192 | if self.xxxClass and self.xxxClass == xxx.__class__: | |
193 | compare = true # a little weird code | |
194 | if xxx.hasChild: | |
195 | if self.xxxChildClass != xxx.child.__class__: | |
196 | compare = false | |
197 | if not compare: # not same | |
198 | self.SetPage(htmlHeader + xxx.generateHtml() + htmlFooter) | |
199 | self.xxxClass = xxx.__class__ | |
200 | if xxx.hasChild: self.xxxChildClass = xxx.child.__class__ | |
201 | self.SetValues(xxx) | |
202 | if xxx.hasChild: | |
203 | self.SetValues(xxx.child) | |
204 | self.Thaw() | |
205 | def SetValues(self, xxx): | |
a40b5e84 RD |
206 | # Set values, checkboxes to false, disable defaults |
207 | if xxx.hasChild: prefix = '_' | |
208 | else: prefix = '' | |
32ce09e2 RD |
209 | if xxx.hasName: |
210 | self.FindWindowByName('data_name').SetValue(xxx.name) | |
a40b5e84 | 211 | for param in xxx.allParams: |
32ce09e2 RD |
212 | try: |
213 | value = xxx.params[param].value() | |
a40b5e84 RD |
214 | self.FindWindowByName(prefix + 'data_' + param).SetValue(value) |
215 | if not param in xxx.required: | |
216 | self.FindWindowByName(prefix + 'check_' + param).SetValue(true) | |
32ce09e2 | 217 | except KeyError: |
a40b5e84 | 218 | self.FindWindowByName(prefix + 'data_' + param).Enable(false) |
a40b5e84 | 219 | # If some parameter has changed |
09f3d4e6 RD |
220 | def IsModified(self): |
221 | return self.modified | |
222 | def SetModified(self, value): | |
223 | self.modified = value | |
224 | ||
a40b5e84 RD |
225 | ################################################################################ |
226 | ||
227 | # Style notebook page | |
228 | class StylePage(wxPanel, ParamPage): | |
229 | def __init__(self, parent, xxx): | |
230 | wxPanel.__init__(self, parent, -1) | |
231 | ParamPage.__init__(self) | |
32ce09e2 | 232 | self.cacheId = xxx.__class__ |
a40b5e84 RD |
233 | if wxGetOsVersion()[1] == 1: |
234 | self.SetFont(wxFont(12, wxDEFAULT, wxNORMAL, wxNORMAL)) | |
235 | else: | |
236 | self.SetFont(wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL)) | |
237 | topSizer = wxBoxSizer(wxVERTICAL) | |
32ce09e2 | 238 | sizer = wxFlexGridSizer(len(xxx.styles), 2, 1, 1) |
a40b5e84 RD |
239 | self.controls = {} # save python objects |
240 | for param in xxx.styles: | |
241 | present = param in xxx.params.keys() | |
242 | check = wxCheckBox(self, paramIDs[param], | |
243 | param + ':', name = 'check_' + param) | |
244 | check.SetValue(present) | |
32ce09e2 | 245 | control = paramDict[param](self, name = 'data_' + param) |
a40b5e84 | 246 | if present: |
32ce09e2 | 247 | control.SetValue(xxx.params[param].value()) |
a40b5e84 RD |
248 | else: |
249 | control.SetValue('') | |
250 | control.Enable(present) | |
32ce09e2 RD |
251 | sizer.AddMany([ (check, 0, wxALIGN_CENTER_VERTICAL), |
252 | (control, 0, wxALIGN_CENTER_VERTICAL) ]) | |
a40b5e84 RD |
253 | self.controls[param] = control |
254 | topSizer.Add(sizer, 1, wxALL, 5) | |
255 | self.SetAutoLayout(true) | |
256 | self.SetSizer(topSizer) | |
257 | ||
258 | self.modified = false | |
259 | ||
32ce09e2 RD |
260 | # Set data for a cahced page |
261 | def SetData(self, xxx): | |
262 | for param in xxx.styles: | |
263 | present = param in xxx.params.keys() | |
264 | check = self.FindWindowByName('check_' + param) | |
265 | check.SetValue(present) | |
266 | control = self.controls[param] | |
267 | if present: | |
268 | control.SetValue(xxx.params[param].value()) | |
269 | else: | |
270 | control.SetValue('') | |
271 | control.Enable(present) | |
272 | self.modified = false | |
273 | ||
a40b5e84 RD |
274 | # If some parameter has changed |
275 | def IsModified(self): | |
276 | return self.modified | |
277 | def SetModified(self, value): | |
278 | self.modified = value | |
279 | ||
280 | ################################################################################ | |
281 | ||
09f3d4e6 RD |
282 | class HightLightBox: |
283 | def __init__(self, pos, size): | |
284 | w = testWin.panel | |
285 | l1 = wxWindow(w, -1, pos, wxSize(size.x, 2)) | |
286 | l1.SetBackgroundColour(wxRED) | |
287 | l2 = wxWindow(w, -1, pos, wxSize(2, size.y)) | |
288 | l2.SetBackgroundColour(wxRED) | |
289 | l3 = wxWindow(w, -1, wxPoint(pos.x + size.x - 2, pos.y), wxSize(2, size.y)) | |
290 | l3.SetBackgroundColour(wxRED) | |
291 | l4 = wxWindow(w, -1, wxPoint(pos.x, pos.y + size.y - 2), wxSize(size.x, 2)) | |
292 | l4.SetBackgroundColour(wxRED) | |
293 | self.lines = [l1, l2, l3, l4] | |
294 | # Move highlight to a new position | |
295 | def Replace(self, pos, size): | |
296 | self.lines[0].SetDimensions(pos.x, pos.y, size.x, 2, wxSIZE_ALLOW_MINUS_ONE) | |
297 | self.lines[1].SetDimensions(pos.x, pos.y, 2, size.y, wxSIZE_ALLOW_MINUS_ONE) | |
298 | self.lines[2].SetDimensions(pos.x + size.x - 2, pos.y, 2, size.y, | |
299 | wxSIZE_ALLOW_MINUS_ONE) | |
300 | self.lines[3].SetDimensions(pos.x, pos.y + size.y - 2, size.x, 2, | |
301 | wxSIZE_ALLOW_MINUS_ONE) | |
302 | # Remove it | |
303 | def Remove(self): | |
304 | map(wxWindow.Destroy, self.lines) | |
305 | testWin.highLight = None | |
306 | ||
a40b5e84 RD |
307 | ################################################################################ |
308 | ||
09f3d4e6 RD |
309 | class MemoryFile: |
310 | def __init__(self, name): | |
311 | self.name = name | |
312 | self.buffer = '' | |
313 | def write(self, data): | |
314 | self.buffer = self.buffer + data.encode() | |
315 | def close(self): | |
316 | f = open(self.name, 'w') | |
317 | f.write(self.buffer) | |
318 | f.close() | |
319 | # !!! memory FS will work someday | |
320 | #self.file = wxMemoryFSHandler_AddFile(self.name, self.buffer) | |
321 | ||
322 | class XML_Tree(wxTreeCtrl): | |
323 | def __init__(self, parent, id): | |
a40b5e84 RD |
324 | wxTreeCtrl.__init__(self, parent, id, |
325 | style=wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT) | |
09f3d4e6 RD |
326 | self.SetBackgroundColour(wxColour(224, 248, 224)) |
327 | EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) | |
32ce09e2 | 328 | # One works on Linux, another on Windows |
09f3d4e6 | 329 | EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) |
32ce09e2 | 330 | EVT_LEFT_DCLICK(self, self.OnDClick) |
09f3d4e6 RD |
331 | EVT_RIGHT_DOWN(self, self.OnRightDown) |
332 | self.needUpdate = false | |
333 | self.pendingHighLight = None | |
334 | self.ctrl = false | |
335 | self.dom = None | |
336 | # Create image list | |
337 | il = wxImageList(16, 16, true) | |
a40b5e84 RD |
338 | self.rootImage = il.AddIcon(wxIconFromXPMData(images.getTreeRootData())) |
339 | xxxObject.image = il.AddIcon(wxIconFromXPMData(images.getTreeDefaultData())) | |
340 | xxxPanel.image = il.AddIcon(wxIconFromXPMData(images.getTreePanelData())) | |
341 | xxxDialog.image = il.AddIcon(wxIconFromXPMData(images.getTreeDialogData())) | |
342 | xxxFrame.image = il.AddIcon(wxIconFromXPMData(images.getTreeFrameData())) | |
343 | xxxMenuBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuBarData())) | |
32ce09e2 | 344 | xxxToolBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeToolBarData())) |
a40b5e84 RD |
345 | xxxMenu.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuData())) |
346 | xxxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeSizerHData())) | |
347 | xxxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeSizerVData())) | |
348 | xxxStaticBoxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerHData())) | |
349 | xxxStaticBoxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerVData())) | |
350 | xxxGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerGridData())) | |
351 | xxxFlexGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerFlexGridData())) | |
09f3d4e6 RD |
352 | self.il = il |
353 | self.SetImageList(il) | |
354 | ||
355 | # !!! temporary solution for GetOldItem problem | |
356 | def Unselect(self): | |
357 | self.selection = wxTreeItemId() | |
358 | wxTreeCtrl.Unselect(self) | |
359 | def GetSelection(self): | |
360 | return self.selection | |
a40b5e84 | 361 | |
09f3d4e6 RD |
362 | def ExpandAll(self, item): |
363 | if self.ItemHasChildren(item): | |
364 | self.Expand(item) | |
365 | i, cookie = self.GetFirstChild(item, 0) | |
366 | children = [] | |
367 | while i.IsOk(): | |
368 | children.append(i) | |
369 | i, cookie = self.GetNextChild(item, cookie) | |
370 | for i in children: | |
371 | self.ExpandAll(i) | |
372 | ||
373 | def SetData(self, dom): | |
374 | self.dom = dom | |
375 | # Find 'resource' child, add it's children | |
376 | self.mainNode = dom.getElementsByTagName('resource')[0] | |
377 | nodes = self.mainNode.childNodes[:] | |
378 | for node in nodes: | |
379 | if IsObject(node): | |
380 | self.AddNode(self.GetRootItem(), None, node) | |
381 | else: | |
382 | self.mainNode.removeChild(node) | |
383 | node.unlink() | |
384 | self.Unselect() | |
385 | ||
386 | # Add tree item for given parent item if node is DOM element node with | |
387 | # 'object' tag. xxxParent is parent xxx object | |
388 | def AddNode(self, itemParent, xxxParent, node): | |
389 | # Set item data to current node | |
32ce09e2 RD |
390 | try: |
391 | xxx = MakeXXXFromDOM(xxxParent, node) | |
392 | except: | |
393 | return | |
09f3d4e6 RD |
394 | treeObj = xxx.treeObject() |
395 | # Append tree item | |
396 | item = self.AppendItem(itemParent, treeObj.treeName(), | |
397 | image=treeObj.treeImage(), | |
398 | data=wxTreeItemData(xxx)) | |
399 | # Try to find children objects | |
400 | if treeObj.hasChildren: | |
401 | nodes = treeObj.element.childNodes[:] | |
402 | for n in nodes: | |
403 | if IsObject(n): | |
404 | self.AddNode(item, treeObj, n) | |
405 | elif n.nodeType != minidom.Node.ELEMENT_NODE: | |
406 | treeObj.element.removeChild(n) | |
407 | n.unlink() | |
a40b5e84 | 408 | |
09f3d4e6 RD |
409 | |
410 | # Remove leaf of tree, return it's data object | |
411 | def RemoveLeaf(self, leaf): | |
412 | xxx = self.GetPyData(leaf) | |
413 | node = xxx.element | |
414 | parent = node.parentNode | |
415 | parent.removeChild(node) | |
416 | self.Delete(leaf) | |
417 | # Update view? | |
418 | #if testWin and self.GetItemAncestor(leaf) == testWin.item: | |
419 | # if testWin.highLight: | |
420 | # testWin.highLight.Remove() | |
421 | # self.needUpdate = true | |
422 | return node | |
423 | ||
424 | # Find position relative to the top-level window | |
425 | def FindNodePos(self, item): | |
426 | itemParent = self.GetItemParent(item) | |
427 | if itemParent == self.GetRootItem(): return wxPoint(0, 0) | |
428 | obj = self.FindNodeObject(item) | |
429 | # Find first ancestor which is a wxWindow (not a sizer) | |
430 | winParent = itemParent | |
431 | while self.GetPyData(winParent).isSizer: | |
432 | winParent = self.GetItemParent(winParent) | |
433 | parentPos = self.FindNodePos(winParent) | |
434 | return parentPos + obj.GetPosition() | |
435 | ||
436 | # Find window (or sizer) corresponding to a tree item. | |
437 | def FindNodeObject(self, item): | |
438 | itemParent = self.GetItemParent(item) | |
439 | # If top-level, return testWin (or panel if wxFrame) | |
440 | if itemParent == self.GetRootItem(): return testWin.panel | |
441 | xxx = self.GetPyData(item).treeObject() | |
442 | parentWin = self.FindNodeObject(itemParent) | |
443 | # Top-level sizer? return window's sizer | |
444 | if xxx.isSizer and isinstance(parentWin, wxWindowPtr): | |
445 | return parentWin.GetSizer() | |
446 | # Otherwise get parent's object and it's child | |
447 | n = 0 # index of sibling | |
448 | prev = self.GetPrevSibling(item) | |
449 | while prev.IsOk(): | |
450 | prev = self.GetPrevSibling(prev) | |
451 | n += 1 | |
452 | child = parentWin.GetChildren()[n] | |
453 | # Return window or sizer for sizer items | |
454 | if child.GetClassName() == 'wxSizerItem': | |
455 | if child.IsWindow(): child = child.GetWindow() | |
456 | elif child.IsSizer(): | |
457 | child = child.GetSizer() | |
458 | # Test for notebook sizers | |
459 | if isinstance(child, wxNotebookSizerPtr): | |
460 | child = child.GetNotebook() | |
461 | return child | |
462 | ||
463 | def OnSelChanged(self, evt): | |
464 | # Apply changes | |
465 | # !!! problem with wxGTK | |
466 | #oldItem = evt.GetOldItem() | |
467 | oldItem = self.selection | |
468 | if oldItem.IsOk(): | |
469 | xxx = self.GetPyData(oldItem) | |
470 | # If some data was modified, apply changes | |
471 | if xxx: | |
472 | if panel.IsModified(): | |
473 | self.Apply(xxx, oldItem) | |
474 | #if conf.autoRefresh: | |
475 | if testWin and testWin.highLight: | |
476 | testWin.highLight.Remove() | |
477 | self.needUpdate = true | |
478 | # Generate HTML view | |
479 | item = evt.GetItem() | |
480 | self.selection = item # !!! fix | |
481 | xxx = self.GetPyData(item) | |
a40b5e84 RD |
482 | # Update panel |
483 | panel.SetData(xxx) | |
484 | # Remove highlight? | |
485 | if not xxx and testWin and testWin.highLight: | |
486 | testWin.highLight.Remove() | |
09f3d4e6 | 487 | return |
09f3d4e6 RD |
488 | # Clear flag |
489 | panel.SetModified(false) | |
490 | # Hightlighting is done in OnIdle | |
491 | tree.pendingHighLight = item | |
492 | ||
493 | # Find top-level parent | |
494 | def GetItemAncestor(self, item): | |
495 | while self.GetItemParent(item) != self.GetRootItem(): | |
496 | item = self.GetItemParent(item) | |
497 | return item | |
498 | ||
499 | # Highlight selected item | |
500 | def HighLight(self, item): | |
501 | self.pendingHighLight = None | |
502 | if not testWin or self.GetPyData(testWin.item).className \ | |
503 | not in ['wxDialog', 'wxPanel', 'wxFrame']: | |
504 | return | |
505 | # Top-level does not have highlight | |
32ce09e2 | 506 | if item == testWin.item or item == tree.GetRootItem(): |
09f3d4e6 RD |
507 | if testWin.highLight: testWin.highLight.Remove() |
508 | return | |
509 | # If a control from another window is selected, remove highlight | |
510 | if self.GetItemAncestor(item) != testWin.item and testWin.highLight: | |
511 | testWin.highLight.Remove() | |
512 | return | |
513 | # Get window/sizer object | |
514 | obj, pos = self.FindNodeObject(item), self.FindNodePos(item) | |
515 | size = obj.GetSize() | |
516 | # For notebook, select item's page. | |
517 | # For children of page, nothing happens (too much work) | |
518 | if isinstance(self.GetPyData(item).parent, xxxNotebook): | |
519 | notebook = self.FindNodeObject(self.GetItemParent(item)) | |
520 | # Find position | |
521 | n = 0 | |
522 | prev = self.GetPrevSibling(item) | |
523 | while prev.IsOk(): | |
524 | n += 1 | |
525 | prev = self.GetPrevSibling(prev) | |
526 | notebook.SetSelection(n) | |
527 | # Highlight | |
528 | try: # finally I use exceptions | |
529 | testWin.highLight.Replace(pos, size) | |
530 | except AttributeError: | |
531 | testWin.highLight = HightLightBox(pos, size) | |
532 | testWin.highLight.item = item | |
a40b5e84 | 533 | |
32ce09e2 | 534 | # Double-click on Linux |
09f3d4e6 RD |
535 | def OnItemActivated(self, evt): |
536 | item = evt.GetItem() | |
537 | xxx = self.GetPyData(item) | |
538 | if not xxx: return # if root selected, do nothing | |
539 | if panel.IsModified(): | |
540 | self.Apply(xxx, item) # apply changes | |
541 | self.CreateTestWin(item) | |
a40b5e84 | 542 | |
32ce09e2 RD |
543 | # Double-click on Windows |
544 | def OnDClick(self, evt): | |
545 | id, flags = self.HitTest(evt.GetPosition()) | |
546 | if flags in [wxTREE_HITTEST_ONITEMBUTTON, wxTREE_HITTEST_ONITEMLABEL]: | |
547 | # !!! can't create a wxTreeItemId from int | |
548 | item = self.selection # assume item already selected | |
549 | xxx = self.GetPyData(item) | |
550 | if not xxx: return # if root selected, do nothing | |
551 | if panel.IsModified(): | |
552 | self.Apply(xxx, item) # apply changes | |
553 | self.CreateTestWin(item) | |
554 | else: | |
555 | evt.Skip() | |
556 | ||
09f3d4e6 RD |
557 | # (re)create test window |
558 | def CreateTestWin(self, node): | |
559 | global testWin | |
560 | # Create a window with this resource | |
561 | xxx = self.GetPyData(node).treeObject() | |
562 | if not xxx: return # if root selected, do nothing | |
563 | # If noname element, display error | |
564 | if not xxx.hasName or not xxx.name: | |
565 | wxLogError("Can't display a noname element") | |
566 | return | |
567 | # Close old window, remember where it was | |
568 | highLight = None | |
569 | if testWin: | |
570 | pos = testWin.GetPosition() | |
571 | if node == testWin.item: | |
572 | # Remember highlight if same top-level window | |
573 | if testWin.highLight: | |
574 | highLight = testWin.highLight.item | |
575 | # !!! if 0 is removed, refresh is broken (notebook not deleted?) | |
576 | if 0 and xxx.className == 'wxPanel': | |
577 | if testWin.highLight: | |
578 | testWin.pendingHighLight = highLight | |
579 | testWin.highLight.Remove() | |
580 | testWin.panel.Destroy() | |
581 | testWin.panel = None | |
582 | else: | |
583 | testWin.Destroy() | |
584 | testWin = None | |
585 | else: | |
586 | testWin.Destroy() | |
587 | testWin = None | |
588 | else: | |
589 | pos = testWinPos | |
590 | # Save in temporary file before activating | |
591 | memFile = MemoryFile(tempfile.mktemp('xrc')) | |
592 | #memFile = MemoryFile('core.xrc') # to write debug file | |
593 | # Create partial XML file - faster for big files | |
594 | ||
595 | dom = minidom.Document() | |
596 | mainNode = dom.createElement('resource') | |
597 | dom.appendChild(mainNode) | |
598 | ||
599 | # Remove temporarily from old parent | |
600 | elem = xxx.element | |
601 | parent = elem.parentNode | |
602 | next = elem.nextSibling | |
603 | parent.replaceChild(self.dummyNode, elem) | |
604 | # Append to new DOM, write it | |
605 | mainNode.appendChild(elem) | |
606 | dom.writexml(memFile) | |
607 | # Put back in place | |
608 | mainNode.removeChild(elem) | |
609 | dom.unlink() | |
610 | parent.replaceChild(elem, self.dummyNode) | |
611 | ||
612 | memFile.close() # write to wxMemoryFS | |
613 | res = wxXmlResource('') | |
614 | res.Load(memFile.name) | |
615 | if xxx.className == 'wxFrame': | |
616 | # Create new frame | |
617 | testWin = wxPreFrame() | |
618 | res.LoadFrame(testWin, frame, xxx.name) | |
619 | testWin.panel = testWin | |
620 | testWin.SetPosition(pos) | |
621 | testWin.Show(true) | |
622 | elif xxx.className == 'wxPanel': | |
623 | # Create new frame | |
624 | if not testWin: | |
625 | testWin = wxFrame(frame, -1, 'Panel: ' + xxx.name, pos=pos) | |
626 | testWin.panel = res.LoadPanel(testWin, xxx.name) | |
627 | testWin.SetSize(testWin.panel.GetSize()) | |
628 | testWin.Show(true) | |
629 | elif xxx.className == 'wxDialog': | |
630 | # Create new frame | |
631 | testWin = res.LoadDialog(None, xxx.name) | |
632 | testWin.panel = testWin | |
633 | testWin.SetPosition(pos) | |
634 | testWin.Show(true) | |
635 | elif xxx.className == 'wxMenuBar': | |
636 | testWin = wxFrame(frame, -1, 'MenuBar: ' + xxx.name, pos=pos) | |
637 | # Set status bar to display help | |
638 | testWin.CreateStatusBar() | |
639 | testWin.menuBar = res.LoadMenuBar(xxx.name) | |
640 | testWin.SetMenuBar(testWin.menuBar) | |
641 | testWin.Show(true) | |
642 | else: | |
643 | wxLogMessage('No view for this element yet') | |
644 | return | |
645 | os.unlink(memFile.name) # remove tmp file | |
646 | testWin.item = node | |
647 | testWin.Connect(testWin.GetId(), -1, wxEVT_CLOSE_WINDOW, self.OnCloseTestWin) | |
648 | testWin.highLight = None | |
649 | if highLight and not tree.pendingHighLight: | |
650 | self.HighLight(highLight) | |
a40b5e84 | 651 | |
09f3d4e6 RD |
652 | def OnCloseTestWin(self, evt): |
653 | global testWin, testWinPos | |
654 | testWinPos = testWin.GetPosition() | |
655 | testWin.Destroy() | |
656 | testWin = None | |
657 | evt.Skip() | |
658 | ||
659 | # True if next item should be inserted after current (vs. appended to it) | |
660 | def NeedInsert(self, item): | |
661 | xxx = self.GetPyData(item) | |
662 | if not xxx: return false # root item | |
663 | if self.ctrl: return true # if Ctrl pressed, always insert | |
664 | if xxx.hasChildren and not self.ItemHasChildren(item): | |
665 | return false | |
666 | return not (self.IsExpanded(item) and self.ItemHasChildren(item)) | |
32ce09e2 | 667 | |
09f3d4e6 RD |
668 | # Pull-down |
669 | def OnRightDown(self, evt): | |
670 | # Setup menu | |
a40b5e84 RD |
671 | menu = wxMenu() |
672 | ||
09f3d4e6 | 673 | item = self.GetSelection() |
a40b5e84 RD |
674 | if not item.IsOk(): |
675 | menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand tree') | |
09f3d4e6 RD |
676 | else: |
677 | self.ctrl = evt.ControlDown() # save Ctrl state | |
32ce09e2 RD |
678 | self.shift = evt.ShiftDown() # and Shift too |
679 | m = wxMenu() # create menu | |
680 | needInsert = false | |
09f3d4e6 RD |
681 | if item != self.GetRootItem(): needInsert = self.NeedInsert(item) |
682 | if item == self.GetRootItem() or \ | |
683 | self.GetItemParent(item) == self.GetRootItem() and needInsert: | |
684 | m.Append(pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel') | |
685 | m.Append(pullDownMenu.ID_NEW_DIALOG, 'Dialog', 'Create dialog') | |
686 | m.Append(pullDownMenu.ID_NEW_FRAME, 'Frame', 'Create frame') | |
687 | m.AppendSeparator() | |
32ce09e2 RD |
688 | m.Append(pullDownMenu.ID_NEW_TOOL_BAR, 'ToolBar', 'Create toolbar') |
689 | m.Append(pullDownMenu.ID_NEW_MENU_BAR, 'MenuBar', 'Create menubar') | |
09f3d4e6 RD |
690 | m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu') |
691 | else: | |
692 | xxx = self.GetPyData(item) | |
693 | if xxx.__class__ == xxxMenuBar: | |
694 | m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu') | |
32ce09e2 RD |
695 | elif xxx.__class__ in [xxxToolBar, xxxTool]: |
696 | SetMenu(m, pullDownMenu.toolBarControls) | |
09f3d4e6 RD |
697 | elif xxx.__class__ in [xxxMenu, xxxMenuItem]: |
698 | SetMenu(m, pullDownMenu.menuControls) | |
699 | else: | |
700 | SetMenu(m, pullDownMenu.controls) | |
701 | if not (xxx.isSizer or \ | |
702 | xxx.parent and xxx.parent.isSizer): | |
703 | m.Enable(pullDownMenu.ID_NEW_SPACER, false) | |
704 | # Select correct label for create menu | |
32ce09e2 RD |
705 | if not needInsert: |
706 | if self.shift: | |
707 | menu.AppendMenu(wxNewId(), 'Insert Child', m, | |
708 | 'Create child object as the first child') | |
709 | else: | |
710 | menu.AppendMenu(wxNewId(), 'Append Child', m, | |
711 | 'Create child object as the last child') | |
09f3d4e6 | 712 | else: |
32ce09e2 RD |
713 | if self.shift: |
714 | menu.AppendMenu(wxNewId(), 'Create Sibling', m, | |
715 | 'Create sibling before selected object') | |
09f3d4e6 | 716 | else: |
a40b5e84 | 717 | menu.AppendMenu(wxNewId(), 'Create Sibling', m, |
32ce09e2 | 718 | 'Create sibling after selected object') |
a40b5e84 RD |
719 | menu.AppendSeparator() |
720 | menu.Append(wxID_CUT, 'Cut', 'Cut to the clipboard') | |
721 | menu.Append(wxID_COPY, 'Copy', 'Copy to the clipboard') | |
722 | menu.Append(wxID_PASTE, 'Paste', 'Paste from the clipboard') | |
723 | menu.Append(pullDownMenu.ID_DELETE, | |
724 | 'Delete', 'Delete object') | |
09f3d4e6 | 725 | if item.IsOk() and self.ItemHasChildren(item): |
a40b5e84 RD |
726 | menu.AppendSeparator() |
727 | menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand subtree') | |
728 | self.PopupMenu(menu, evt.GetPosition()) | |
729 | menu.Destroy() | |
09f3d4e6 RD |
730 | |
731 | # Clear tree | |
732 | def Clear(self): | |
733 | self.DeleteAllItems() | |
734 | # Add minimal structure | |
a40b5e84 | 735 | root = self.AddRoot('XML tree', self.rootImage) |
09f3d4e6 RD |
736 | self.Unselect() |
737 | if self.dom: self.dom.unlink() | |
738 | self.dom = minidom.Document() | |
739 | self.dummyNode = self.dom.createComment('dummy node') | |
740 | # Create main node | |
741 | self.mainNode = self.dom.createElement('resource') | |
742 | self.dom.appendChild(self.mainNode) | |
a40b5e84 | 743 | |
09f3d4e6 RD |
744 | # Apply changes |
745 | def Apply(self, xxx, item): | |
746 | if not xxx: return | |
747 | # !!! Save undo info | |
748 | if xxx.undo: xxx.undo.unlink() | |
749 | xxx.undo = xxx.element.cloneNode(false) | |
750 | if xxx.hasName: | |
a40b5e84 | 751 | name = panel.page1.FindWindowByName('data_name').GetValue() |
09f3d4e6 RD |
752 | if xxx.name != name: |
753 | xxx.name = name | |
754 | xxx.element.setAttribute('name', name) | |
755 | self.SetItemText(item, xxx.treeName()) | |
756 | if xxx.hasChild: prefix = '_' | |
757 | else: prefix = '' | |
32ce09e2 RD |
758 | for param, paramObj in xxx.params.items(): |
759 | paramWin = panel.FindWindowByName(prefix + 'data_' + param) | |
760 | if not paramWin.modified: continue | |
761 | value = paramWin.GetValue() | |
762 | if param in xxx.specials: | |
763 | xxx.setSpecial(param, value) | |
09f3d4e6 | 764 | else: |
32ce09e2 | 765 | paramObj.update(value) |
09f3d4e6 RD |
766 | if xxx.hasChild: |
767 | self.Apply(xxx.child, item) | |
768 | else: | |
769 | # Change tree icon for sizers | |
770 | if isinstance(xxx, xxxBoxSizer): | |
771 | self.SetItemImage(item, xxx.treeImage()) | |
772 | # Set global modified state | |
773 | frame.modified = true | |
774 | ||
a40b5e84 RD |
775 | class PullDownMenu: |
776 | ID_NEW_PANEL = wxNewId() | |
777 | ID_NEW_DIALOG = wxNewId() | |
778 | ID_NEW_FRAME = wxNewId() | |
32ce09e2 RD |
779 | ID_NEW_TOOL_BAR = wxNewId() |
780 | ID_NEW_TOOL = wxNewId() | |
a40b5e84 RD |
781 | ID_NEW_MENU_BAR = wxNewId() |
782 | ID_NEW_MENU = wxNewId() | |
783 | ||
784 | ID_NEW_STATIC_TEXT = wxNewId() | |
785 | ID_NEW_TEXT_CTRL = wxNewId() | |
786 | ||
787 | ID_NEW_BUTTON = wxNewId() | |
788 | ID_NEW_BITMAP_BUTTON = wxNewId() | |
789 | ID_NEW_RADIO_BUTTON = wxNewId() | |
790 | ID_NEW_SPIN_BUTTON = wxNewId() | |
791 | ||
792 | ID_NEW_STATIC_BOX = wxNewId() | |
793 | ID_NEW_CHECK_BOX = wxNewId() | |
794 | ID_NEW_RADIO_BOX = wxNewId() | |
795 | ID_NEW_COMBO_BOX = wxNewId() | |
796 | ID_NEW_LIST_BOX = wxNewId() | |
797 | ||
798 | ID_NEW_STATIC_LINE = wxNewId() | |
32ce09e2 | 799 | ID_NEW_STATIC_BITMAP = wxNewId() |
a40b5e84 RD |
800 | ID_NEW_CHOICE = wxNewId() |
801 | ID_NEW_SLIDER = wxNewId() | |
802 | ID_NEW_GAUGE = wxNewId() | |
803 | ID_NEW_SCROLL_BAR = wxNewId() | |
804 | ID_NEW_TREE_CTRL = wxNewId() | |
805 | ID_NEW_LIST_CTRL = wxNewId() | |
806 | ID_NEW_CHECK_LIST = wxNewId() | |
807 | ID_NEW_NOTEBOOK = wxNewId() | |
808 | ID_NEW_HTML_WINDOW = wxNewId() | |
809 | ID_NEW_CALENDAR = wxNewId() | |
810 | ||
811 | ID_NEW_BOX_SIZER = wxNewId() | |
812 | ID_NEW_STATIC_BOX_SIZER = wxNewId() | |
813 | ID_NEW_GRID_SIZER = wxNewId() | |
814 | ID_NEW_FLEX_GRID_SIZER = wxNewId() | |
815 | ID_NEW_SPACER = wxNewId() | |
32ce09e2 RD |
816 | ID_NEW_TOOL_BAR = wxNewId() |
817 | ID_NEW_TOOL = wxNewId() | |
a40b5e84 RD |
818 | ID_NEW_MENU = wxNewId() |
819 | ID_NEW_MENU_ITEM = wxNewId() | |
820 | ID_NEW_SEPARATOR = wxNewId() | |
821 | ID_NEW_LAST = wxNewId() | |
822 | ID_EXPAND = wxNewId() | |
823 | ||
824 | def __init__(self, parent): | |
825 | self.ID_DELETE = parent.ID_DELETE | |
826 | EVT_MENU_RANGE(parent, self.ID_NEW_PANEL, | |
827 | self.ID_NEW_LAST, parent.OnCreate) | |
828 | EVT_MENU(parent, self.ID_EXPAND, parent.OnExpand) | |
829 | # We connect to tree, but process in frame | |
830 | EVT_MENU_HIGHLIGHT_ALL(tree, parent.OnPullDownHighlight) | |
831 | ||
32ce09e2 RD |
832 | ################################################################################ |
833 | ||
09f3d4e6 RD |
834 | class Frame(wxFrame): |
835 | def __init__(self, size): | |
836 | wxFrame.__init__(self, None, -1, '', size=size) | |
837 | self.CreateStatusBar() | |
a40b5e84 | 838 | self.SetIcon(wxIconFromXPMData(images.getIconData())) |
09f3d4e6 RD |
839 | |
840 | # Make menus | |
841 | menuBar = wxMenuBar() | |
842 | ||
843 | menu = wxMenu() | |
844 | menu.Append(wxID_NEW, '&New\tCtrl-N', 'New file') | |
845 | menu.Append(wxID_OPEN, '&Open...\tCtrl-O', 'Open XRC file') | |
846 | menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file') | |
847 | menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name') | |
848 | menu.AppendSeparator() | |
849 | menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application') | |
850 | menuBar.Append(menu, '&File') | |
a40b5e84 | 851 | |
09f3d4e6 RD |
852 | menu = wxMenu() |
853 | menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo') | |
854 | menu.Append(wxID_REDO, '&Redo\tCtrl-R', 'Redo') | |
855 | menu.AppendSeparator() | |
856 | menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard') | |
857 | menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard') | |
858 | menu.Append(wxID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard') | |
859 | self.ID_DELETE = wxNewId() | |
860 | menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object') | |
861 | menuBar.Append(menu, '&Edit') | |
a40b5e84 | 862 | |
09f3d4e6 RD |
863 | menu = wxMenu() |
864 | self.ID_REFRESH = wxNewId() | |
865 | menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh view') | |
866 | self.ID_AUTO_REFRESH = wxNewId() | |
867 | menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A', | |
868 | 'Toggle auto-refresh mode', true) | |
869 | menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) | |
870 | menuBar.Append(menu, '&View') | |
a40b5e84 | 871 | |
09f3d4e6 RD |
872 | menu = wxMenu() |
873 | menu.Append(wxID_ABOUT, 'About...', 'About XCRed') | |
874 | if debug: | |
875 | self.ID_DEBUG_CMD = wxNewId() | |
876 | menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line') | |
877 | EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD) | |
878 | menuBar.Append(menu, '&Help') | |
879 | ||
880 | self.menuBar = menuBar | |
881 | self.SetMenuBar(menuBar) | |
882 | ||
883 | # Create toolbar | |
a40b5e84 RD |
884 | tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT) |
885 | tb.SetToolBitmapSize((24, 23)) | |
09f3d4e6 RD |
886 | tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file') |
887 | tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file') | |
888 | tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file') | |
889 | tb.AddSeparator() | |
890 | tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut') | |
891 | tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy') | |
892 | tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste') | |
893 | tb.AddSeparator() | |
894 | tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(), | |
895 | 'Refresh', 'Refresh view') | |
896 | tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(), | |
897 | 'Auto-refresh', 'Toggle auto-refresh mode', true) | |
898 | tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) | |
09f3d4e6 | 899 | tb.Realize() |
a40b5e84 | 900 | self.tb = tb |
09f3d4e6 RD |
901 | |
902 | # File | |
903 | EVT_MENU(self, wxID_NEW, self.OnNew) | |
904 | EVT_MENU(self, wxID_OPEN, self.OnOpen) | |
905 | EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs) | |
906 | EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs) | |
907 | EVT_MENU(self, wxID_EXIT, self.OnExit) | |
908 | # Edit | |
909 | EVT_MENU(self, wxID_UNDO, self.OnUndo) | |
910 | EVT_MENU(self, wxID_REDO, self.OnRedo) | |
911 | EVT_MENU(self, wxID_CUT, self.OnCut) | |
912 | EVT_MENU(self, wxID_COPY, self.OnCopy) | |
913 | EVT_MENU(self, wxID_PASTE, self.OnPaste) | |
914 | EVT_MENU(self, self.ID_DELETE, self.OnDelete) | |
915 | # View | |
916 | EVT_MENU(self, self.ID_REFRESH, self.OnRefresh) | |
917 | EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh) | |
918 | # Help | |
919 | EVT_MENU(self, wxID_ABOUT, self.OnAbout) | |
920 | ||
921 | # Update events | |
922 | EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI) | |
923 | EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI) | |
924 | EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI) | |
925 | EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI) | |
926 | ||
927 | # Build interface | |
a40b5e84 RD |
928 | sizer = wxBoxSizer(wxVERTICAL) |
929 | sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND) | |
930 | splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH) | |
32ce09e2 | 931 | self.splitter = splitter |
a40b5e84 | 932 | splitter.SetMinimumPaneSize(100) |
09f3d4e6 RD |
933 | # Create tree |
934 | global tree | |
935 | tree = XML_Tree(splitter, -1) | |
936 | sys.modules['xxx'].tree = tree | |
937 | # Create panel for parameters | |
938 | global panel | |
a40b5e84 | 939 | panel = Panel(splitter) |
09f3d4e6 | 940 | # Set plitter windows |
32ce09e2 | 941 | splitter.SplitVertically(tree, panel, conf.sashPos) |
a40b5e84 RD |
942 | sizer.Add(splitter, 1, wxEXPAND) |
943 | self.SetAutoLayout(true) | |
944 | self.SetSizer(sizer) | |
09f3d4e6 RD |
945 | |
946 | # Init pull-down menu data | |
09f3d4e6 | 947 | global pullDownMenu |
a40b5e84 | 948 | pullDownMenu = PullDownMenu(self) |
09f3d4e6 RD |
949 | # Mapping from IDs to element names |
950 | self.createMap = { | |
951 | pullDownMenu.ID_NEW_PANEL: 'wxPanel', | |
952 | pullDownMenu.ID_NEW_DIALOG: 'wxDialog', | |
953 | pullDownMenu.ID_NEW_FRAME: 'wxFrame', | |
32ce09e2 RD |
954 | pullDownMenu.ID_NEW_TOOL_BAR: 'wxToolBar', |
955 | pullDownMenu.ID_NEW_TOOL: 'tool', | |
09f3d4e6 RD |
956 | pullDownMenu.ID_NEW_MENU_BAR: 'wxMenuBar', |
957 | pullDownMenu.ID_NEW_MENU: 'wxMenu', | |
32ce09e2 RD |
958 | pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem', |
959 | pullDownMenu.ID_NEW_SEPARATOR: 'separator', | |
09f3d4e6 RD |
960 | |
961 | pullDownMenu.ID_NEW_STATIC_TEXT: 'wxStaticText', | |
962 | pullDownMenu.ID_NEW_TEXT_CTRL: 'wxTextCtrl', | |
963 | ||
964 | pullDownMenu.ID_NEW_BUTTON: 'wxButton', | |
965 | pullDownMenu.ID_NEW_BITMAP_BUTTON: 'wxBitmapButton', | |
966 | pullDownMenu.ID_NEW_RADIO_BUTTON: 'wxRadioButton', | |
967 | pullDownMenu.ID_NEW_SPIN_BUTTON: 'wxSpinButton', | |
a40b5e84 | 968 | |
09f3d4e6 RD |
969 | pullDownMenu.ID_NEW_STATIC_BOX: 'wxStaticBox', |
970 | pullDownMenu.ID_NEW_CHECK_BOX: 'wxCheckBox', | |
971 | pullDownMenu.ID_NEW_RADIO_BOX: 'wxRadioBox', | |
972 | pullDownMenu.ID_NEW_COMBO_BOX: 'wxComboBox', | |
973 | pullDownMenu.ID_NEW_LIST_BOX: 'wxListBox', | |
a40b5e84 | 974 | |
09f3d4e6 | 975 | pullDownMenu.ID_NEW_STATIC_LINE: 'wxStaticLine', |
32ce09e2 | 976 | pullDownMenu.ID_NEW_STATIC_BITMAP: 'wxStaticBitmap', |
09f3d4e6 RD |
977 | pullDownMenu.ID_NEW_CHOICE: 'wxChoice', |
978 | pullDownMenu.ID_NEW_SLIDER: 'wxSlider', | |
979 | pullDownMenu.ID_NEW_GAUGE: 'wxGauge', | |
980 | pullDownMenu.ID_NEW_SCROLL_BAR: 'wxScrollBar', | |
981 | pullDownMenu.ID_NEW_TREE_CTRL: 'wxTreeCtrl', | |
982 | pullDownMenu.ID_NEW_LIST_CTRL: 'wxListCtrl', | |
983 | pullDownMenu.ID_NEW_CHECK_LIST: 'wxCheckList', | |
984 | pullDownMenu.ID_NEW_NOTEBOOK: 'wxNotebook', | |
985 | pullDownMenu.ID_NEW_HTML_WINDOW: 'wxHtmlWindow', | |
986 | pullDownMenu.ID_NEW_CALENDAR: 'wxCalendar', | |
a40b5e84 | 987 | |
09f3d4e6 RD |
988 | pullDownMenu.ID_NEW_BOX_SIZER: 'wxBoxSizer', |
989 | pullDownMenu.ID_NEW_STATIC_BOX_SIZER: 'wxStaticBoxSizer', | |
990 | pullDownMenu.ID_NEW_GRID_SIZER: 'wxGridSizer', | |
991 | pullDownMenu.ID_NEW_FLEX_GRID_SIZER: 'wxFlexGridSizer', | |
992 | pullDownMenu.ID_NEW_SPACER: 'spacer', | |
09f3d4e6 RD |
993 | } |
994 | pullDownMenu.controls = [ | |
995 | ['control', 'Various controls', | |
996 | (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'), | |
997 | (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'), | |
998 | (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'), | |
999 | (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'), | |
1000 | (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'), | |
1001 | (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'), | |
1002 | (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'), | |
1003 | (pullDownMenu.ID_NEW_TREE_CTRL, 'TreeCtrl', 'Create tree control'), | |
1004 | (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'), | |
1005 | (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckList', 'Create check list control'), | |
1006 | (pullDownMenu.ID_NEW_HTML_WINDOW, 'HtmlWindow', 'Create HTML window'), | |
1007 | (pullDownMenu.ID_NEW_CALENDAR, 'Calendar', 'Create calendar control'), | |
1008 | (pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel'), | |
1009 | (pullDownMenu.ID_NEW_NOTEBOOK, 'Notebook', 'Create notebook control'), | |
1010 | ], | |
1011 | ['button', 'Buttons', | |
1012 | (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'), | |
1013 | (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'), | |
1014 | (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'), | |
1015 | (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'), | |
1016 | ], | |
1017 | ['box', 'Boxes', | |
1018 | (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'), | |
1019 | (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'), | |
1020 | (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'), | |
1021 | (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'), | |
1022 | (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'), | |
1023 | ], | |
1024 | ['sizer', 'Sizers', | |
1025 | (pullDownMenu.ID_NEW_BOX_SIZER, 'BoxSizer', 'Create box sizer'), | |
1026 | (pullDownMenu.ID_NEW_STATIC_BOX_SIZER, 'StaticBoxSizer', | |
1027 | 'Create static box sizer'), | |
1028 | (pullDownMenu.ID_NEW_GRID_SIZER, 'GridSizer', 'Create grid sizer'), | |
1029 | (pullDownMenu.ID_NEW_FLEX_GRID_SIZER, 'FlexGridSizer', | |
1030 | 'Create flexgrid sizer'), | |
1031 | (pullDownMenu.ID_NEW_SPACER, 'Spacer', 'Create spacer'), | |
1032 | ] | |
1033 | ] | |
1034 | pullDownMenu.menuControls = [ | |
1035 | (pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu'), | |
1036 | (pullDownMenu.ID_NEW_MENU_ITEM, 'MenuItem', 'Create menu item'), | |
1037 | (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'), | |
1038 | ] | |
32ce09e2 RD |
1039 | pullDownMenu.toolBarControls = [ |
1040 | (pullDownMenu.ID_NEW_TOOL, 'Tool', 'Create tool'), | |
1041 | (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'), | |
1042 | ] | |
a40b5e84 | 1043 | |
09f3d4e6 RD |
1044 | # Initialize |
1045 | self.Clear() | |
1046 | ||
1047 | # Other events | |
1048 | EVT_IDLE(self, self.OnIdle) | |
1049 | EVT_CLOSE(self, self.OnCloseWindow) | |
1050 | ||
1051 | def OnNew(self, evt): | |
1052 | self.Clear() | |
1053 | ||
1054 | def OnOpen(self, evt): | |
1055 | if not self.AskSave(): return | |
1056 | dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile), | |
1057 | '', '*.xrc', wxOPEN | wxCHANGE_DIR) | |
1058 | if dlg.ShowModal() == wxID_OK: | |
1059 | path = dlg.GetPath() | |
1060 | self.SetStatusText('Loading...') | |
1061 | wxYield() | |
1062 | wxBeginBusyCursor() | |
1063 | self.Open(path) | |
1064 | wxEndBusyCursor() | |
1065 | self.SetStatusText('Ready') | |
a40b5e84 | 1066 | dlg.Destroy() |
09f3d4e6 RD |
1067 | |
1068 | def OnSaveOrSaveAs(self, evt): | |
1069 | if evt.GetId() == wxID_SAVEAS or not self.dataFile: | |
1070 | if self.dataFile: defaultName = '' | |
1071 | else: defaultName = 'UNTITLED.xrc' | |
1072 | dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile), | |
1073 | defaultName, '*.xrc', | |
1074 | wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR) | |
a40b5e84 RD |
1075 | if dlg.ShowModal() == wxID_OK: |
1076 | path = dlg.GetPath() | |
1077 | dlg.Destroy() | |
1078 | else: | |
1079 | dlg.Destroy() | |
1080 | return | |
09f3d4e6 RD |
1081 | else: |
1082 | path = self.dataFile | |
1083 | self.SetStatusText('Saving...') | |
1084 | wxYield() | |
1085 | wxBeginBusyCursor() | |
1086 | self.Save(path) | |
1087 | self.dataFile = path | |
1088 | wxEndBusyCursor() | |
1089 | self.SetStatusText('Ready') | |
1090 | ||
1091 | def OnExit(self, evt): | |
1092 | self.Close() | |
1093 | ||
1094 | def OnUndo(self, evt): | |
1095 | print '*** being implemented' | |
1096 | print self.lastOp, self.undo | |
1097 | if self.lastOp == 'DELETE': | |
1098 | parent, prev, elem = self.undo | |
1099 | if prev.IsOk(): | |
1100 | xxx = MakeXXXFromDOM(tree.GetPyData(parent).treeObject(), elem) | |
1101 | item = tree.InsertItem( parent, prev, xxx.treeObject().className, | |
1102 | data=wxTreeItemData(xxx) ) | |
a40b5e84 | 1103 | |
09f3d4e6 RD |
1104 | def OnRedo(self, evt): |
1105 | print '*** being implemented' | |
a40b5e84 | 1106 | |
09f3d4e6 RD |
1107 | def OnCut(self, evt): |
1108 | selected = tree.GetSelection() | |
1109 | # Undo info | |
1110 | self.lastOp = 'CUT' | |
1111 | self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] | |
1112 | # Delete testWin? | |
1113 | global testWin | |
1114 | if testWin: | |
1115 | # If deleting top-level item, delete testWin | |
1116 | if selected == testWin.item: | |
1117 | testWin.Destroy() | |
1118 | testWin = None | |
1119 | else: | |
1120 | # Remove highlight, update testWin | |
1121 | if tree.GetItemAncestor(selected) == testWin.item: | |
1122 | if testWin.highLight: testWin.highLight.Remove() | |
1123 | tree.needUpdate = true | |
1124 | self.clipboard = tree.RemoveLeaf(selected) | |
1125 | tree.pendingHighLight = None | |
1126 | tree.Unselect() | |
1127 | panel.Clear() | |
1128 | self.modified = true | |
1129 | ||
1130 | def OnCopy(self, evt): | |
1131 | selected = tree.GetSelection() | |
1132 | xxx = tree.GetPyData(selected) | |
1133 | self.clipboard = xxx.element.cloneNode(true) | |
1134 | ||
1135 | def OnPaste(self, evt): | |
1136 | selected = tree.GetSelection() | |
1137 | appendChild = not tree.NeedInsert(selected) | |
1138 | xxx = tree.GetPyData(selected) | |
1139 | if not appendChild: | |
1140 | # If has next item, insert, else append to parent | |
1141 | nextItem = tree.GetNextSibling(selected) | |
1142 | if nextItem.IsOk(): | |
1143 | # Insert before nextItem | |
1144 | parentLeaf = tree.GetItemParent(selected) | |
1145 | else: # last child: change selected to parent | |
1146 | appendChild = true | |
1147 | selected = tree.GetItemParent(selected) | |
1148 | # Expanded container (must have children) | |
a40b5e84 | 1149 | elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected): |
09f3d4e6 RD |
1150 | appendChild = false |
1151 | nextItem = tree.GetFirstChild(selected, 0)[0] | |
1152 | parentLeaf = selected | |
1153 | # Parent should be tree element or None | |
1154 | if appendChild: | |
1155 | parent = tree.GetPyData(selected) | |
1156 | else: | |
1157 | parent = tree.GetPyData(parentLeaf) | |
1158 | if parent and parent.hasChild: parent = parent.child | |
1159 | ||
1160 | # Create a copy of clipboard element | |
1161 | elem = self.clipboard.cloneNode(true) | |
1162 | # Tempopary xxx object to test things | |
1163 | xxx = MakeXXXFromDOM(parent, elem) | |
1164 | className = xxx.treeObject().className | |
1165 | # Check parent and child relationships | |
1166 | # Parent is sizer or notebook, child is of wrong class or | |
1167 | # parent is normal window, child is child container: detach child | |
1168 | isChildContainer = isinstance(xxx, xxxChildContainer) | |
1169 | if not parent and isChildContainer or \ | |
1170 | (parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ | |
1171 | (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \ | |
1172 | (not parent.isSizer and not isinstance(parent, xxxNotebook) and \ | |
1173 | isChildContainer): | |
1174 | if isChildContainer: | |
1175 | elem.removeChild(xxx.child.element) # detach child | |
1176 | elem.unlink() # delete child container | |
1177 | elem = xxx.child.element # replace | |
1178 | # This should help garbage collection (!!! maybe not needed?) | |
1179 | xxx.child.parent = None | |
1180 | xxx.child = None | |
1181 | if parent: | |
1182 | # Parent is sizer or notebook, child is not child container | |
1183 | if parent.isSizer and not isChildContainer and \ | |
1184 | not isinstance(xxx, xxxSpacer): | |
1185 | # Create sizer item element | |
1186 | sizerItemElem = MakeEmptyDOM('sizeritem') | |
1187 | sizerItemElem.appendChild(elem) | |
1188 | elem = sizerItemElem | |
1189 | elif isinstance(parent, xxxNotebook) and not isChildContainer: | |
1190 | pageElem = MakeEmptyDOM('notebookpage') | |
1191 | pageElem.appendChild(elem) | |
1192 | elem = pageElem | |
1193 | xxx = MakeXXXFromDOM(parent, elem) | |
1194 | # Figure out if we must append a new child or sibling | |
1195 | if appendChild: | |
1196 | if parent: node = parent.element | |
1197 | else: node = tree.mainNode | |
1198 | node.appendChild(elem) | |
1199 | newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), | |
1200 | data=wxTreeItemData(xxx)) | |
1201 | else: | |
1202 | node = tree.GetPyData(nextItem).element | |
1203 | if parent: | |
1204 | elemParent = parent.element | |
1205 | else: | |
1206 | elemParent = tree.mainNode | |
1207 | elemParent.insertBefore(elem, node) | |
1208 | # Inserting before is difficult, se we insert after or first child | |
1209 | newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(), | |
1210 | image=xxx.treeImage(), data=wxTreeItemData(xxx)) | |
1211 | # Add children items | |
1212 | if xxx.hasChildren: | |
1213 | treeObj = xxx.treeObject() | |
1214 | for n in treeObj.element.childNodes: | |
1215 | if IsObject(n): | |
1216 | tree.AddNode(newItem, treeObj, n) | |
1217 | # Scroll to show new item | |
1218 | tree.EnsureVisible(newItem) | |
1219 | tree.SelectItem(newItem) | |
1220 | if not tree.IsVisible(newItem): | |
1221 | tree.ScrollTo(newItem) | |
1222 | tree.Refresh() | |
1223 | # Update view? | |
1224 | if testWin and tree.GetItemAncestor(newItem) == testWin.item: | |
1225 | if conf.autoRefresh: | |
1226 | tree.needUpdate = true | |
1227 | tree.pendingHighLight = newItem | |
1228 | else: | |
1229 | tree.pendingHighLight = None | |
1230 | self.modified = true | |
1231 | ||
1232 | def OnDelete(self, evt): | |
1233 | selected = tree.GetSelection() | |
1234 | # Undo info | |
1235 | self.lastOp = 'DELETE' | |
1236 | self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] | |
1237 | # Delete testWin? | |
1238 | global testWin | |
1239 | if testWin: | |
1240 | # If deleting top-level item, delete testWin | |
1241 | if selected == testWin.item: | |
1242 | testWin.Destroy() | |
1243 | testWin = None | |
1244 | else: | |
1245 | # Remove highlight, update testWin | |
1246 | if tree.GetItemAncestor(selected) == testWin.item: | |
1247 | if testWin.highLight: testWin.highLight.Remove() | |
1248 | tree.needUpdate = true | |
1249 | xnode = tree.RemoveLeaf(selected) | |
1250 | self.undo.append(xnode.cloneNode(true)) | |
1251 | xnode.unlink() | |
1252 | tree.pendingHighLight = None | |
1253 | tree.Unselect() | |
1254 | panel.Clear() | |
1255 | self.modified = true | |
1256 | ||
1257 | def OnRefresh(self, evt): | |
1258 | # If modified, apply first | |
1259 | selection = tree.GetSelection() | |
1260 | if selection.IsOk(): | |
1261 | xxx = tree.GetPyData(selection) | |
1262 | if xxx and panel.IsModified(): | |
1263 | tree.Apply(xxx, selection) | |
1264 | if testWin: | |
1265 | # (re)create | |
1266 | tree.CreateTestWin(testWin.item) | |
1267 | tree.needUpdate = false | |
1268 | ||
1269 | def OnAutoRefresh(self, evt): | |
1270 | conf.autoRefresh = evt.IsChecked() | |
1271 | self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) | |
1272 | self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) | |
1273 | ||
1274 | def OnAbout(self, evt): | |
32ce09e2 RD |
1275 | str = '%s %s\n\nRoman Rolinsky <rolinsky@mema.ucl.ac.be>' % \ |
1276 | (progname, version) | |
1277 | dlg = wxMessageDialog(self, str, 'About ' + progname, wxOK | wxCENTRE) | |
1278 | dlg.ShowModal() | |
1279 | dlg.Destroy() | |
09f3d4e6 RD |
1280 | |
1281 | # Simple emulation of python command line | |
1282 | def OnDebugCMD(self, evt): | |
1283 | while 1: | |
1284 | try: | |
1285 | exec raw_input('C:\> ') | |
1286 | except EOFError: | |
1287 | print '^D' | |
1288 | break | |
1289 | except: | |
1290 | (etype, value, tb) =sys.exc_info() | |
1291 | tblist =traceback.extract_tb(tb)[1:] | |
1292 | msg =string.join(traceback.format_exception_only(etype, value) | |
1293 | +traceback.format_list(tblist)) | |
1294 | print msg | |
1295 | ||
1296 | def OnCreate(self, evt): | |
1297 | selected = tree.GetSelection() | |
1298 | appendChild = not tree.NeedInsert(selected) | |
1299 | xxx = tree.GetPyData(selected) | |
1300 | if not appendChild: | |
1301 | # If has next item, insert, else append to parent | |
1302 | nextItem = tree.GetNextSibling(selected) | |
1303 | if nextItem.IsOk(): | |
1304 | # Insert before nextItem | |
1305 | parentLeaf = tree.GetItemParent(selected) | |
1306 | else: # last child: change selected to parent | |
1307 | appendChild = true | |
1308 | selected = tree.GetItemParent(selected) | |
1309 | # Expanded container (must have children) | |
a40b5e84 | 1310 | elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected): |
09f3d4e6 RD |
1311 | appendChild = false |
1312 | nextItem = tree.GetFirstChild(selected, 0)[0] | |
1313 | parentLeaf = selected | |
1314 | # Parent should be tree element or None | |
1315 | if appendChild: | |
1316 | parent = tree.GetPyData(selected) | |
1317 | else: | |
1318 | parent = tree.GetPyData(parentLeaf) | |
1319 | if parent and parent.hasChild: parent = parent.child | |
1320 | ||
1321 | # Create element | |
1322 | className = self.createMap[evt.GetId()] | |
1323 | xxx = MakeEmptyXXX(parent, className) | |
1324 | # Figure out if we must append a new child or sibling | |
1325 | elem = xxx.element | |
1326 | if appendChild: | |
1327 | if parent: node = parent.element | |
1328 | else: node = tree.mainNode | |
1329 | # Insert newline for debug purposes | |
1330 | node.appendChild(tree.dom.createTextNode('\n')) | |
1331 | node.appendChild(elem) | |
1332 | newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), | |
1333 | data=wxTreeItemData(xxx)) | |
1334 | else: | |
1335 | node = tree.GetPyData(nextItem).element | |
1336 | if parent: | |
1337 | elemParent = parent.element | |
1338 | else: | |
1339 | elemParent = tree.mainNode | |
1340 | elemParent.insertBefore(tree.dom.createTextNode('\n'), node) | |
1341 | elemParent.insertBefore(elem, node) | |
1342 | # Inserting before is difficult, se we insert after or first child | |
1343 | newItem = tree.InsertItem(parentLeaf, selected, | |
1344 | xxx.treeName(), image=xxx.treeImage(), | |
1345 | data=wxTreeItemData(xxx)) | |
1346 | tree.EnsureVisible(newItem) | |
1347 | tree.SelectItem(newItem) | |
1348 | if not tree.IsVisible(newItem): | |
1349 | tree.ScrollTo(newItem) | |
1350 | tree.Refresh() | |
1351 | # Update view? | |
1352 | if testWin and tree.GetItemAncestor(newItem) == testWin.item: | |
1353 | if conf.autoRefresh: | |
1354 | tree.needUpdate = true | |
1355 | tree.pendingHighLight = newItem | |
1356 | else: | |
1357 | tree.pendingHighLight = None | |
1358 | ||
1359 | def OnExpand(self, evt): | |
1360 | if tree.GetSelection().IsOk(): | |
1361 | tree.ExpandAll(tree.GetSelection()) | |
1362 | else: | |
1363 | tree.ExpandAll(tree.GetRootItem()) | |
1364 | ||
1365 | def OnPullDownHighlight(self, evt): | |
1366 | menuId = evt.GetMenuId() | |
a40b5e84 RD |
1367 | if menuId != -1: |
1368 | menu = evt.GetEventObject() | |
1369 | help = menu.GetHelpString(menuId) | |
1370 | self.SetStatusText(help) | |
1371 | else: | |
1372 | self.SetStatusText('') | |
09f3d4e6 RD |
1373 | |
1374 | def OnUpdateUI(self, evt): | |
1375 | if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]: | |
1376 | enable = tree.GetSelection().IsOk() and \ | |
1377 | tree.GetSelection() != tree.GetRootItem() | |
1378 | evt.Enable(enable) | |
1379 | elif evt.GetId() == wxID_PASTE: | |
32ce09e2 RD |
1380 | enable = tree.GetSelection().IsOk() and \ |
1381 | tree.GetSelection() != tree.GetRootItem() and \ | |
1382 | self.clipboard != None | |
09f3d4e6 RD |
1383 | evt.Enable(enable) |
1384 | ||
1385 | def OnIdle(self, evt): | |
1386 | if tree.needUpdate: | |
1387 | if conf.autoRefresh: | |
1388 | if testWin: | |
1389 | # (re)create | |
1390 | tree.CreateTestWin(testWin.item) | |
1391 | tree.needUpdate = false | |
1392 | elif tree.pendingHighLight: | |
1393 | tree.HighLight(tree.pendingHighLight) | |
32ce09e2 RD |
1394 | else: |
1395 | evt.Skip() | |
09f3d4e6 RD |
1396 | |
1397 | def OnCloseWindow(self, evt): | |
1398 | if not self.AskSave(): return | |
1399 | if testWin: testWin.Destroy() | |
32ce09e2 RD |
1400 | # Destroy cached windows |
1401 | for w in panel.styleCache.values(): w.Destroy() | |
09f3d4e6 | 1402 | conf.width, conf.height = self.GetSize() |
32ce09e2 | 1403 | conf.sashPos = self.splitter.GetSashPosition() |
09f3d4e6 RD |
1404 | evt.Skip() |
1405 | ||
1406 | def Clear(self): | |
1407 | self.dataFile = '' | |
1408 | self.clipboard = None | |
1409 | self.modified = false | |
1410 | panel.SetModified(false) | |
1411 | panel.Clear() | |
1412 | tree.Clear() | |
1413 | global testWin | |
1414 | if testWin: | |
1415 | testWin.Destroy() | |
1416 | testWin = None | |
1417 | self.SetTitle(progname) | |
1418 | ||
1419 | def Open(self, path): | |
1420 | # Try to read the file | |
1421 | try: | |
1422 | open(path) | |
1423 | self.Clear() | |
1424 | # Build wx tree | |
1425 | dom = minidom.parse(path) | |
1426 | tree.SetData(dom) | |
1427 | self.dataFile = path | |
1428 | self.SetTitle(progname + ': ' + os.path.basename(path)) | |
1429 | except: | |
1430 | wxLogError('Error reading file: ' + path) | |
a40b5e84 | 1431 | raise |
09f3d4e6 RD |
1432 | |
1433 | def Save(self, path): | |
1434 | try: | |
a40b5e84 | 1435 | self.OnRefresh(wxCommandEvent()) |
09f3d4e6 RD |
1436 | memFile = MemoryFile(path) |
1437 | tree.dom.writexml(memFile) | |
1438 | memFile.close() | |
1439 | self.modified = false | |
1440 | panel.SetModified(false) | |
1441 | except: | |
1442 | wxLogError('Error writing file: ' + path) | |
a40b5e84 | 1443 | raise |
09f3d4e6 RD |
1444 | |
1445 | def AskSave(self): | |
1446 | if not (self.modified or panel.IsModified()): return true | |
1447 | flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE | |
32ce09e2 RD |
1448 | dlg = wxMessageDialog( self, 'File is modified. Save before exit?', |
1449 | 'Save before too late?', flags ) | |
1450 | say = dlg.ShowModal() | |
1451 | dlg.Destroy() | |
09f3d4e6 RD |
1452 | if say == wxID_YES: |
1453 | self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE)) | |
1454 | # If save was successful, modified flag is unset | |
1455 | if not self.modified: return true | |
1456 | elif say == wxID_NO: | |
1457 | self.modified = false | |
1458 | panel.SetModified(false) | |
1459 | return true | |
1460 | return false | |
1461 | ||
a40b5e84 RD |
1462 | ################################################################################ |
1463 | ||
09f3d4e6 RD |
1464 | class App(wxApp): |
1465 | def OnInit(self): | |
1466 | self.SetAppName("xrced") | |
1467 | # Settings | |
1468 | global conf | |
1469 | # !!! wxConfigBase_Get doesn't seem to work | |
1470 | conf = wxConfig(style=wxCONFIG_USE_LOCAL_FILE) | |
1471 | conf.autoRefresh = conf.ReadInt('autorefresh', true) | |
1472 | size = conf.ReadInt('width', 800), conf.ReadInt('height', 600) | |
32ce09e2 | 1473 | conf.sashPos = conf.ReadInt('sashPos', 200) |
09f3d4e6 RD |
1474 | # Add handlers |
1475 | wxFileSystem_AddHandler(wxMemoryFSHandler()) | |
1476 | wxInitAllImageHandlers() | |
1477 | # Create main frame | |
1478 | global frame | |
1479 | frame = self.frame = Frame(size) | |
1480 | self.frame.Show(true) | |
32ce09e2 | 1481 | # Load resources from XRC file (!!! should be transformed to .py later?) |
09f3d4e6 RD |
1482 | sys.modules['params'].frame = frame |
1483 | frame.res = wxXmlResource('') | |
1484 | frame.res.Load(os.path.join(sys.path[0], 'xrced.xrc')) | |
1485 | return true | |
1486 | ||
1487 | def OnExit(self): | |
1488 | # Write config | |
1489 | wc = wxConfigBase_Get() | |
1490 | wc.WriteInt('autorefresh', conf.autoRefresh) | |
1491 | wc.WriteInt('width', conf.width) | |
1492 | wc.WriteInt('height', conf.height) | |
32ce09e2 | 1493 | wc.WriteInt('sashPos', conf.sashPos) |
09f3d4e6 RD |
1494 | wc.Flush() |
1495 | ||
1496 | def main(): | |
1497 | app = App() | |
1498 | app.MainLoop() | |
1499 | app.OnExit() | |
1500 | ||
1501 | if __name__ == '__main__': | |
1502 | main() |