]> git.saurik.com Git - wxWidgets.git/blob - wxPython/tools/XRCed/xrced.py
wxFont can now raelly use the native fonts
[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 self.SetIcon(wxIconFromXPMData(images.getIconData()))
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')
851
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')
862
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')
871
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
884 tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT)
885 tb.SetToolBitmapSize((24, 23))
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)
899 tb.Realize()
900 self.tb = tb
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
928 sizer = wxBoxSizer(wxVERTICAL)
929 sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND)
930 splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH)
931 self.splitter = splitter
932 splitter.SetMinimumPaneSize(100)
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
939 panel = Panel(splitter)
940 # Set plitter windows
941 splitter.SplitVertically(tree, panel, conf.sashPos)
942 sizer.Add(splitter, 1, wxEXPAND)
943 self.SetAutoLayout(true)
944 self.SetSizer(sizer)
945
946 # Init pull-down menu data
947 global pullDownMenu
948 pullDownMenu = PullDownMenu(self)
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',
954 pullDownMenu.ID_NEW_TOOL_BAR: 'wxToolBar',
955 pullDownMenu.ID_NEW_TOOL: 'tool',
956 pullDownMenu.ID_NEW_MENU_BAR: 'wxMenuBar',
957 pullDownMenu.ID_NEW_MENU: 'wxMenu',
958 pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem',
959 pullDownMenu.ID_NEW_SEPARATOR: 'separator',
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',
968
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',
974
975 pullDownMenu.ID_NEW_STATIC_LINE: 'wxStaticLine',
976 pullDownMenu.ID_NEW_STATIC_BITMAP: 'wxStaticBitmap',
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',
987
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',
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 ]
1039 pullDownMenu.toolBarControls = [
1040 (pullDownMenu.ID_NEW_TOOL, 'Tool', 'Create tool'),
1041 (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'),
1042 ]
1043
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')
1066 dlg.Destroy()
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)
1075 if dlg.ShowModal() == wxID_OK:
1076 path = dlg.GetPath()
1077 dlg.Destroy()
1078 else:
1079 dlg.Destroy()
1080 return
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) )
1103
1104 def OnRedo(self, evt):
1105 print '*** being implemented'
1106
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)
1149 elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected):
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):
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()
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)
1310 elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected):
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()
1367 if menuId != -1:
1368 menu = evt.GetEventObject()
1369 help = menu.GetHelpString(menuId)
1370 self.SetStatusText(help)
1371 else:
1372 self.SetStatusText('')
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:
1380 enable = tree.GetSelection().IsOk() and \
1381 tree.GetSelection() != tree.GetRootItem() and \
1382 self.clipboard != None
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)
1394 else:
1395 evt.Skip()
1396
1397 def OnCloseWindow(self, evt):
1398 if not self.AskSave(): return
1399 if testWin: testWin.Destroy()
1400 # Destroy cached windows
1401 for w in panel.styleCache.values(): w.Destroy()
1402 conf.width, conf.height = self.GetSize()
1403 conf.sashPos = self.splitter.GetSashPosition()
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)
1431 raise
1432
1433 def Save(self, path):
1434 try:
1435 self.OnRefresh(wxCommandEvent())
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)
1443 raise
1444
1445 def AskSave(self):
1446 if not (self.modified or panel.IsModified()): return true
1447 flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
1448 dlg = wxMessageDialog( self, 'File is modified. Save before exit?',
1449 'Save before too late?', flags )
1450 say = dlg.ShowModal()
1451 dlg.Destroy()
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
1462 ################################################################################
1463
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)
1473 conf.sashPos = conf.ReadInt('sashPos', 200)
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)
1481 # Load resources from XRC file (!!! should be transformed to .py later?)
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)
1493 wc.WriteInt('sashPos', conf.sashPos)
1494 wc.Flush()
1495
1496 def main():
1497 app = App()
1498 app.MainLoop()
1499 app.OnExit()
1500
1501 if __name__ == '__main__':
1502 main()