]> git.saurik.com Git - wxWidgets.git/blob - wxPython/tools/XRCed/xrced.py
removed superfluous assignment handled in base class.
[wxWidgets.git] / wxPython / tools / XRCed / xrced.py
1 # Name: xrced.py
2 # Purpose: XRC editor, main module
3 # Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be>
4 # Created: 20.08.2001
5 # RCS-ID: $Id$
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
18
19 faceColour = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_3DFACE)
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())
25 htmlHeader = '<html><body bgcolor="#%02x%02x%02x">\n' % bgcolor
26 htmlFooter = '</body></html>\n'
27 progname = 'XRCed'
28 version = '0.0.6'
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
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)
65 sys.modules['params'].panel = self
66 self.page1 = HtmlPage(self)
67 self.AddPage(self.page1, 'Properties')
68 self.page2 = None
69 # Cache for already used panels
70 self.styleCache = {}
71 def SetData(self, xxx):
72 # First page
73 self.page1.SetPageData(xxx)
74 # Replace/remove style page
75 curPage = self.GetSelection()
76 if self.page2:
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
82 self.RemovePage(1)
83 if not self.styleCache.has_key(self.page2.cacheId):
84 self.styleCache[self.page2.cacheId] = self.page2
85 self.Freeze()
86 if xxx and xxx.treeObject().hasStyle:
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())
93 self.AddPage(self.page2, 'Style')
94 else:
95 self.page2 = None
96 # Keep page selected
97 if self.page2 and curPage == 1:
98 self.SetSelection(curPage)
99 self.Thaw()
100 def Clear(self):
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)
122 def OnCheckParams(self, evt):
123 selected = tree.GetSelection()
124 xxx = tree.GetPyData(selected)
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:]
133 # Set current object
134 if sizerParam:
135 w = self.FindWindowByName('_data_' + param)
136 else:
137 w = self.FindWindowByName('data_' + param)
138 objElem = xxx.element
139 if evt.IsChecked():
140 # Ad new text node in order of allParams
141 w.SetValue('') # set empty (default) value
142 w.SetModified() # mark as changed
143 elem = tree.dom.createElement(param)
144 xxx.params[param] = xxxParam(elem)
145 # Find place to put new element: first present element after param
146 found = false
147 paramStyles = xxx.allParams + xxx.styles
148 for p in paramStyles[paramStyles.index(param) + 1:]:
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:
154 nextTextElem = xxx.params[p].node
155 objElem.insertBefore(elem, nextTextElem)
156 else:
157 objElem.appendChild(elem)
158 else:
159 # Remove parameter
160 xxx.params[param].remove()
161 del xxx.params[param]
162 w.SetValue('')
163 # Set modified flas
164 self.SetModified(true)
165 w.Enable(evt.IsChecked())
166
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
181 # Previous type
182 self.xxxClass = self.xxxChildClass = None
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
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):
206 # Set values, checkboxes to false, disable defaults
207 if xxx.hasChild: prefix = '_'
208 else: prefix = ''
209 if xxx.hasName:
210 self.FindWindowByName('data_name').SetValue(xxx.name)
211 for param in xxx.allParams:
212 try:
213 value = xxx.params[param].value()
214 self.FindWindowByName(prefix + 'data_' + param).SetValue(value)
215 if not param in xxx.required:
216 self.FindWindowByName(prefix + 'check_' + param).SetValue(true)
217 except KeyError:
218 self.FindWindowByName(prefix + 'data_' + param).Enable(false)
219 # If some parameter has changed
220 def IsModified(self):
221 return self.modified
222 def SetModified(self, value):
223 self.modified = value
224
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)
232 self.cacheId = xxx.__class__
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)
238 sizer = wxFlexGridSizer(len(xxx.styles), 2, 1, 1)
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)
245 control = paramDict[param](self, name = 'data_' + param)
246 if present:
247 control.SetValue(xxx.params[param].value())
248 else:
249 control.SetValue('')
250 control.Enable(present)
251 sizer.AddMany([ (check, 0, wxALIGN_CENTER_VERTICAL),
252 (control, 0, wxALIGN_CENTER_VERTICAL) ])
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
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
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
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
307 ################################################################################
308
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):
324 wxTreeCtrl.__init__(self, parent, id,
325 style=wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT)
326 self.SetBackgroundColour(wxColour(224, 248, 224))
327 EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
328 # One works on Linux, another on Windows
329 EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated)
330 EVT_LEFT_DCLICK(self, self.OnDClick)
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)
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()))
344 xxxToolBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeToolBarData()))
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()))
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
361
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
390 try:
391 xxx = MakeXXXFromDOM(xxxParent, node)
392 except:
393 return
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()
408
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)
482 # Update panel
483 panel.SetData(xxx)
484 # Remove highlight?
485 if not xxx and testWin and testWin.highLight:
486 testWin.highLight.Remove()
487 return
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
506 if item == testWin.item or item == tree.GetRootItem():
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
533
534 # Double-click on Linux
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)
542
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
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)
651
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))
667
668 # Pull-down
669 def OnRightDown(self, evt):
670 # Setup menu
671 menu = wxMenu()
672
673 item = self.GetSelection()
674 if not item.IsOk():
675 menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand tree')
676 else:
677 self.ctrl = evt.ControlDown() # save Ctrl state
678 self.shift = evt.ShiftDown() # and Shift too
679 m = wxMenu() # create menu
680 needInsert = false
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()
688 m.Append(pullDownMenu.ID_NEW_TOOL_BAR, 'ToolBar', 'Create toolbar')
689 m.Append(pullDownMenu.ID_NEW_MENU_BAR, 'MenuBar', 'Create menubar')
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')
695 elif xxx.__class__ in [xxxToolBar, xxxTool]:
696 SetMenu(m, pullDownMenu.toolBarControls)
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
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')
712 else:
713 if self.shift:
714 menu.AppendMenu(wxNewId(), 'Create Sibling', m,
715 'Create sibling before selected object')
716 else:
717 menu.AppendMenu(wxNewId(), 'Create Sibling', m,
718 'Create sibling after selected object')
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')
725 if item.IsOk() and self.ItemHasChildren(item):
726 menu.AppendSeparator()
727 menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand subtree')
728 self.PopupMenu(menu, evt.GetPosition())
729 menu.Destroy()
730
731 # Clear tree
732 def Clear(self):
733 self.DeleteAllItems()
734 # Add minimal structure
735 root = self.AddRoot('XML tree', self.rootImage)
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)
743
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:
751 name = panel.page1.FindWindowByName('data_name').GetValue()
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 = ''
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)
764 else:
765 paramObj.update(value)
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
775 class PullDownMenu:
776 ID_NEW_PANEL = wxNewId()
777 ID_NEW_DIALOG = wxNewId()
778 ID_NEW_FRAME = wxNewId()
779 ID_NEW_TOOL_BAR = wxNewId()
780 ID_NEW_TOOL = wxNewId()
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()
799 ID_NEW_STATIC_BITMAP = wxNewId()
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()
816 ID_NEW_TOOL_BAR = wxNewId()
817 ID_NEW_TOOL = wxNewId()
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
832 ################################################################################
833
834 class Frame(wxFrame):
835 def __init__(self, size):
836 wxFrame.__init__(self, None, -1, '', size=size)
837 self.CreateStatusBar()
838 #icon = wxIconFromXPMData(images.getIconData())
839 icon = wxIcon(os.path.join(sys.path[0], "xrced.ico"), wxBITMAP_TYPE_ICO)
840 self.SetIcon(icon)
841
842 # Make menus
843 menuBar = wxMenuBar()
844
845 menu = wxMenu()
846 menu.Append(wxID_NEW, '&New\tCtrl-N', 'New file')
847 menu.Append(wxID_OPEN, '&Open...\tCtrl-O', 'Open XRC file')
848 menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file')
849 menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name')
850 menu.AppendSeparator()
851 menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application')
852 menuBar.Append(menu, '&File')
853
854 menu = wxMenu()
855 menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo')
856 menu.Append(wxID_REDO, '&Redo\tCtrl-R', 'Redo')
857 menu.AppendSeparator()
858 menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard')
859 menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard')
860 menu.Append(wxID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard')
861 self.ID_DELETE = wxNewId()
862 menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object')
863 menuBar.Append(menu, '&Edit')
864
865 menu = wxMenu()
866 self.ID_REFRESH = wxNewId()
867 menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh view')
868 self.ID_AUTO_REFRESH = wxNewId()
869 menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A',
870 'Toggle auto-refresh mode', true)
871 menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
872 menuBar.Append(menu, '&View')
873
874 menu = wxMenu()
875 menu.Append(wxID_ABOUT, 'About...', 'About XCRed')
876 if debug:
877 self.ID_DEBUG_CMD = wxNewId()
878 menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line')
879 EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD)
880 menuBar.Append(menu, '&Help')
881
882 self.menuBar = menuBar
883 self.SetMenuBar(menuBar)
884
885 # Create toolbar
886 tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT)
887 tb.SetToolBitmapSize((24, 23))
888 tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file')
889 tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file')
890 tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file')
891 tb.AddSeparator()
892 tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut')
893 tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy')
894 tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste')
895 tb.AddSeparator()
896 tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(),
897 'Refresh', 'Refresh view')
898 tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(),
899 'Auto-refresh', 'Toggle auto-refresh mode', true)
900 tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
901 tb.Realize()
902 self.tb = tb
903
904 # File
905 EVT_MENU(self, wxID_NEW, self.OnNew)
906 EVT_MENU(self, wxID_OPEN, self.OnOpen)
907 EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs)
908 EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs)
909 EVT_MENU(self, wxID_EXIT, self.OnExit)
910 # Edit
911 EVT_MENU(self, wxID_UNDO, self.OnUndo)
912 EVT_MENU(self, wxID_REDO, self.OnRedo)
913 EVT_MENU(self, wxID_CUT, self.OnCut)
914 EVT_MENU(self, wxID_COPY, self.OnCopy)
915 EVT_MENU(self, wxID_PASTE, self.OnPaste)
916 EVT_MENU(self, self.ID_DELETE, self.OnDelete)
917 # View
918 EVT_MENU(self, self.ID_REFRESH, self.OnRefresh)
919 EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh)
920 # Help
921 EVT_MENU(self, wxID_ABOUT, self.OnAbout)
922
923 # Update events
924 EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI)
925 EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI)
926 EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI)
927 EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI)
928
929 # Build interface
930 sizer = wxBoxSizer(wxVERTICAL)
931 sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND)
932 splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH)
933 self.splitter = splitter
934 splitter.SetMinimumPaneSize(100)
935 # Create tree
936 global tree
937 tree = XML_Tree(splitter, -1)
938 sys.modules['xxx'].tree = tree
939 # Create panel for parameters
940 global panel
941 panel = Panel(splitter)
942 # Set plitter windows
943 splitter.SplitVertically(tree, panel, conf.sashPos)
944 sizer.Add(splitter, 1, wxEXPAND)
945 self.SetAutoLayout(true)
946 self.SetSizer(sizer)
947
948 # Init pull-down menu data
949 global pullDownMenu
950 pullDownMenu = PullDownMenu(self)
951 # Mapping from IDs to element names
952 self.createMap = {
953 pullDownMenu.ID_NEW_PANEL: 'wxPanel',
954 pullDownMenu.ID_NEW_DIALOG: 'wxDialog',
955 pullDownMenu.ID_NEW_FRAME: 'wxFrame',
956 pullDownMenu.ID_NEW_TOOL_BAR: 'wxToolBar',
957 pullDownMenu.ID_NEW_TOOL: 'tool',
958 pullDownMenu.ID_NEW_MENU_BAR: 'wxMenuBar',
959 pullDownMenu.ID_NEW_MENU: 'wxMenu',
960 pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem',
961 pullDownMenu.ID_NEW_SEPARATOR: 'separator',
962
963 pullDownMenu.ID_NEW_STATIC_TEXT: 'wxStaticText',
964 pullDownMenu.ID_NEW_TEXT_CTRL: 'wxTextCtrl',
965
966 pullDownMenu.ID_NEW_BUTTON: 'wxButton',
967 pullDownMenu.ID_NEW_BITMAP_BUTTON: 'wxBitmapButton',
968 pullDownMenu.ID_NEW_RADIO_BUTTON: 'wxRadioButton',
969 pullDownMenu.ID_NEW_SPIN_BUTTON: 'wxSpinButton',
970
971 pullDownMenu.ID_NEW_STATIC_BOX: 'wxStaticBox',
972 pullDownMenu.ID_NEW_CHECK_BOX: 'wxCheckBox',
973 pullDownMenu.ID_NEW_RADIO_BOX: 'wxRadioBox',
974 pullDownMenu.ID_NEW_COMBO_BOX: 'wxComboBox',
975 pullDownMenu.ID_NEW_LIST_BOX: 'wxListBox',
976
977 pullDownMenu.ID_NEW_STATIC_LINE: 'wxStaticLine',
978 pullDownMenu.ID_NEW_STATIC_BITMAP: 'wxStaticBitmap',
979 pullDownMenu.ID_NEW_CHOICE: 'wxChoice',
980 pullDownMenu.ID_NEW_SLIDER: 'wxSlider',
981 pullDownMenu.ID_NEW_GAUGE: 'wxGauge',
982 pullDownMenu.ID_NEW_SCROLL_BAR: 'wxScrollBar',
983 pullDownMenu.ID_NEW_TREE_CTRL: 'wxTreeCtrl',
984 pullDownMenu.ID_NEW_LIST_CTRL: 'wxListCtrl',
985 pullDownMenu.ID_NEW_CHECK_LIST: 'wxCheckList',
986 pullDownMenu.ID_NEW_NOTEBOOK: 'wxNotebook',
987 pullDownMenu.ID_NEW_HTML_WINDOW: 'wxHtmlWindow',
988 pullDownMenu.ID_NEW_CALENDAR: 'wxCalendar',
989
990 pullDownMenu.ID_NEW_BOX_SIZER: 'wxBoxSizer',
991 pullDownMenu.ID_NEW_STATIC_BOX_SIZER: 'wxStaticBoxSizer',
992 pullDownMenu.ID_NEW_GRID_SIZER: 'wxGridSizer',
993 pullDownMenu.ID_NEW_FLEX_GRID_SIZER: 'wxFlexGridSizer',
994 pullDownMenu.ID_NEW_SPACER: 'spacer',
995 }
996 pullDownMenu.controls = [
997 ['control', 'Various controls',
998 (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'),
999 (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'),
1000 (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'),
1001 (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'),
1002 (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'),
1003 (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'),
1004 (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'),
1005 (pullDownMenu.ID_NEW_TREE_CTRL, 'TreeCtrl', 'Create tree control'),
1006 (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'),
1007 (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckList', 'Create check list control'),
1008 (pullDownMenu.ID_NEW_HTML_WINDOW, 'HtmlWindow', 'Create HTML window'),
1009 (pullDownMenu.ID_NEW_CALENDAR, 'Calendar', 'Create calendar control'),
1010 (pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel'),
1011 (pullDownMenu.ID_NEW_NOTEBOOK, 'Notebook', 'Create notebook control'),
1012 ],
1013 ['button', 'Buttons',
1014 (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'),
1015 (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'),
1016 (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'),
1017 (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'),
1018 ],
1019 ['box', 'Boxes',
1020 (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'),
1021 (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'),
1022 (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'),
1023 (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'),
1024 (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'),
1025 ],
1026 ['sizer', 'Sizers',
1027 (pullDownMenu.ID_NEW_BOX_SIZER, 'BoxSizer', 'Create box sizer'),
1028 (pullDownMenu.ID_NEW_STATIC_BOX_SIZER, 'StaticBoxSizer',
1029 'Create static box sizer'),
1030 (pullDownMenu.ID_NEW_GRID_SIZER, 'GridSizer', 'Create grid sizer'),
1031 (pullDownMenu.ID_NEW_FLEX_GRID_SIZER, 'FlexGridSizer',
1032 'Create flexgrid sizer'),
1033 (pullDownMenu.ID_NEW_SPACER, 'Spacer', 'Create spacer'),
1034 ]
1035 ]
1036 pullDownMenu.menuControls = [
1037 (pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu'),
1038 (pullDownMenu.ID_NEW_MENU_ITEM, 'MenuItem', 'Create menu item'),
1039 (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'),
1040 ]
1041 pullDownMenu.toolBarControls = [
1042 (pullDownMenu.ID_NEW_TOOL, 'Tool', 'Create tool'),
1043 (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'),
1044 ]
1045
1046 # Initialize
1047 self.Clear()
1048
1049 # Other events
1050 EVT_IDLE(self, self.OnIdle)
1051 EVT_CLOSE(self, self.OnCloseWindow)
1052
1053 def OnNew(self, evt):
1054 self.Clear()
1055
1056 def OnOpen(self, evt):
1057 if not self.AskSave(): return
1058 dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile),
1059 '', '*.xrc', wxOPEN | wxCHANGE_DIR)
1060 if dlg.ShowModal() == wxID_OK:
1061 path = dlg.GetPath()
1062 self.SetStatusText('Loading...')
1063 wxYield()
1064 wxBeginBusyCursor()
1065 self.Open(path)
1066 wxEndBusyCursor()
1067 self.SetStatusText('Ready')
1068 dlg.Destroy()
1069
1070 def OnSaveOrSaveAs(self, evt):
1071 if evt.GetId() == wxID_SAVEAS or not self.dataFile:
1072 if self.dataFile: defaultName = ''
1073 else: defaultName = 'UNTITLED.xrc'
1074 dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile),
1075 defaultName, '*.xrc',
1076 wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR)
1077 if dlg.ShowModal() == wxID_OK:
1078 path = dlg.GetPath()
1079 dlg.Destroy()
1080 else:
1081 dlg.Destroy()
1082 return
1083 else:
1084 path = self.dataFile
1085 self.SetStatusText('Saving...')
1086 wxYield()
1087 wxBeginBusyCursor()
1088 self.Save(path)
1089 self.dataFile = path
1090 wxEndBusyCursor()
1091 self.SetStatusText('Ready')
1092
1093 def OnExit(self, evt):
1094 self.Close()
1095
1096 def OnUndo(self, evt):
1097 print '*** being implemented'
1098 print self.lastOp, self.undo
1099 if self.lastOp == 'DELETE':
1100 parent, prev, elem = self.undo
1101 if prev.IsOk():
1102 xxx = MakeXXXFromDOM(tree.GetPyData(parent).treeObject(), elem)
1103 item = tree.InsertItem( parent, prev, xxx.treeObject().className,
1104 data=wxTreeItemData(xxx) )
1105
1106 def OnRedo(self, evt):
1107 print '*** being implemented'
1108
1109 def OnCut(self, evt):
1110 selected = tree.GetSelection()
1111 # Undo info
1112 self.lastOp = 'CUT'
1113 self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)]
1114 # Delete testWin?
1115 global testWin
1116 if testWin:
1117 # If deleting top-level item, delete testWin
1118 if selected == testWin.item:
1119 testWin.Destroy()
1120 testWin = None
1121 else:
1122 # Remove highlight, update testWin
1123 if tree.GetItemAncestor(selected) == testWin.item:
1124 if testWin.highLight: testWin.highLight.Remove()
1125 tree.needUpdate = true
1126 self.clipboard = tree.RemoveLeaf(selected)
1127 tree.pendingHighLight = None
1128 tree.Unselect()
1129 panel.Clear()
1130 self.modified = true
1131
1132 def OnCopy(self, evt):
1133 selected = tree.GetSelection()
1134 xxx = tree.GetPyData(selected)
1135 self.clipboard = xxx.element.cloneNode(true)
1136
1137 def OnPaste(self, evt):
1138 selected = tree.GetSelection()
1139 appendChild = not tree.NeedInsert(selected)
1140 xxx = tree.GetPyData(selected)
1141 if not appendChild:
1142 # If has next item, insert, else append to parent
1143 nextItem = tree.GetNextSibling(selected)
1144 if nextItem.IsOk():
1145 # Insert before nextItem
1146 parentLeaf = tree.GetItemParent(selected)
1147 else: # last child: change selected to parent
1148 appendChild = true
1149 selected = tree.GetItemParent(selected)
1150 # Expanded container (must have children)
1151 elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected):
1152 appendChild = false
1153 nextItem = tree.GetFirstChild(selected, 0)[0]
1154 parentLeaf = selected
1155 # Parent should be tree element or None
1156 if appendChild:
1157 parent = tree.GetPyData(selected)
1158 else:
1159 parent = tree.GetPyData(parentLeaf)
1160 if parent and parent.hasChild: parent = parent.child
1161
1162 # Create a copy of clipboard element
1163 elem = self.clipboard.cloneNode(true)
1164 # Tempopary xxx object to test things
1165 xxx = MakeXXXFromDOM(parent, elem)
1166 className = xxx.treeObject().className
1167 # Check parent and child relationships
1168 # Parent is sizer or notebook, child is of wrong class or
1169 # parent is normal window, child is child container: detach child
1170 isChildContainer = isinstance(xxx, xxxChildContainer)
1171 if not parent and isChildContainer or \
1172 (parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
1173 (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
1174 (not parent.isSizer and not isinstance(parent, xxxNotebook) and \
1175 isChildContainer):
1176 if isChildContainer:
1177 elem.removeChild(xxx.child.element) # detach child
1178 elem.unlink() # delete child container
1179 elem = xxx.child.element # replace
1180 # This should help garbage collection (!!! maybe not needed?)
1181 xxx.child.parent = None
1182 xxx.child = None
1183 if parent:
1184 # Parent is sizer or notebook, child is not child container
1185 if parent.isSizer and not isChildContainer and \
1186 not isinstance(xxx, xxxSpacer):
1187 # Create sizer item element
1188 sizerItemElem = MakeEmptyDOM('sizeritem')
1189 sizerItemElem.appendChild(elem)
1190 elem = sizerItemElem
1191 elif isinstance(parent, xxxNotebook) and not isChildContainer:
1192 pageElem = MakeEmptyDOM('notebookpage')
1193 pageElem.appendChild(elem)
1194 elem = pageElem
1195 xxx = MakeXXXFromDOM(parent, elem)
1196 # Figure out if we must append a new child or sibling
1197 if appendChild:
1198 if parent: node = parent.element
1199 else: node = tree.mainNode
1200 node.appendChild(elem)
1201 newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(),
1202 data=wxTreeItemData(xxx))
1203 else:
1204 node = tree.GetPyData(nextItem).element
1205 if parent:
1206 elemParent = parent.element
1207 else:
1208 elemParent = tree.mainNode
1209 elemParent.insertBefore(elem, node)
1210 # Inserting before is difficult, se we insert after or first child
1211 newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(),
1212 image=xxx.treeImage(), data=wxTreeItemData(xxx))
1213 # Add children items
1214 if xxx.hasChildren:
1215 treeObj = xxx.treeObject()
1216 for n in treeObj.element.childNodes:
1217 if IsObject(n):
1218 tree.AddNode(newItem, treeObj, n)
1219 # Scroll to show new item
1220 tree.EnsureVisible(newItem)
1221 tree.SelectItem(newItem)
1222 if not tree.IsVisible(newItem):
1223 tree.ScrollTo(newItem)
1224 tree.Refresh()
1225 # Update view?
1226 if testWin and tree.GetItemAncestor(newItem) == testWin.item:
1227 if conf.autoRefresh:
1228 tree.needUpdate = true
1229 tree.pendingHighLight = newItem
1230 else:
1231 tree.pendingHighLight = None
1232 self.modified = true
1233
1234 def OnDelete(self, evt):
1235 selected = tree.GetSelection()
1236 # Undo info
1237 self.lastOp = 'DELETE'
1238 self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)]
1239 # Delete testWin?
1240 global testWin
1241 if testWin:
1242 # If deleting top-level item, delete testWin
1243 if selected == testWin.item:
1244 testWin.Destroy()
1245 testWin = None
1246 else:
1247 # Remove highlight, update testWin
1248 if tree.GetItemAncestor(selected) == testWin.item:
1249 if testWin.highLight: testWin.highLight.Remove()
1250 tree.needUpdate = true
1251 xnode = tree.RemoveLeaf(selected)
1252 self.undo.append(xnode.cloneNode(true))
1253 xnode.unlink()
1254 tree.pendingHighLight = None
1255 tree.Unselect()
1256 panel.Clear()
1257 self.modified = true
1258
1259 def OnRefresh(self, evt):
1260 # If modified, apply first
1261 selection = tree.GetSelection()
1262 if selection.IsOk():
1263 xxx = tree.GetPyData(selection)
1264 if xxx and panel.IsModified():
1265 tree.Apply(xxx, selection)
1266 if testWin:
1267 # (re)create
1268 tree.CreateTestWin(testWin.item)
1269 tree.needUpdate = false
1270
1271 def OnAutoRefresh(self, evt):
1272 conf.autoRefresh = evt.IsChecked()
1273 self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
1274 self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
1275
1276 def OnAbout(self, evt):
1277 str = '%s %s\n\nRoman Rolinsky <rolinsky@mema.ucl.ac.be>' % \
1278 (progname, version)
1279 dlg = wxMessageDialog(self, str, 'About ' + progname, wxOK | wxCENTRE)
1280 dlg.ShowModal()
1281 dlg.Destroy()
1282
1283 # Simple emulation of python command line
1284 def OnDebugCMD(self, evt):
1285 while 1:
1286 try:
1287 exec raw_input('C:\> ')
1288 except EOFError:
1289 print '^D'
1290 break
1291 except:
1292 (etype, value, tb) =sys.exc_info()
1293 tblist =traceback.extract_tb(tb)[1:]
1294 msg =string.join(traceback.format_exception_only(etype, value)
1295 +traceback.format_list(tblist))
1296 print msg
1297
1298 def OnCreate(self, evt):
1299 selected = tree.GetSelection()
1300 appendChild = not tree.NeedInsert(selected)
1301 xxx = tree.GetPyData(selected)
1302 if not appendChild:
1303 # If has next item, insert, else append to parent
1304 nextItem = tree.GetNextSibling(selected)
1305 if nextItem.IsOk():
1306 # Insert before nextItem
1307 parentLeaf = tree.GetItemParent(selected)
1308 else: # last child: change selected to parent
1309 appendChild = true
1310 selected = tree.GetItemParent(selected)
1311 # Expanded container (must have children)
1312 elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected):
1313 appendChild = false
1314 nextItem = tree.GetFirstChild(selected, 0)[0]
1315 parentLeaf = selected
1316 # Parent should be tree element or None
1317 if appendChild:
1318 parent = tree.GetPyData(selected)
1319 else:
1320 parent = tree.GetPyData(parentLeaf)
1321 if parent and parent.hasChild: parent = parent.child
1322
1323 # Create element
1324 className = self.createMap[evt.GetId()]
1325 xxx = MakeEmptyXXX(parent, className)
1326 # Figure out if we must append a new child or sibling
1327 elem = xxx.element
1328 if appendChild:
1329 if parent: node = parent.element
1330 else: node = tree.mainNode
1331 # Insert newline for debug purposes
1332 node.appendChild(tree.dom.createTextNode('\n'))
1333 node.appendChild(elem)
1334 newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(),
1335 data=wxTreeItemData(xxx))
1336 else:
1337 node = tree.GetPyData(nextItem).element
1338 if parent:
1339 elemParent = parent.element
1340 else:
1341 elemParent = tree.mainNode
1342 elemParent.insertBefore(tree.dom.createTextNode('\n'), node)
1343 elemParent.insertBefore(elem, node)
1344 # Inserting before is difficult, se we insert after or first child
1345 newItem = tree.InsertItem(parentLeaf, selected,
1346 xxx.treeName(), image=xxx.treeImage(),
1347 data=wxTreeItemData(xxx))
1348 tree.EnsureVisible(newItem)
1349 tree.SelectItem(newItem)
1350 if not tree.IsVisible(newItem):
1351 tree.ScrollTo(newItem)
1352 tree.Refresh()
1353 # Update view?
1354 if testWin and tree.GetItemAncestor(newItem) == testWin.item:
1355 if conf.autoRefresh:
1356 tree.needUpdate = true
1357 tree.pendingHighLight = newItem
1358 else:
1359 tree.pendingHighLight = None
1360
1361 def OnExpand(self, evt):
1362 if tree.GetSelection().IsOk():
1363 tree.ExpandAll(tree.GetSelection())
1364 else:
1365 tree.ExpandAll(tree.GetRootItem())
1366
1367 def OnPullDownHighlight(self, evt):
1368 menuId = evt.GetMenuId()
1369 if menuId != -1:
1370 menu = evt.GetEventObject()
1371 help = menu.GetHelpString(menuId)
1372 self.SetStatusText(help)
1373 else:
1374 self.SetStatusText('')
1375
1376 def OnUpdateUI(self, evt):
1377 if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]:
1378 enable = tree.GetSelection().IsOk() and \
1379 tree.GetSelection() != tree.GetRootItem()
1380 evt.Enable(enable)
1381 elif evt.GetId() == wxID_PASTE:
1382 enable = tree.GetSelection().IsOk() and \
1383 tree.GetSelection() != tree.GetRootItem() and \
1384 self.clipboard != None
1385 evt.Enable(enable)
1386
1387 def OnIdle(self, evt):
1388 if tree.needUpdate:
1389 if conf.autoRefresh:
1390 if testWin:
1391 # (re)create
1392 tree.CreateTestWin(testWin.item)
1393 tree.needUpdate = false
1394 elif tree.pendingHighLight:
1395 tree.HighLight(tree.pendingHighLight)
1396 else:
1397 evt.Skip()
1398
1399 def OnCloseWindow(self, evt):
1400 if not self.AskSave(): return
1401 if testWin: testWin.Destroy()
1402 # Destroy cached windows
1403 for w in panel.styleCache.values(): w.Destroy()
1404 conf.width, conf.height = self.GetSize()
1405 conf.sashPos = self.splitter.GetSashPosition()
1406 evt.Skip()
1407
1408 def Clear(self):
1409 self.dataFile = ''
1410 self.clipboard = None
1411 self.modified = false
1412 panel.SetModified(false)
1413 panel.Clear()
1414 tree.Clear()
1415 global testWin
1416 if testWin:
1417 testWin.Destroy()
1418 testWin = None
1419 self.SetTitle(progname)
1420
1421 def Open(self, path):
1422 # Try to read the file
1423 try:
1424 open(path)
1425 self.Clear()
1426 # Build wx tree
1427 dom = minidom.parse(path)
1428 tree.SetData(dom)
1429 self.dataFile = path
1430 self.SetTitle(progname + ': ' + os.path.basename(path))
1431 except:
1432 wxLogError('Error reading file: ' + path)
1433 raise
1434
1435 def Save(self, path):
1436 try:
1437 self.OnRefresh(wxCommandEvent())
1438 memFile = MemoryFile(path)
1439 tree.dom.writexml(memFile)
1440 memFile.close()
1441 self.modified = false
1442 panel.SetModified(false)
1443 except:
1444 wxLogError('Error writing file: ' + path)
1445 raise
1446
1447 def AskSave(self):
1448 if not (self.modified or panel.IsModified()): return true
1449 flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
1450 dlg = wxMessageDialog( self, 'File is modified. Save before exit?',
1451 'Save before too late?', flags )
1452 say = dlg.ShowModal()
1453 dlg.Destroy()
1454 if say == wxID_YES:
1455 self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE))
1456 # If save was successful, modified flag is unset
1457 if not self.modified: return true
1458 elif say == wxID_NO:
1459 self.modified = false
1460 panel.SetModified(false)
1461 return true
1462 return false
1463
1464 ################################################################################
1465
1466 class App(wxApp):
1467 def OnInit(self):
1468 self.SetAppName("xrced")
1469 # Settings
1470 global conf
1471 # !!! wxConfigBase_Get doesn't seem to work
1472 conf = wxConfig(style=wxCONFIG_USE_LOCAL_FILE)
1473 conf.autoRefresh = conf.ReadInt('autorefresh', true)
1474 size = conf.ReadInt('width', 800), conf.ReadInt('height', 600)
1475 conf.sashPos = conf.ReadInt('sashPos', 200)
1476 # Add handlers
1477 wxFileSystem_AddHandler(wxMemoryFSHandler())
1478 wxInitAllImageHandlers()
1479 # Create main frame
1480 global frame
1481 frame = self.frame = Frame(size)
1482 self.frame.Show(true)
1483 # Load resources from XRC file (!!! should be transformed to .py later?)
1484 sys.modules['params'].frame = frame
1485 frame.res = wxXmlResource('')
1486 frame.res.Load(os.path.join(sys.path[0], 'xrced.xrc'))
1487 return true
1488
1489 def OnExit(self):
1490 # Write config
1491 wc = wxConfigBase_Get()
1492 wc.WriteInt('autorefresh', conf.autoRefresh)
1493 wc.WriteInt('width', conf.width)
1494 wc.WriteInt('height', conf.height)
1495 wc.WriteInt('sashPos', conf.sashPos)
1496 wc.Flush()
1497
1498 def main():
1499 app = App()
1500 app.MainLoop()
1501 app.OnExit()
1502
1503 if __name__ == '__main__':
1504 main()