]> git.saurik.com Git - wxWidgets.git/blob - wxPython/tools/XRCed/xrced.py
More XRCed updates from Roman
[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 from xml.dom import minidom
11 import os
12 import getopt
13
14 # Icons
15 import images
16
17 # Constants
18
19 # Return code from wxGetOsVersion
20 wxGTK = 9
21
22 if wxGetOsVersion()[0] == wxGTK:
23 labelFont = wxFont(12, wxDEFAULT, wxNORMAL, wxBOLD)
24 modernFont = wxFont(12, wxMODERN, wxNORMAL, wxNORMAL)
25 else:
26 labelFont = wxFont(10, wxDEFAULT, wxNORMAL, wxBOLD)
27 modernFont = wxFont(10, wxMODERN, wxNORMAL, wxNORMAL)
28
29 progname = 'XRCed'
30 version = '0.0.7-2'
31
32 # Local modules
33 from xxx import *
34
35 # Globals
36 testWin = None
37 testWinPos = wxDefaultPosition
38
39 # 1 adds CMD command to Help menu
40 debug = 0
41
42 helpText = """\
43 <HTML><H2>Welcome to XRCed!</H2><H3><font color="green">DON'T PANIC :)</font></H3>
44 To start select tree root, then popup menu with your right mouse button,
45 select "Append Child", and then any command.<P>
46 Enter XML ID, change properties, create children.<P>
47 To test your interface select Test command (View menu).<P>
48 Consult README file for the details.</HTML>
49 """
50
51 defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME',
52 xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR'}
53
54 # Set menu to list items.
55 # Each menu command is a tuple (id, label, help)
56 # submenus are lists [id, label, help, submenu]
57 # and separators are any other type
58 def SetMenu(m, list):
59 for l in list:
60 if type(l) == types.TupleType:
61 apply(m.Append, l)
62 elif type(l) == types.ListType:
63 subMenu = wxMenu()
64 SetMenu(subMenu, l[2:])
65 m.AppendMenu(wxNewId(), l[0], subMenu, l[1])
66 else: # separator
67 m.AppendSeparator()
68
69 ################################################################################
70
71 # Properties panel containing notebook
72 class Panel(wxNotebook):
73 def __init__(self, parent, id = -1):
74 wxNotebook.__init__(self, parent, id, style=wxNB_BOTTOM)
75 sys.modules['params'].panel = self
76 # List of child windows
77 self.pages = []
78 # Create scrolled windows for pages
79 self.page1 = wxScrolledWindow(self, -1)
80 sizer = wxBoxSizer()
81 sizer.Add(wxBoxSizer()) # dummy sizer
82 self.page1.SetAutoLayout(true)
83 self.page1.SetSizer(sizer)
84 self.AddPage(self.page1, 'Properties')
85 # Second page
86 self.page2 = wxScrolledWindow(self, -1)
87 sizer = wxBoxSizer()
88 sizer.Add(wxBoxSizer()) # dummy sizer
89 self.page2.SetAutoLayout(true)
90 self.page2.SetSizer(sizer)
91 # Cache for already used panels
92 self.pageCache = {} # cached property panels
93 self.stylePageCache = {} # cached style panels
94 # Dummy parent window for cache pages
95 self.cacheParent = wxFrame(None, -1, 'non visible')
96 # Delete child windows and recreate page sizer
97 def ResetPage(self, page):
98 topSizer = page.GetSizer()
99 sizer = topSizer.GetChildren()[0].GetSizer()
100 for w in page.GetChildren():
101 sizer.RemoveWindow(w)
102 if isinstance(w, ParamPage):
103 # With SetParent, we wouldn't need this
104 w.Reparent(self.cacheParent)
105 else:
106 w.Destroy()
107 topSizer.RemoveSizer(sizer)
108 # Create new windows
109 sizer = wxBoxSizer(wxVERTICAL)
110 # Special case - resize html window
111 if conf.panic:
112 topSizer.Add(sizer, 1, wxEXPAND)
113 else:
114 topSizer.Add(sizer, 0, wxALL, 5)
115 return sizer
116 def SetData(self, xxx):
117 self.pages = []
118 # First page
119 # Set cached or new page
120 # Remove current objects and sizer
121 sizer = self.ResetPage(self.page1)
122 if not xxx or (not xxx.allParams and not xxx.hasName):
123 if tree.selection:
124 sizer.Add(wxStaticText(self.page1, -1, 'This item has no properties.'))
125 else: # nothing selected
126 # If first time, show some help
127 if conf.panic:
128 html = wxHtmlWindow(self.page1, -1, wxDefaultPosition,
129 wxDefaultSize, wxSUNKEN_BORDER)
130 html.SetPage(helpText)
131 sizer.Add(html, 1, wxEXPAND)
132 conf.panic = false
133 else:
134 sizer.Add(wxStaticText(self.page1, -1, 'Select a tree item.'))
135 else:
136 SetCurrentXXX(xxx.treeObject())
137 try:
138 page = self.pageCache[xxx.__class__]
139 page.Reparent(self.page1)
140 except KeyError:
141 page = PropPage(self.page1, xxx.className, xxx)
142 self.pageCache[xxx.__class__] = page
143 page.SetValues(xxx)
144 self.pages.append(page)
145 sizer.Add(page, 1, wxEXPAND)
146 if xxx.hasChild:
147 # Special label for child objects - they may have different GUI
148 cacheID = (xxx.child.__class__, xxx.__class__)
149 try:
150 page = self.pageCache[cacheID]
151 page.Reparent(self.page1)
152 except KeyError:
153 page = PropPage(self.page1, xxx.child.className, xxx.child)
154 self.pageCache[cacheID] = page
155 page.SetValues(xxx.child)
156 self.pages.append(page)
157 sizer.Add(page, 0, wxEXPAND | wxTOP, 5)
158 self.page1.Layout()
159 size = self.page1.GetSizer().GetMinSize()
160 self.page1.SetScrollbars(1, 1, size.x, size.y, 0, 0, true)
161
162 # Second page
163 # Create if does not exist
164 if xxx and xxx.treeObject().hasStyle:
165 xxx = xxx.treeObject()
166 # Simplest case: set data if class is the same
167 sizer = self.ResetPage(self.page2)
168 try:
169 page = self.stylePageCache[xxx.__class__]
170 page.Reparent(self.page2)
171 except KeyError:
172 page = StylePage(self.page2, xxx.className + ' style', xxx)
173 self.stylePageCache[xxx.__class__] = page
174 page.SetValues(xxx)
175 self.pages.append(page)
176 sizer.Add(page, 0, wxEXPAND)
177 # Add page if not exists
178 if not self.GetPageCount() == 2:
179 self.AddPage(self.page2, 'Style')
180 self.page2.Layout()
181 size = self.page2.GetSizer().GetMinSize()
182 self.page2.SetScrollbars(1, 1, size.x, size.y, 0, 0, true)
183 else:
184 # Remove page if exists
185 if self.GetPageCount() == 2:
186 self.RemovePage(1)
187 def Clear(self):
188 self.SetData(None)
189 # Check if some parameter on some page has changed
190 def IsModified(self):
191 for p in self.pages:
192 if p.IsModified(): return true
193 return false
194 # Reset changed state
195 def SetModified(self, value):
196 for p in self.pages: p.SetModified(value)
197 def Apply(self):
198 for p in self.pages: p.Apply()
199
200 ################################################################################
201
202 # General class for notebook pages
203 class ParamPage(wxPanel):
204 def __init__(self, parent, xxx):
205 wxPanel.__init__(self, parent, -1)
206 self.xxx = xxx
207 # Register event handlers
208 for id in paramIDs.values():
209 EVT_CHECKBOX(self, id, self.OnCheckParams)
210 self.modified = false
211 self.checks = {}
212 self.controls = {} # save python objects
213 self.controlName = None
214 def OnCheckParams(self, evt):
215 xxx = self.xxx
216 param = evt.GetEventObject().GetName()
217 w = self.controls[param]
218 objElem = xxx.element
219 if evt.IsChecked():
220 # Ad new text node in order of allParams
221 w.SetValue('') # set empty (default) value
222 w.SetModified() # mark as changed
223 elem = tree.dom.createElement(param)
224 # Some classes are special
225 if param == 'font':
226 xxx.params[param] = xxxParamFont(xxx.element, elem)
227 else:
228 xxx.params[param] = xxxParam(elem)
229 # Find place to put new element: first present element after param
230 found = false
231 paramStyles = xxx.allParams + xxx.styles
232 for p in paramStyles[paramStyles.index(param) + 1:]:
233 # Content params don't have same type
234 if xxx.params.has_key(p) and p != 'content':
235 found = true
236 break
237 if found:
238 nextTextElem = xxx.params[p].node
239 objElem.insertBefore(elem, nextTextElem)
240 else:
241 objElem.appendChild(elem)
242 else:
243 # Remove parameter
244 xxx.params[param].remove()
245 del xxx.params[param]
246 w.SetValue('')
247 w.modified = false # mark as not changed
248 # Set modified flas
249 self.SetModified(true)
250 w.Enable(evt.IsChecked())
251 # If some parameter has changed
252 def IsModified(self):
253 return self.modified
254 def SetModified(self, value):
255 self.modified = value
256 def Apply(self):
257 xxx = self.xxx
258 # !!! Save undo info
259 # if xxx.undo: xxx.undo.unlink()
260 # xxx.undo = xxx.element.cloneNode(false)
261 if self.controlName:
262 name = self.controlName.GetValue()
263 if xxx.name != name:
264 xxx.name = name
265 xxx.element.setAttribute('name', name)
266 for param, w in self.controls.items():
267 if w.modified:
268 paramObj = xxx.params[param]
269 value = w.GetValue()
270 if param in xxx.specials:
271 xxx.setSpecial(param, value)
272 else:
273 paramObj.update(value)
274
275 ################################################################################
276
277 # Panel for displaying properties
278 class PropPage(ParamPage):
279 def __init__(self, parent, label, xxx):
280 ParamPage.__init__(self, parent, xxx)
281 box = wxStaticBox(self, -1, label)
282 box.SetFont(labelFont)
283 topSizer = wxStaticBoxSizer(box, wxVERTICAL)
284 sizer = wxFlexGridSizer(len(xxx.allParams), 2, 1, 1)
285 if xxx.hasName:
286 label = wxStaticText(self, -1, 'XML ID:', size=(100,-1))
287 control = ParamText(self, name='XML_name')
288 sizer.AddMany([ (label, 0, wxALIGN_CENTER_VERTICAL),
289 (control, 0, wxALIGN_CENTER_VERTICAL) ])
290 self.controlName = control
291 for param in xxx.allParams:
292 present = param in xxx.params
293 if param in xxx.required:
294 label = wxStaticText(self, paramIDs[param], param + ':',
295 size = (100,-1), name = param)
296 else:
297 # Notebook has one very loooooong parameter
298 if param == 'usenotebooksizer': sParam = 'usesizer:'
299 else: sParam = param + ':'
300 label = wxCheckBox(self, paramIDs[param], sParam,
301 size = (100,-1), name = param)
302 self.checks[param] = label
303 try:
304 typeClass = xxx.paramDict[param]
305 except KeyError:
306 try:
307 # Standart type
308 typeClass = paramDict[param]
309 except KeyError:
310 # Default
311 typeClass = ParamText
312 control = typeClass(self, param)
313 control.Enable(present)
314 sizer.AddMany([ (label, 0, wxALIGN_CENTER_VERTICAL),
315 (control, 0, wxALIGN_CENTER_VERTICAL) ])
316 self.controls[param] = control
317 topSizer.Add(sizer, 1, wxALL | wxEXPAND, 3)
318 self.SetAutoLayout(true)
319 self.SetSizer(topSizer)
320 topSizer.Fit(self)
321 def SetValues(self, xxx):
322 self.xxx = xxx
323 # Set values, checkboxes to false, disable defaults
324 if xxx.hasName:
325 self.controlName.SetValue(xxx.name)
326 for param in xxx.allParams:
327 w = self.controls[param]
328 w.modified = false
329 try:
330 value = xxx.params[param].value()
331 w.Enable(true)
332 w.SetValue(value)
333 if not param in xxx.required:
334 self.checks[param].SetValue(true)
335 except KeyError:
336 self.checks[param].SetValue(false)
337 w.SetValue('')
338 w.Enable(false)
339 self.SetModified(false)
340
341 ################################################################################
342
343 # Style notebook page
344 class StylePage(ParamPage):
345 def __init__(self, parent, label, xxx):
346 ParamPage.__init__(self, parent, xxx)
347 box = wxStaticBox(self, -1, label)
348 box.SetFont(labelFont)
349 topSizer = wxStaticBoxSizer(box, wxVERTICAL)
350 sizer = wxFlexGridSizer(len(xxx.styles), 2, 1, 1)
351 for param in xxx.styles:
352 present = param in xxx.params.keys()
353 check = wxCheckBox(self, paramIDs[param],
354 param + ':', size = (100,-1), name = param)
355 check.SetValue(present)
356 control = paramDict[param](self, name = param)
357 control.Enable(present)
358 sizer.AddMany([ (check, 0, wxALIGN_CENTER_VERTICAL),
359 (control, 0, wxALIGN_CENTER_VERTICAL) ])
360 self.checks[param] = check
361 self.controls[param] = control
362 topSizer.Add(sizer, 1, wxALL | wxEXPAND, 3)
363 self.SetAutoLayout(true)
364 self.SetSizer(topSizer)
365 topSizer.Fit(self)
366 # Set data for a cahced page
367 def SetValues(self, xxx):
368 self.xxx = xxx
369 for param in xxx.styles:
370 present = param in xxx.params.keys()
371 check = self.checks[param]
372 check.SetValue(present)
373 control = self.controls[param]
374 if present:
375 control.SetValue(xxx.params[param].value())
376 else:
377 control.SetValue('')
378 control.Enable(present)
379 self.SetModified(false)
380
381 ################################################################################
382
383 class HightLightBox:
384 def __init__(self, pos, size):
385 w = testWin.panel
386 l1 = wxWindow(w, -1, pos, wxSize(size.x, 2))
387 l1.SetBackgroundColour(wxRED)
388 l2 = wxWindow(w, -1, pos, wxSize(2, size.y))
389 l2.SetBackgroundColour(wxRED)
390 l3 = wxWindow(w, -1, wxPoint(pos.x + size.x - 2, pos.y), wxSize(2, size.y))
391 l3.SetBackgroundColour(wxRED)
392 l4 = wxWindow(w, -1, wxPoint(pos.x, pos.y + size.y - 2), wxSize(size.x, 2))
393 l4.SetBackgroundColour(wxRED)
394 self.lines = [l1, l2, l3, l4]
395 # Move highlight to a new position
396 def Replace(self, pos, size):
397 self.lines[0].SetDimensions(pos.x, pos.y, size.x, 2, wxSIZE_ALLOW_MINUS_ONE)
398 self.lines[1].SetDimensions(pos.x, pos.y, 2, size.y, wxSIZE_ALLOW_MINUS_ONE)
399 self.lines[2].SetDimensions(pos.x + size.x - 2, pos.y, 2, size.y,
400 wxSIZE_ALLOW_MINUS_ONE)
401 self.lines[3].SetDimensions(pos.x, pos.y + size.y - 2, size.x, 2,
402 wxSIZE_ALLOW_MINUS_ONE)
403 # Remove it
404 def Remove(self):
405 map(wxWindow.Destroy, self.lines)
406 testWin.highLight = None
407
408 ################################################################################
409
410 class MemoryFile:
411 def __init__(self, name):
412 self.name = name
413 self.buffer = ''
414 def write(self, data):
415 self.buffer += data.encode()
416 def close(self):
417 wxMemoryFSHandler_AddFile(self.name, self.buffer)
418
419 class XML_Tree(wxTreeCtrl):
420 def __init__(self, parent, id):
421 wxTreeCtrl.__init__(self, parent, id, style = wxTR_HAS_BUTTONS)
422 self.SetBackgroundColour(wxColour(224, 248, 224))
423 EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
424 # One works on Linux, another on Windows
425 if wxGetOsVersion()[1] == 1:
426 EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated)
427 else:
428 EVT_LEFT_DCLICK(self, self.OnDClick)
429 EVT_RIGHT_DOWN(self, self.OnRightDown)
430
431 self.needUpdate = false
432 self.pendingHighLight = None
433 self.ctrl = self.shift = false
434 self.dom = None
435 # Create image list
436 il = wxImageList(16, 16, true)
437 self.rootImage = il.AddIcon(wxIconFromXPMData(images.getTreeRootData()))
438 xxxObject.image = il.AddIcon(wxIconFromXPMData(images.getTreeDefaultData()))
439 xxxPanel.image = il.AddIcon(wxIconFromXPMData(images.getTreePanelData()))
440 xxxDialog.image = il.AddIcon(wxIconFromXPMData(images.getTreeDialogData()))
441 xxxFrame.image = il.AddIcon(wxIconFromXPMData(images.getTreeFrameData()))
442 xxxMenuBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuBarData()))
443 xxxToolBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeToolBarData()))
444 xxxMenu.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuData()))
445 xxxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeSizerHData()))
446 xxxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeSizerVData()))
447 xxxStaticBoxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerHData()))
448 xxxStaticBoxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerVData()))
449 xxxGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerGridData()))
450 xxxFlexGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerFlexGridData()))
451 self.il = il
452 self.SetImageList(il)
453
454 def Unselect(self):
455 self.selection = None
456 wxTreeCtrl.Unselect(self)
457
458 def ExpandAll(self, item):
459 if self.ItemHasChildren(item):
460 self.Expand(item)
461 i, cookie = self.GetFirstChild(item, 0)
462 children = []
463 while i.IsOk():
464 children.append(i)
465 i, cookie = self.GetNextChild(item, cookie)
466 for i in children:
467 self.ExpandAll(i)
468 def CollapseAll(self, item):
469 if self.ItemHasChildren(item):
470 i, cookie = self.GetFirstChild(item, 0)
471 children = []
472 while i.IsOk():
473 children.append(i)
474 i, cookie = self.GetNextChild(item, cookie)
475 for i in children:
476 self.CollapseAll(i)
477 self.Collapse(item)
478
479 # Clear tree
480 def Clear(self):
481 self.DeleteAllItems()
482 # Add minimal structure
483 if self.dom: self.dom.unlink()
484 self.dom = minidom.Document()
485 self.dummyNode = self.dom.createComment('dummy node')
486 # Create main node
487 self.mainNode = self.dom.createElement('resource')
488 self.dom.appendChild(self.mainNode)
489 xxx = xxxMainNode(None, self.mainNode)
490 self.root = self.AddRoot('XML tree', self.rootImage, data=wxTreeItemData(xxx))
491 self.SetItemHasChildren(self.root)
492 self.Expand(self.root)
493 self.Unselect()
494
495 # Clear old data and set new
496 def SetData(self, dom):
497 self.DeleteAllItems()
498 # Add minimal structure
499 if self.dom: self.dom.unlink()
500 self.dom = dom
501 self.dummyNode = self.dom.createComment('dummy node')
502 # Find 'resource' child, add it's children
503 self.mainNode = dom.getElementsByTagName('resource')[0]
504 xxx = xxxMainNode(None, self.mainNode)
505 self.root = self.AddRoot('XML tree', self.rootImage, data=wxTreeItemData(xxx))
506 self.SetItemHasChildren(self.root)
507 nodes = self.mainNode.childNodes[:]
508 for node in nodes:
509 if IsObject(node):
510 self.AddNode(self.root, None, node)
511 else:
512 self.mainNode.removeChild(node)
513 node.unlink()
514 self.Expand(self.root)
515 self.Unselect()
516
517 # Add tree item for given parent item if node is DOM element node with
518 # 'object' tag. xxxParent is parent xxx object
519 def AddNode(self, itemParent, xxxParent, node):
520 # Set item data to current node
521 try:
522 xxx = MakeXXXFromDOM(xxxParent, node)
523 except:
524 print 'ERROR: MakeXXXFromDom(%s, %s)' % (xxxParent, node)
525 raise
526 # return
527 treeObj = xxx.treeObject()
528 # Append tree item
529 item = self.AppendItem(itemParent, treeObj.treeName(),
530 image=treeObj.treeImage(),
531 data=wxTreeItemData(xxx))
532 # Try to find children objects
533 if treeObj.hasChildren:
534 nodes = treeObj.element.childNodes[:]
535 for n in nodes:
536 if IsObject(n):
537 self.AddNode(item, treeObj, n)
538 elif n.nodeType != minidom.Node.ELEMENT_NODE:
539 treeObj.element.removeChild(n)
540 n.unlink()
541 # Remove leaf of tree, return it's data object
542 def RemoveLeaf(self, leaf):
543 xxx = self.GetPyData(leaf)
544 node = xxx.element
545 parent = node.parentNode
546 parent.removeChild(node)
547 self.Delete(leaf)
548 # Reset selection object
549 self.selection = None
550 return node
551 # Find position relative to the top-level window
552 def FindNodePos(self, item):
553 # Root at (0,0)
554 if item == testWin.item: return wxPoint(0, 0)
555 itemParent = self.GetItemParent(item)
556 # Select NB page
557 obj = self.FindNodeObject(item)
558 if self.GetPyData(itemParent).treeObject().__class__ == xxxNotebook:
559 notebook = self.FindNodeObject(itemParent)
560 # Find position
561 for i in range(notebook.GetPageCount()):
562 if notebook.GetPage(i) == obj:
563 if notebook.GetSelection() != i: notebook.SetSelection(i)
564 break
565 # Find first ancestor which is a wxWindow (not a sizer)
566 winParent = itemParent
567 while self.GetPyData(winParent).isSizer:
568 winParent = self.GetItemParent(winParent)
569 parentPos = self.FindNodePos(winParent)
570 # Position (-1,-1) is really (0,0)
571 pos = obj.GetPosition()
572 if pos == (-1,-1): pos = (0,0)
573 return parentPos + pos
574 # Find window (or sizer) corresponding to a tree item.
575 def FindNodeObject(self, item):
576 if item == testWin.item: return testWin.panel
577 itemParent = self.GetItemParent(item)
578 # If top-level, return testWin (or panel if wxFrame)
579 xxx = self.GetPyData(item).treeObject()
580 parentWin = self.FindNodeObject(itemParent)
581 # Top-level sizer? return window's sizer
582 if xxx.isSizer and isinstance(parentWin, wxWindowPtr):
583 return parentWin.GetSizer()
584 # Otherwise get parent's object and it's child
585 n = 0 # index of sibling
586 prev = self.GetPrevSibling(item)
587 while prev.IsOk():
588 prev = self.GetPrevSibling(prev)
589 n += 1
590 child = parentWin.GetChildren()[n]
591 # Return window or sizer for sizer items
592 if child.GetClassName() == 'wxSizerItem':
593 if child.IsWindow(): child = child.GetWindow()
594 elif child.IsSizer():
595 child = child.GetSizer()
596 # Test for notebook sizers
597 if isinstance(child, wxNotebookSizerPtr):
598 child = child.GetNotebook()
599 return child
600 def OnSelChanged(self, evt):
601 # Apply changes
602 # !!! problem with wxGTK - GetOldItem is Ok if nothing selected
603 #oldItem = evt.GetOldItem()
604 status = ''
605 oldItem = self.selection
606 if oldItem:
607 xxx = self.GetPyData(oldItem)
608 # If some data was modified, apply changes
609 if panel.IsModified():
610 self.Apply(xxx, oldItem)
611 #if conf.autoRefresh:
612 if testWin and not tree.IsHighlatable(oldItem):
613 if testWin.highLight:
614 testWin.highLight.Remove()
615 self.needUpdate = true
616 status = 'Changes were applied'
617 frame.SetStatusText(status)
618 # Generate view
619 self.selection = evt.GetItem()
620 if not self.selection.IsOk():
621 self.selection = None
622 return
623 xxx = self.GetPyData(self.selection)
624 # Update panel
625 panel.SetData(xxx)
626 # Clear flag
627 panel.SetModified(false)
628 # Hightlighting is done in OnIdle
629 tree.pendingHighLight = self.selection
630 # Check if item is in testWin subtree
631 def IsHighlatable(self, item):
632 if item == testWin.item: return false
633 while item != self.root:
634 item = self.GetItemParent(item)
635 if item == testWin.item: return true
636 return false
637 # Highlight selected item
638 def HighLight(self, item):
639 self.pendingHighLight = None
640 if not testWin or self.GetPyData(testWin.item).className \
641 not in ['wxDialog', 'wxPanel', 'wxFrame']:
642 return
643 # Top-level does not have highlight
644 if item == testWin.item or item == tree.root:
645 if testWin.highLight: testWin.highLight.Remove()
646 return
647 # If a control from another window is selected, remove highlight
648 if not self.IsHighlatable(item):
649 if testWin.highLight: testWin.highLight.Remove()
650 return
651 # Get window/sizer object
652 obj, pos = self.FindNodeObject(item), self.FindNodePos(item)
653 size = obj.GetSize()
654 # Highlight
655 # Nagative positions are not working wuite well
656 if testWin.highLight:
657 testWin.highLight.Replace(pos, size)
658 else:
659 testWin.highLight = HightLightBox(pos, size)
660 testWin.highLight.item = item
661 def ShowTestWindow(self, item):
662 global testWin
663 xxx = self.GetPyData(item)
664 if panel.IsModified():
665 self.Apply(xxx, item) # apply changes
666 treeObj = xxx.treeObject()
667 if treeObj.className not in ['wxFrame', 'wxPanel', 'wxDialog',
668 'wxMenuBar', 'wxToolBar']:
669 wxLogMessage('No view for this element (yet)')
670 return
671 if not treeObj.name:
672 wxLogError("Can't display a noname element!")
673 return
674 # Show item in bold
675 if testWin:
676 self.SetItemBold(testWin.item, false)
677 self.SetItemBold(item)
678 self.CreateTestWin(item)
679 # Double-click on Linux
680 def OnItemActivated(self, evt):
681 if evt.GetItem() != self.root:
682 self.ShowTestWindow(evt.GetItem())
683 # Double-click on Windows
684 def OnDClick(self, evt):
685 item, flags = self.HitTest(evt.GetPosition())
686 if flags in [wxTREE_HITTEST_ONITEMBUTTON, wxTREE_HITTEST_ONITEMLABEL]:
687 if item != self.root: self.ShowTestWindow(item)
688 else:
689 evt.Skip()
690 # (re)create test window
691 def CreateTestWin(self, item):
692 global testWin
693 wxBeginBusyCursor()
694 # Create a window with this resource
695 xxx = self.GetPyData(item).treeObject()
696 # Close old window, remember where it was
697 highLight = None
698 if testWin:
699 pos = testWin.GetPosition()
700 if item == testWin.item:
701 # Remember highlight if same top-level window
702 if testWin.highLight:
703 highLight = testWin.highLight.item
704 # !!! if 0 is removed, refresh is broken (notebook not deleted?)
705 if xxx.className == 'wxPanel':
706 if testWin.highLight:
707 testWin.pendingHighLight = highLight
708 testWin.highLight.Remove()
709 testWin.panel.Destroy()
710 testWin.panel = None
711 else:
712 testWin.Destroy()
713 testWin = None
714 else:
715 testWin.Destroy()
716 testWin = None
717 else:
718 pos = testWinPos
719 # Save in memory FS
720 memFile = MemoryFile('xxx.xrc')
721 # Create partial XML file - faster for big files
722
723 dom = minidom.Document()
724 mainNode = dom.createElement('resource')
725 dom.appendChild(mainNode)
726
727 # Remove temporarily from old parent
728 elem = xxx.element
729 parent = elem.parentNode
730 next = elem.nextSibling
731 parent.replaceChild(self.dummyNode, elem)
732 # Append to new DOM, write it
733 mainNode.appendChild(elem)
734 dom.writexml(memFile)
735 # Put back in place
736 mainNode.removeChild(elem)
737 dom.unlink()
738 parent.replaceChild(elem, self.dummyNode)
739 memFile.close() # write to wxMemoryFS
740 res = wxXmlResource('')
741 res.Load('memory:xxx.xrc')
742 if xxx.className == 'wxFrame':
743 # Create new frame
744 testWin = wxPreFrame()
745 res.LoadFrame(testWin, frame, xxx.name)
746 # Create status bar
747 testWin.CreateStatusBar()
748 testWin.panel = testWin
749 testWin.SetPosition(pos)
750 testWin.Show(true)
751 elif xxx.className == 'wxPanel':
752 # Create new frame
753 if not testWin:
754 testWin = wxFrame(frame, -1, 'Panel: ' + xxx.name, pos=pos)
755 testWin.panel = res.LoadPanel(testWin, xxx.name)
756 testWin.SetClientSize(testWin.panel.GetSize())
757 testWin.Show(true)
758 elif xxx.className == 'wxDialog':
759 # Create new frame
760 testWin = res.LoadDialog(None, xxx.name)
761 testWin.panel = testWin
762 testWin.Layout()
763 testWin.SetPosition(pos)
764 testWin.Show(true)
765 elif xxx.className == 'wxMenuBar':
766 testWin = wxFrame(frame, -1, 'MenuBar: ' + xxx.name, pos=pos)
767 testWin.panel = None
768 # Set status bar to display help
769 testWin.CreateStatusBar()
770 testWin.menuBar = res.LoadMenuBar(xxx.name)
771 testWin.SetMenuBar(testWin.menuBar)
772 testWin.Show(true)
773 elif xxx.className == 'wxToolBar':
774 testWin = wxFrame(frame, -1, 'ToolBar: ' + xxx.name, pos=pos)
775 testWin.panel = None
776 # Set status bar to display help
777 testWin.CreateStatusBar()
778 testWin.toolBar = res.LoadToolBar(testWin, xxx.name)
779 testWin.SetToolBar(testWin.toolBar)
780 testWin.Show(true)
781 wxMemoryFSHandler_RemoveFile('xxx.xrc')
782 testWin.item = item
783 EVT_CLOSE(testWin, self.OnCloseTestWin)
784 EVT_BUTTON(testWin, wxID_OK, self.OnCloseTestWin)
785 EVT_BUTTON(testWin, wxID_CANCEL, self.OnCloseTestWin)
786 testWin.highLight = None
787 if highLight and not tree.pendingHighLight:
788 self.HighLight(highLight)
789 wxEndBusyCursor()
790
791 def OnCloseTestWin(self, evt):
792 global testWin, testWinPos
793 self.SetItemBold(testWin.item, false)
794 testWinPos = testWin.GetPosition()
795 testWin.Destroy()
796 testWin = None
797
798 # Return item index in parent
799 def ItemIndex(self, parent, item):
800 i = 0
801 it, cookie = self.GetFirstChild(parent, 0)
802 while it != item:
803 i += 1
804 it, cookie = self.GetNextChild(parent, cookie)
805 return i
806
807 # True if next item should be inserted after current (vs. appended to it)
808 def NeedInsert(self, item):
809 xxx = self.GetPyData(item)
810 if item == self.root: return false # root item
811 if xxx.hasChildren and not self.GetChildrenCount(item, false):
812 return false
813 return not (self.IsExpanded(item) and self.GetChildrenCount(item, false))
814
815 # Pull-down
816 def OnRightDown(self, evt):
817 # select this item
818 pt = evt.GetPosition();
819 item, flags = self.HitTest(pt)
820 if item.Ok() and flags & wxTREE_HITTEST_ONITEM:
821 self.SelectItem(item)
822
823 # Setup menu
824 menu = wxMenu()
825
826 item = self.selection
827 if not item:
828 menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand tree')
829 menu.Append(pullDownMenu.ID_COLLAPSE, 'Collapse', 'Collapse tree')
830 else:
831 self.ctrl = evt.ControlDown() # save Ctrl state
832 self.shift = evt.ShiftDown() # and Shift too
833 m = wxMenu() # create menu
834 if self.ctrl:
835 needInsert = true
836 else:
837 needInsert = self.NeedInsert(item)
838 if item == self.root or needInsert and self.GetItemParent(item) == self.root:
839 m.Append(pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel')
840 m.Append(pullDownMenu.ID_NEW_DIALOG, 'Dialog', 'Create dialog')
841 m.Append(pullDownMenu.ID_NEW_FRAME, 'Frame', 'Create frame')
842 m.AppendSeparator()
843 m.Append(pullDownMenu.ID_NEW_TOOL_BAR, 'ToolBar', 'Create toolbar')
844 m.Append(pullDownMenu.ID_NEW_MENU_BAR, 'MenuBar', 'Create menubar')
845 m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu')
846 else:
847 xxx = self.GetPyData(item).treeObject()
848 # Check parent for possible child nodes if inserting sibling
849 if needInsert: xxx = xxx.parent
850 if xxx.__class__ == xxxMenuBar:
851 m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu')
852 elif xxx.__class__ in [xxxToolBar, xxxTool] or \
853 xxx.__class__ == xxxSeparator and xxx.parent.__class__ == xxxToolBar:
854 SetMenu(m, pullDownMenu.toolBarControls)
855 elif xxx.__class__ in [xxxMenu, xxxMenuItem]:
856 SetMenu(m, pullDownMenu.menuControls)
857 else:
858 SetMenu(m, pullDownMenu.controls)
859 if xxx.__class__ == xxxNotebook:
860 m.Enable(m.FindItem('sizer'), false)
861 elif not (xxx.isSizer or xxx.parent and xxx.parent.isSizer):
862 m.Enable(pullDownMenu.ID_NEW_SPACER, false)
863 # Select correct label for create menu
864 if not needInsert:
865 if self.shift:
866 menu.AppendMenu(wxNewId(), 'Insert Child', m,
867 'Create child object as the first child')
868 else:
869 menu.AppendMenu(wxNewId(), 'Append Child', m,
870 'Create child object as the last child')
871 else:
872 if self.shift:
873 menu.AppendMenu(wxNewId(), 'Create Sibling', m,
874 'Create sibling before selected object')
875 else:
876 menu.AppendMenu(wxNewId(), 'Create Sibling', m,
877 'Create sibling after selected object')
878 menu.AppendSeparator()
879 # Not using standart IDs because we don't want to show shortcuts
880 menu.Append(wxID_CUT, 'Cut', 'Cut to the clipboard')
881 menu.Append(wxID_COPY, 'Copy', 'Copy to the clipboard')
882 if self.ctrl and item != tree.root:
883 menu.Append(pullDownMenu.ID_PASTE_SIBLING, 'Paste Sibling',
884 'Paste from the clipboard as a sibling')
885 else:
886 menu.Append(wxID_PASTE, 'Paste', 'Paste from the clipboard')
887 menu.Append(pullDownMenu.ID_DELETE,
888 'Delete', 'Delete object')
889 if self.ItemHasChildren(item):
890 menu.AppendSeparator()
891 menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand subtree')
892 menu.Append(pullDownMenu.ID_COLLAPSE, 'Collapse', 'Collapse subtree')
893 self.PopupMenu(menu, evt.GetPosition())
894 menu.Destroy()
895
896 # Apply changes
897 def Apply(self, xxx, item):
898 panel.Apply()
899 # Update tree view
900 xxx = xxx.treeObject()
901 if xxx.hasName and self.GetItemText(item) != xxx.name:
902 self.SetItemText(item, xxx.treeName())
903 # Change tree icon for sizers
904 if isinstance(xxx, xxxBoxSizer):
905 self.SetItemImage(item, xxx.treeImage())
906 # Set global modified state
907 frame.modified = true
908
909 class PullDownMenu:
910 ID_NEW_PANEL = wxNewId()
911 ID_NEW_DIALOG = wxNewId()
912 ID_NEW_FRAME = wxNewId()
913 ID_NEW_TOOL_BAR = wxNewId()
914 ID_NEW_TOOL = wxNewId()
915 ID_NEW_MENU_BAR = wxNewId()
916 ID_NEW_MENU = wxNewId()
917
918 ID_NEW_STATIC_TEXT = wxNewId()
919 ID_NEW_TEXT_CTRL = wxNewId()
920
921 ID_NEW_BUTTON = wxNewId()
922 ID_NEW_BITMAP_BUTTON = wxNewId()
923 ID_NEW_RADIO_BUTTON = wxNewId()
924 ID_NEW_SPIN_BUTTON = wxNewId()
925
926 ID_NEW_STATIC_BOX = wxNewId()
927 ID_NEW_CHECK_BOX = wxNewId()
928 ID_NEW_RADIO_BOX = wxNewId()
929 ID_NEW_COMBO_BOX = wxNewId()
930 ID_NEW_LIST_BOX = wxNewId()
931
932 ID_NEW_STATIC_LINE = wxNewId()
933 ID_NEW_STATIC_BITMAP = wxNewId()
934 ID_NEW_CHOICE = wxNewId()
935 ID_NEW_SLIDER = wxNewId()
936 ID_NEW_GAUGE = wxNewId()
937 ID_NEW_SCROLL_BAR = wxNewId()
938 ID_NEW_TREE_CTRL = wxNewId()
939 ID_NEW_LIST_CTRL = wxNewId()
940 ID_NEW_CHECK_LIST = wxNewId()
941 ID_NEW_NOTEBOOK = wxNewId()
942 ID_NEW_HTML_WINDOW = wxNewId()
943 ID_NEW_CALENDAR = wxNewId()
944
945 ID_NEW_BOX_SIZER = wxNewId()
946 ID_NEW_STATIC_BOX_SIZER = wxNewId()
947 ID_NEW_GRID_SIZER = wxNewId()
948 ID_NEW_FLEX_GRID_SIZER = wxNewId()
949 ID_NEW_SPACER = wxNewId()
950 ID_NEW_TOOL_BAR = wxNewId()
951 ID_NEW_TOOL = wxNewId()
952 ID_NEW_MENU = wxNewId()
953 ID_NEW_MENU_ITEM = wxNewId()
954 ID_NEW_SEPARATOR = wxNewId()
955 ID_NEW_LAST = wxNewId()
956 ID_EXPAND = wxNewId()
957 ID_COLLAPSE = wxNewId()
958 ID_PASTE_SIBLING = wxNewId()
959
960 def __init__(self, parent):
961 self.ID_DELETE = parent.ID_DELETE
962 EVT_MENU_RANGE(parent, self.ID_NEW_PANEL,
963 self.ID_NEW_LAST, parent.OnCreate)
964 EVT_MENU(parent, self.ID_COLLAPSE, parent.OnCollapse)
965 EVT_MENU(parent, self.ID_EXPAND, parent.OnExpand)
966 EVT_MENU(parent, self.ID_PASTE_SIBLING, parent.OnPaste)
967 # We connect to tree, but process in frame
968 EVT_MENU_HIGHLIGHT_ALL(tree, parent.OnPullDownHighlight)
969
970 ################################################################################
971
972 # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font
973 class ScrolledMessageDialog(wxDialog):
974 def __init__(self, parent, msg, caption, pos = wxDefaultPosition, size = (500,300)):
975 from wxPython.lib.layoutf import Layoutf
976 wxDialog.__init__(self, parent, -1, caption, pos, size)
977 text = wxTextCtrl(self, -1, msg, wxDefaultPosition,
978 wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY)
979 text.SetFont(modernFont)
980 dc = wxWindowDC(text)
981 w, h = dc.GetTextExtent(' ')
982 ok = wxButton(self, wxID_OK, "OK")
983 text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok)))
984 text.SetSize((w * 80 + 30, h * 40))
985 ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,)))
986 self.SetAutoLayout(TRUE)
987 self.Fit()
988 self.CenterOnScreen(wxBOTH)
989
990 ################################################################################
991
992 class Frame(wxFrame):
993 def __init__(self, pos, size):
994 global frame
995 frame = self
996 wxFrame.__init__(self, None, -1, '', pos, size)
997 self.CreateStatusBar()
998 icon = wxIcon(os.path.join(sys.path[0], 'xrced.ico'), wxBITMAP_TYPE_ICO)
999 self.SetIcon(icon)
1000
1001 # Defaults
1002 self.sashPos = 100
1003 self.panelX = self.panelY = -1
1004 self.panelWidth = 300
1005 self.panelHeight = 200
1006
1007 # Idle flas
1008 self.inIdle = false
1009
1010 # Make menus
1011 menuBar = wxMenuBar()
1012
1013 menu = wxMenu()
1014 menu.Append(wxID_NEW, '&New\tCtrl-N', 'New file')
1015 menu.Append(wxID_OPEN, '&Open...\tCtrl-O', 'Open XRC file')
1016 menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file')
1017 menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name')
1018 menu.AppendSeparator()
1019 menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application')
1020 menuBar.Append(menu, '&File')
1021
1022 menu = wxMenu()
1023 menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo')
1024 menu.Append(wxID_REDO, '&Redo\tCtrl-Y', 'Redo')
1025 menu.AppendSeparator()
1026 menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard')
1027 menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard')
1028 menu.Append(wxID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard')
1029 self.ID_DELETE = wxNewId()
1030 menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object')
1031 menuBar.Append(menu, '&Edit')
1032
1033 menu = wxMenu()
1034 self.ID_EMBED_PANEL = wxNewId()
1035 menu.Append(self.ID_EMBED_PANEL, '&Embed Panel',
1036 'Toggle embedding properties panel in the main window', true)
1037 menu.Check(self.ID_EMBED_PANEL, conf.embedPanel)
1038 menu.AppendSeparator()
1039 self.ID_TEST = wxNewId()
1040 menu.Append(self.ID_TEST, '&Test\tF5', 'Test window')
1041 self.ID_REFRESH = wxNewId()
1042 menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window')
1043 self.ID_AUTO_REFRESH = wxNewId()
1044 menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A',
1045 'Toggle auto-refresh mode', true)
1046 menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
1047 menuBar.Append(menu, '&View')
1048
1049 menu = wxMenu()
1050 menu.Append(wxID_ABOUT, '&About...', 'About XCRed')
1051 self.ID_README = wxNewId()
1052 menu.Append(self.ID_README, '&Readme...', 'View the README file')
1053 if debug:
1054 self.ID_DEBUG_CMD = wxNewId()
1055 menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line')
1056 EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD)
1057 menuBar.Append(menu, '&Help')
1058
1059 self.menuBar = menuBar
1060 self.SetMenuBar(menuBar)
1061
1062 # Create toolbar
1063 tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT)
1064 tb.SetToolBitmapSize((24, 23))
1065 tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file')
1066 tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file')
1067 tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file')
1068 tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL))
1069 tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut')
1070 tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy')
1071 tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste')
1072 tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL))
1073 tb.AddSimpleTool(self.ID_TEST, images.getTestBitmap(), 'Test', 'Test window')
1074 tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(),
1075 'Refresh', 'Refresh view')
1076 tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(),
1077 'Auto-refresh', 'Toggle auto-refresh mode', true)
1078 if wxGetOsVersion()[1] == 1:
1079 tb.AddSeparator() # otherwise auto-refresh sticks in status line
1080 tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
1081 tb.Realize()
1082 self.tb = tb
1083
1084 # File
1085 EVT_MENU(self, wxID_NEW, self.OnNew)
1086 EVT_MENU(self, wxID_OPEN, self.OnOpen)
1087 EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs)
1088 EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs)
1089 EVT_MENU(self, wxID_EXIT, self.OnExit)
1090 # Edit
1091 EVT_MENU(self, wxID_UNDO, self.OnUndo)
1092 EVT_MENU(self, wxID_REDO, self.OnRedo)
1093 EVT_MENU(self, wxID_CUT, self.OnCut)
1094 EVT_MENU(self, wxID_COPY, self.OnCopy)
1095 EVT_MENU(self, wxID_PASTE, self.OnPaste)
1096 EVT_MENU(self, self.ID_DELETE, self.OnDelete)
1097 # View
1098 EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel)
1099 EVT_MENU(self, self.ID_TEST, self.OnTest)
1100 EVT_MENU(self, self.ID_REFRESH, self.OnRefresh)
1101 EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh)
1102 # Help
1103 EVT_MENU(self, wxID_ABOUT, self.OnAbout)
1104 EVT_MENU(self, self.ID_README, self.OnReadme)
1105
1106 # Update events
1107 EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI)
1108 EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI)
1109 EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI)
1110 EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI)
1111 EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI)
1112 EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI)
1113
1114 # Build interface
1115 sizer = wxBoxSizer(wxVERTICAL)
1116 sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND)
1117 splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH)
1118 self.splitter = splitter
1119 splitter.SetMinimumPaneSize(100)
1120 # Create tree
1121 global tree
1122 tree = XML_Tree(splitter, -1)
1123 sys.modules['xxx'].tree = tree
1124 # !!! frame styles are broken
1125 # Miniframe for not embedded mode
1126 miniFrame = wxFrame(self, -1, 'Properties Panel',
1127 (conf.panelX, conf.panelY),
1128 (conf.panelWidth, conf.panelHeight))
1129 self.miniFrame = miniFrame
1130 sizer2 = wxBoxSizer()
1131 miniFrame.SetAutoLayout(true)
1132 miniFrame.SetSizer(sizer2)
1133 EVT_CLOSE(self.miniFrame, self.OnCloseMiniFrame)
1134 # Create panel for parameters
1135 global panel
1136 if conf.embedPanel:
1137 panel = Panel(splitter)
1138 # Set plitter windows
1139 splitter.SplitVertically(tree, panel, conf.sashPos)
1140 else:
1141 panel = Panel(miniFrame)
1142 sizer2.Add(panel, 1, wxEXPAND)
1143 miniFrame.Show(true)
1144 splitter.Initialize(tree)
1145 sizer.Add(splitter, 1, wxEXPAND)
1146 self.SetAutoLayout(true)
1147 self.SetSizer(sizer)
1148
1149 # Init pull-down menu data
1150 global pullDownMenu
1151 pullDownMenu = PullDownMenu(self)
1152 # Mapping from IDs to element names
1153 self.createMap = {
1154 pullDownMenu.ID_NEW_PANEL: 'wxPanel',
1155 pullDownMenu.ID_NEW_DIALOG: 'wxDialog',
1156 pullDownMenu.ID_NEW_FRAME: 'wxFrame',
1157 pullDownMenu.ID_NEW_TOOL_BAR: 'wxToolBar',
1158 pullDownMenu.ID_NEW_TOOL: 'tool',
1159 pullDownMenu.ID_NEW_MENU_BAR: 'wxMenuBar',
1160 pullDownMenu.ID_NEW_MENU: 'wxMenu',
1161 pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem',
1162 pullDownMenu.ID_NEW_SEPARATOR: 'separator',
1163
1164 pullDownMenu.ID_NEW_STATIC_TEXT: 'wxStaticText',
1165 pullDownMenu.ID_NEW_TEXT_CTRL: 'wxTextCtrl',
1166
1167 pullDownMenu.ID_NEW_BUTTON: 'wxButton',
1168 pullDownMenu.ID_NEW_BITMAP_BUTTON: 'wxBitmapButton',
1169 pullDownMenu.ID_NEW_RADIO_BUTTON: 'wxRadioButton',
1170 pullDownMenu.ID_NEW_SPIN_BUTTON: 'wxSpinButton',
1171
1172 pullDownMenu.ID_NEW_STATIC_BOX: 'wxStaticBox',
1173 pullDownMenu.ID_NEW_CHECK_BOX: 'wxCheckBox',
1174 pullDownMenu.ID_NEW_RADIO_BOX: 'wxRadioBox',
1175 pullDownMenu.ID_NEW_COMBO_BOX: 'wxComboBox',
1176 pullDownMenu.ID_NEW_LIST_BOX: 'wxListBox',
1177
1178 pullDownMenu.ID_NEW_STATIC_LINE: 'wxStaticLine',
1179 pullDownMenu.ID_NEW_STATIC_BITMAP: 'wxStaticBitmap',
1180 pullDownMenu.ID_NEW_CHOICE: 'wxChoice',
1181 pullDownMenu.ID_NEW_SLIDER: 'wxSlider',
1182 pullDownMenu.ID_NEW_GAUGE: 'wxGauge',
1183 pullDownMenu.ID_NEW_SCROLL_BAR: 'wxScrollBar',
1184 pullDownMenu.ID_NEW_TREE_CTRL: 'wxTreeCtrl',
1185 pullDownMenu.ID_NEW_LIST_CTRL: 'wxListCtrl',
1186 pullDownMenu.ID_NEW_CHECK_LIST: 'wxCheckList',
1187 pullDownMenu.ID_NEW_NOTEBOOK: 'wxNotebook',
1188 pullDownMenu.ID_NEW_HTML_WINDOW: 'wxHtmlWindow',
1189 pullDownMenu.ID_NEW_CALENDAR: 'wxCalendar',
1190
1191 pullDownMenu.ID_NEW_BOX_SIZER: 'wxBoxSizer',
1192 pullDownMenu.ID_NEW_STATIC_BOX_SIZER: 'wxStaticBoxSizer',
1193 pullDownMenu.ID_NEW_GRID_SIZER: 'wxGridSizer',
1194 pullDownMenu.ID_NEW_FLEX_GRID_SIZER: 'wxFlexGridSizer',
1195 pullDownMenu.ID_NEW_SPACER: 'spacer',
1196 }
1197 pullDownMenu.controls = [
1198 ['control', 'Various controls',
1199 (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'),
1200 (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'),
1201 (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'),
1202 (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'),
1203 (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'),
1204 (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'),
1205 (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'),
1206 (pullDownMenu.ID_NEW_TREE_CTRL, 'TreeCtrl', 'Create tree control'),
1207 (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'),
1208 (pullDownMenu.ID_NEW_HTML_WINDOW, 'HtmlWindow', 'Create HTML window'),
1209 (pullDownMenu.ID_NEW_CALENDAR, 'Calendar', 'Create calendar control'),
1210 (pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel'),
1211 (pullDownMenu.ID_NEW_NOTEBOOK, 'Notebook', 'Create notebook control'),
1212 ],
1213 ['button', 'Buttons',
1214 (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'),
1215 (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'),
1216 (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'),
1217 (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'),
1218 ],
1219 ['box', 'Boxes',
1220 (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'),
1221 (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'),
1222 (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'),
1223 (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'),
1224 (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'),
1225 (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox',
1226 'Create check list control'),
1227 ],
1228 ['sizer', 'Sizers',
1229 (pullDownMenu.ID_NEW_BOX_SIZER, 'BoxSizer', 'Create box sizer'),
1230 (pullDownMenu.ID_NEW_STATIC_BOX_SIZER, 'StaticBoxSizer',
1231 'Create static box sizer'),
1232 (pullDownMenu.ID_NEW_GRID_SIZER, 'GridSizer', 'Create grid sizer'),
1233 (pullDownMenu.ID_NEW_FLEX_GRID_SIZER, 'FlexGridSizer',
1234 'Create flexgrid sizer'),
1235 (pullDownMenu.ID_NEW_SPACER, 'Spacer', 'Create spacer'),
1236 ]
1237 ]
1238 pullDownMenu.menuControls = [
1239 (pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu'),
1240 (pullDownMenu.ID_NEW_MENU_ITEM, 'MenuItem', 'Create menu item'),
1241 (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'),
1242 ]
1243 pullDownMenu.toolBarControls = [
1244 (pullDownMenu.ID_NEW_TOOL, 'Tool', 'Create tool'),
1245 (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'),
1246 ['control', 'Various controls',
1247 (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'),
1248 (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'),
1249 (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'),
1250 (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'),
1251 (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'),
1252 (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'),
1253 (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'),
1254 (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'),
1255 ],
1256 ['button', 'Buttons',
1257 (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'),
1258 (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'),
1259 (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'),
1260 (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'),
1261 ],
1262 ['box', 'Boxes',
1263 (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'),
1264 (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'),
1265 (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'),
1266 (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'),
1267 (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'),
1268 (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox',
1269 'Create check list control'),
1270 ],
1271 ]
1272
1273 # Initialize
1274 self.Clear()
1275
1276 # Other events
1277 EVT_IDLE(self, self.OnIdle)
1278 EVT_CLOSE(self, self.OnCloseWindow)
1279
1280 def OnNew(self, evt):
1281 self.Clear()
1282
1283 def OnOpen(self, evt):
1284 if not self.AskSave(): return
1285 dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile),
1286 '', '*.xrc', wxOPEN | wxCHANGE_DIR)
1287 if dlg.ShowModal() == wxID_OK:
1288 path = dlg.GetPath()
1289 self.SetStatusText('Loading...')
1290 wxYield()
1291 wxBeginBusyCursor()
1292 try:
1293 self.Open(path)
1294 self.SetStatusText('Data loaded')
1295 except:
1296 self.SetStatusText('Failed')
1297 raise
1298 wxEndBusyCursor()
1299 dlg.Destroy()
1300
1301 def OnSaveOrSaveAs(self, evt):
1302 if evt.GetId() == wxID_SAVEAS or not self.dataFile:
1303 if self.dataFile: defaultName = ''
1304 else: defaultName = 'UNTITLED.xrc'
1305 dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile),
1306 defaultName, '*.xrc',
1307 wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR)
1308 if dlg.ShowModal() == wxID_OK:
1309 path = dlg.GetPath()
1310 dlg.Destroy()
1311 else:
1312 dlg.Destroy()
1313 return
1314 else:
1315 path = self.dataFile
1316 self.SetStatusText('Saving...')
1317 wxYield()
1318 wxBeginBusyCursor()
1319 try:
1320 self.Save(path)
1321 self.dataFile = path
1322 self.SetStatusText('Data saved')
1323 except IOError:
1324 self.SetStatusText('Failed')
1325 wxEndBusyCursor()
1326
1327 def OnExit(self, evt):
1328 self.Close()
1329
1330 def OnUndo(self, evt):
1331 print '*** being implemented'
1332 return
1333 print self.lastOp, self.undo
1334 if self.lastOp == 'DELETE':
1335 parent, prev, elem = self.undo
1336 if prev.IsOk():
1337 xxx = MakeXXXFromDOM(tree.GetPyData(parent).treeObject(), elem)
1338 item = tree.InsertItem( parent, prev, xxx.treeObject().className,
1339 data=wxTreeItemData(xxx) )
1340
1341 def OnRedo(self, evt):
1342 print '*** being implemented'
1343
1344 def OnCut(self, evt):
1345 selected = tree.selection
1346 if not selected: return # key pressed event
1347 # Undo info
1348 self.lastOp = 'CUT'
1349 self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)]
1350 # Delete testWin?
1351 global testWin
1352 if testWin:
1353 # If deleting top-level item, delete testWin
1354 if selected == testWin.item:
1355 testWin.Destroy()
1356 testWin = None
1357 else:
1358 # Remove highlight, update testWin
1359 if not tree.IsHighlatable(selected):
1360 if testWin.highLight: testWin.highLight.Remove()
1361 tree.needUpdate = true
1362 self.clipboard = tree.RemoveLeaf(selected)
1363 tree.pendingHighLight = None
1364 tree.Unselect()
1365 panel.Clear()
1366 self.modified = true
1367 self.SetStatusText('Removed to clipboard')
1368
1369 def OnCopy(self, evt):
1370 selected = tree.selection
1371 if not selected: return # key pressed event
1372 xxx = tree.GetPyData(selected)
1373 self.clipboard = xxx.element.cloneNode(true)
1374 self.SetStatusText('Copied')
1375
1376 def OnPaste(self, evt):
1377 selected = tree.selection
1378 if not selected: return # key pressed event
1379 # For pasting with Ctrl pressed
1380 if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = false
1381 else: appendChild = not tree.NeedInsert(selected)
1382 xxx = tree.GetPyData(selected)
1383 if not appendChild:
1384 # If has next item, insert, else append to parent
1385 nextItem = tree.GetNextSibling(selected)
1386 if nextItem.IsOk():
1387 # Insert before nextItem
1388 parentLeaf = tree.GetItemParent(selected)
1389 else: # last child: change selected to parent
1390 appendChild = true
1391 selected = tree.GetItemParent(selected)
1392 # Expanded container (must have children)
1393 elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, false):
1394 appendChild = false
1395 nextItem = tree.GetFirstChild(selected, 0)[0]
1396 parentLeaf = selected
1397 # Parent should be tree element or None
1398 if appendChild:
1399 parent = tree.GetPyData(selected)
1400 else:
1401 parent = tree.GetPyData(parentLeaf)
1402 if parent.hasChild: parent = parent.child
1403
1404 # Create a copy of clipboard element
1405 elem = self.clipboard.cloneNode(true)
1406 # Tempopary xxx object to test things
1407 xxx = MakeXXXFromDOM(parent, elem)
1408
1409 # Check compatibility
1410 error = false
1411 # Top-level
1412 x = xxx.treeObject()
1413 if x.__class__ in [xxxDialog, xxxFrame, xxxMenuBar, xxxToolBar]:
1414 if parent.__class__ != xxxMainNode: error = true
1415 elif x.__class__ == xxxPanel and parent.__class__ == xxxMainNode:
1416 pass
1417 elif x.__class__ == xxxSpacer:
1418 if not parent.isSizer: error = true
1419 elif x.__class__ == xxxSeparator:
1420 if not parent.__class__ in [xxxMenu, xxxToolBar]: error = true
1421 elif x.__class__ == xxxTool:
1422 if parent.__class__ != xxxToolBar: error = true
1423 elif x.__class__ == xxxMenuItem:
1424 if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = true
1425 elif x.isSizer and parent.__class__ == xxxNotebook: error = true
1426 else: # normal controls can be almost anywhere
1427 if parent.__class__ == xxxMainNode or \
1428 parent.__class__ in [xxxMenuBar, xxxMenu]: error = true
1429 if error:
1430 if parent.__class__ == xxxMainNode: parentClass = 'root'
1431 else: parentClass = parent.className
1432 wxLogError('Incompatible parent/child: parent is %s, child is %s!' %
1433 (parentClass, x.className))
1434 return
1435
1436 # Check parent and child relationships.
1437 # If parent is sizer or notebook, child is of wrong class or
1438 # parent is normal window, child is child container then detach child.
1439 isChildContainer = isinstance(xxx, xxxChildContainer)
1440 if isChildContainer and \
1441 ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
1442 (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
1443 not (parent.isSizer or isinstance(parent, xxxNotebook))):
1444 elem.removeChild(xxx.child.element) # detach child
1445 elem.unlink() # delete child container
1446 elem = xxx.child.element # replace
1447 # This may help garbage collection
1448 xxx.child.parent = None
1449 isChildContainer = false
1450 # Parent is sizer or notebook, child is not child container
1451 if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer):
1452 # Create sizer item element
1453 sizerItemElem = MakeEmptyDOM('sizeritem')
1454 sizerItemElem.appendChild(elem)
1455 elem = sizerItemElem
1456 elif isinstance(parent, xxxNotebook) and not isChildContainer:
1457 pageElem = MakeEmptyDOM('notebookpage')
1458 pageElem.appendChild(elem)
1459 elem = pageElem
1460 xxx = MakeXXXFromDOM(parent, elem)
1461 # Figure out if we must append a new child or sibling
1462 if appendChild:
1463 parent.element.appendChild(elem)
1464 newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(),
1465 data=wxTreeItemData(xxx))
1466 else:
1467 node = tree.GetPyData(nextItem).element
1468 parent.element.insertBefore(elem, node)
1469 # Inserting before is difficult, se we insert after or first child
1470 index = tree.ItemIndex(parentLeaf, nextItem)
1471 newItem = tree.InsertItemBefore(parentLeaf, index,
1472 xxx.treeName(), image=xxx.treeImage())
1473 tree.SetPyData(newItem, xxx)
1474 # newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(),
1475 # image=xxx.treeImage(), data=wxTreeItemData(xxx))
1476 # Add children items
1477 if xxx.hasChildren:
1478 treeObj = xxx.treeObject()
1479 for n in treeObj.element.childNodes:
1480 if IsObject(n):
1481 tree.AddNode(newItem, treeObj, n)
1482 # Scroll to show new item
1483 tree.EnsureVisible(newItem)
1484 tree.SelectItem(newItem)
1485 if not tree.IsVisible(newItem):
1486 tree.ScrollTo(newItem)
1487 tree.Refresh()
1488 # Update view?
1489 if testWin and tree.IsHighlatable(newItem):
1490 if conf.autoRefresh:
1491 tree.needUpdate = true
1492 tree.pendingHighLight = newItem
1493 else:
1494 tree.pendingHighLight = None
1495 self.modified = true
1496 self.SetStatusText('Pasted')
1497
1498 def OnDelete(self, evt):
1499 selected = tree.selection
1500 if not selected: return # key pressed event
1501 # Undo info
1502 self.lastOp = 'DELETE'
1503 self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)]
1504 # Delete testWin?
1505 global testWin
1506 if testWin:
1507 # If deleting top-level item, delete testWin
1508 if selected == testWin.item:
1509 testWin.Destroy()
1510 testWin = None
1511 else:
1512 # Remove highlight, update testWin
1513 if not tree.IsHighlatable(selected):
1514 if testWin.highLight: testWin.highLight.Remove()
1515 tree.needUpdate = true
1516 xnode = tree.RemoveLeaf(selected)
1517 # !!! cloneNode is broken, or something is wrong
1518 # self.undo.append(xnode.cloneNode(true))
1519 xnode.unlink()
1520 tree.pendingHighLight = None
1521 tree.Unselect()
1522 panel.Clear()
1523 self.modified = true
1524 self.SetStatusText('Deleted')
1525
1526 def OnEmbedPanel(self, evt):
1527 conf.embedPanel = evt.IsChecked()
1528 if conf.embedPanel:
1529 # Remember last dimentions
1530 self.panelWidth, self.panelHeight = panel.GetSize()
1531 panel.Reparent(self.splitter)
1532 self.miniFrame.GetSizer().RemoveWindow(panel)
1533 self.splitter.SplitVertically(tree, panel, self.sashPos)
1534 self.miniFrame.Show(false)
1535 else:
1536 self.sashPos = self.splitter.GetSashPosition()
1537 self.splitter.Unsplit(panel)
1538 sizer = self.miniFrame.GetSizer()
1539 panel.Reparent(self.miniFrame)
1540 panel.Show(true)
1541 sizer.Add(panel, 1, wxEXPAND)
1542 self.miniFrame.Show(true)
1543 self.miniFrame.SetSize((self.panelWidth, self.panelHeight))
1544
1545 def OnTest(self, evt):
1546 if not tree.selection: return # key pressed event
1547 tree.ShowTestWindow(tree.selection)
1548
1549 def OnRefresh(self, evt):
1550 # If modified, apply first
1551 selection = tree.selection
1552 if selection:
1553 xxx = tree.GetPyData(selection)
1554 if xxx and panel.IsModified():
1555 tree.Apply(xxx, selection)
1556 if testWin:
1557 # (re)create
1558 tree.CreateTestWin(testWin.item)
1559 tree.needUpdate = false
1560
1561 def OnAutoRefresh(self, evt):
1562 conf.autoRefresh = evt.IsChecked()
1563 self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
1564 self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
1565
1566 def OnAbout(self, evt):
1567 str = '%s %s\n\nRoman Rolinsky <rolinsky@mema.ucl.ac.be>' % \
1568 (progname, version)
1569 dlg = wxMessageDialog(self, str, 'About ' + progname, wxOK | wxCENTRE)
1570 dlg.ShowModal()
1571 dlg.Destroy()
1572
1573 def OnReadme(self, evt):
1574 text = open(os.path.join(sys.path[0], 'README'), 'r').read()
1575 dlg = ScrolledMessageDialog(self, text, "XRCed README")
1576 dlg.ShowModal()
1577 dlg.Destroy()
1578
1579
1580 # Simple emulation of python command line
1581 def OnDebugCMD(self, evt):
1582 import traceback
1583 while 1:
1584 try:
1585 exec raw_input('C:\> ')
1586 except EOFError:
1587 print '^D'
1588 break
1589 except:
1590 (etype, value, tb) =sys.exc_info()
1591 tblist =traceback.extract_tb(tb)[1:]
1592 msg =string.join(traceback.format_exception_only(etype, value)
1593 +traceback.format_list(tblist))
1594 print msg
1595
1596 def OnCreate(self, evt):
1597 selected = tree.selection
1598 if tree.ctrl: appendChild = false
1599 else: appendChild = not tree.NeedInsert(selected)
1600 xxx = tree.GetPyData(selected)
1601 if not appendChild:
1602 # If insert before
1603 if tree.shift:
1604 # If has previous item, insert after it, else append to parent
1605 nextItem = selected
1606 parentLeaf = tree.GetItemParent(selected)
1607 else:
1608 # If has next item, insert, else append to parent
1609 nextItem = tree.GetNextSibling(selected)
1610 if nextItem.IsOk():
1611 # Insert before nextItem
1612 parentLeaf = tree.GetItemParent(selected)
1613 else: # last child: change selected to parent
1614 appendChild = true
1615 selected = tree.GetItemParent(selected)
1616 # Expanded container (must have children)
1617 elif tree.shift and tree.IsExpanded(selected) \
1618 and tree.GetChildrenCount(selected, false):
1619 appendChild = false
1620 nextItem = tree.GetFirstChild(selected, 0)[0]
1621 parentLeaf = selected
1622 # Parent should be tree element or None
1623 if appendChild:
1624 parent = tree.GetPyData(selected)
1625 else:
1626 parent = tree.GetPyData(parentLeaf)
1627 if parent.hasChild: parent = parent.child
1628
1629 # Create element
1630 className = self.createMap[evt.GetId()]
1631 xxx = MakeEmptyXXX(parent, className)
1632
1633 # Set default name for top-level windows
1634 if parent.__class__ == xxxMainNode:
1635 cl = xxx.treeObject().__class__
1636 frame.maxIDs[cl] += 1
1637 xxx.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl])
1638 xxx.treeObject().element.setAttribute('name', xxx.treeObject().name)
1639
1640 # Figure out if we must append a new child or sibling
1641 elem = xxx.element
1642 if appendChild:
1643 # Insert newline for debug purposes
1644 parent.element.appendChild(elem)
1645 newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(),
1646 data=wxTreeItemData(xxx))
1647 else:
1648 node = tree.GetPyData(nextItem).element
1649 parent.element.insertBefore(elem, node)
1650 # !!! There is a different behavious on Win and GTK
1651 # !!! On Win InsertItem(parent, parent, ...) inserts at the end.
1652 index = tree.ItemIndex(parentLeaf, nextItem)
1653 newItem = tree.InsertItemBefore(parentLeaf, index,
1654 xxx.treeName(), image=xxx.treeImage())
1655 # data=wxTreeItemData(xxx)) # does not work
1656 tree.SetPyData(newItem, xxx)
1657 # newItem = tree.InsertItem(parentLeaf, selected,
1658 # xxx.treeName(), image=xxx.treeImage(),
1659 # data=wxTreeItemData(xxx))
1660 tree.EnsureVisible(newItem)
1661 tree.SelectItem(newItem)
1662 if not tree.IsVisible(newItem):
1663 tree.ScrollTo(newItem)
1664 tree.Refresh()
1665 # Update view?
1666 if testWin and tree.IsHighlatable(newItem):
1667 if conf.autoRefresh:
1668 tree.needUpdate = true
1669 tree.pendingHighLight = newItem
1670 else:
1671 tree.pendingHighLight = None
1672
1673 # Expand/collapse subtree
1674 def OnExpand(self, evt):
1675 if tree.selection: tree.ExpandAll(tree.selection)
1676 else: tree.ExpandAll(tree.root)
1677 def OnCollapse(self, evt):
1678 if tree.selection: tree.CollapseAll(tree.selection)
1679 else: tree.CollapseAll(tree.root)
1680
1681 def OnPullDownHighlight(self, evt):
1682 menuId = evt.GetMenuId()
1683 if menuId != -1:
1684 menu = evt.GetEventObject()
1685 help = menu.GetHelpString(menuId)
1686 self.SetStatusText(help)
1687 else:
1688 self.SetStatusText('')
1689
1690 def OnUpdateUI(self, evt):
1691 if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]:
1692 evt.Enable(tree.selection != tree.root)
1693 elif evt.GetId() == wxID_PASTE:
1694 evt.Enable((self.clipboard and tree.selection) != None)
1695 elif evt.GetId() == self.ID_TEST:
1696 evt.Enable(tree.selection != tree.root)
1697 elif evt.GetId() == self.ID_REFRESH:
1698 evt.Enable(testWin != None)
1699
1700 def OnIdle(self, evt):
1701 if self.inIdle: return # Recursive call protection
1702 self.inIdle = true
1703 if tree.needUpdate:
1704 if conf.autoRefresh:
1705 if testWin:
1706 self.SetStatusText('Refreshing test window...')
1707 # (re)create
1708 tree.CreateTestWin(testWin.item)
1709 wxYield()
1710 self.SetStatusText('')
1711 tree.needUpdate = false
1712 elif tree.pendingHighLight:
1713 tree.HighLight(tree.pendingHighLight)
1714 else:
1715 evt.Skip()
1716 self.inIdle = false
1717
1718 # We don't let close panel window
1719 def OnCloseMiniFrame(self, evt):
1720 return
1721
1722 def OnCloseWindow(self, evt):
1723 if not self.AskSave(): return
1724 if testWin: testWin.Destroy()
1725 # Destroy cached windows
1726 panel.cacheParent.Destroy()
1727 # for w in panel.styleCache.values(): w.Destroy()
1728 if not panel.GetPageCount() == 2:
1729 panel.page2.Destroy()
1730 conf.x, conf.y = self.GetPosition()
1731 conf.width, conf.height = self.GetSize()
1732 if conf.embedPanel:
1733 conf.sashPos = self.splitter.GetSashPosition()
1734 conf.panelWidth, conf.panelHeight = self.panelWidth, self.panelHeight
1735 else:
1736 conf.sashPos = self.sashPos
1737 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
1738 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
1739 evt.Skip()
1740
1741 def Clear(self):
1742 self.dataFile = ''
1743 self.clipboard = None
1744 self.modified = false
1745 panel.SetModified(false)
1746 tree.Clear()
1747 panel.Clear()
1748 global testWin
1749 if testWin:
1750 testWin.Destroy()
1751 testWin = None
1752 self.SetTitle(progname)
1753 # Numbers for new controls
1754 self.maxIDs = {}
1755 self.maxIDs[xxxPanel] = self.maxIDs[xxxDialog] = self.maxIDs[xxxFrame] = \
1756 self.maxIDs[xxxMenuBar] = self.maxIDs[xxxMenu] = self.maxIDs[xxxToolBar] = 0
1757
1758 def Open(self, path):
1759 # Try to read the file
1760 try:
1761 open(path)
1762 self.Clear()
1763 # Build wx tree
1764 dom = minidom.parse(path)
1765 tree.SetData(dom)
1766 self.dataFile = path
1767 self.SetTitle(progname + ': ' + os.path.basename(path))
1768 except:
1769 wxLogError('Error reading file: %s' % path)
1770 raise
1771
1772 def Indent(self, node, indent = 0):
1773 # Copy child list because it will change soon
1774 children = node.childNodes[:]
1775 # Main node doesn't need to be indented
1776 if indent:
1777 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1778 node.parentNode.insertBefore(text, node)
1779 if children:
1780 # Append newline after last child, except for text nodes
1781 if children[-1].nodeType == minidom.Node.ELEMENT_NODE:
1782 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1783 node.appendChild(text)
1784 # Indent children which are elements
1785 for n in children:
1786 if n.nodeType == minidom.Node.ELEMENT_NODE:
1787 self.Indent(n, indent + 2)
1788
1789 def Save(self, path):
1790 try:
1791 # Apply changes
1792 self.OnRefresh(wxCommandEvent())
1793 f = open(path, 'w')
1794 # Make temporary copy
1795 self.domCopy = domCopy = tree.dom.cloneNode(true)
1796 self.Indent(domCopy.getElementsByTagName('resource')[0])
1797 domCopy.writexml(f)
1798 # domCopy.unlink()
1799 self.domCopy = None
1800 f.close()
1801 self.modified = false
1802 panel.SetModified(false)
1803 except:
1804 wxLogError('Error writing file: %s' % path)
1805 raise
1806
1807 def AskSave(self):
1808 if not (self.modified or panel.IsModified()): return true
1809 flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
1810 dlg = wxMessageDialog( self, 'File is modified. Save before exit?',
1811 'Save before too late?', flags )
1812 say = dlg.ShowModal()
1813 dlg.Destroy()
1814 if say == wxID_YES:
1815 self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE))
1816 # If save was successful, modified flag is unset
1817 if not self.modified: return true
1818 elif say == wxID_NO:
1819 self.modified = false
1820 panel.SetModified(false)
1821 return true
1822 return false
1823
1824 ################################################################################
1825
1826 def usage():
1827 print >> sys.stderr, 'usage: xrced [-dvh] [file]'
1828
1829 class App(wxApp):
1830 def OnInit(self):
1831 global debug, verbose
1832 # Process comand-line
1833 try:
1834 opts, args = getopt.getopt(sys.argv[1:], 'dvh')
1835 except getopt.GetoptError:
1836 print >> sys.stderr, 'Unknown option'
1837 usage()
1838 sys.exit(1)
1839 for o,a in opts:
1840 if o == '-h':
1841 usage()
1842 sys.exit(0)
1843 elif o == '-d':
1844 debug = true
1845 elif o == '-v':
1846 print 'XRCed version', version
1847 sys.exit(0)
1848
1849 self.SetAppName('xrced')
1850 # Settings
1851 global conf
1852 conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE)
1853 conf.autoRefresh = conf.ReadInt('autorefresh', true)
1854 pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1)
1855 size = conf.ReadInt('width', 800), conf.ReadInt('height', 600)
1856 conf.embedPanel = conf.ReadInt('embedPanel', true)
1857 conf.sashPos = conf.ReadInt('sashPos', 200)
1858 if not conf.embedPanel:
1859 conf.panelX = conf.ReadInt('panelX', -1)
1860 conf.panelY = conf.ReadInt('panelY', -1)
1861 else:
1862 conf.panelX = conf.panelY = -1
1863 conf.panelWidth = conf.ReadInt('panelWidth', 200)
1864 conf.panelHeight = conf.ReadInt('panelHeight', 200)
1865 conf.panic = not conf.HasEntry('nopanic')
1866 # Add handlers
1867 wxFileSystem_AddHandler(wxMemoryFSHandler())
1868 wxInitAllImageHandlers()
1869 # Create main frame
1870 frame = Frame(pos, size)
1871 frame.Show(true)
1872 # Load resources from XRC file (!!! should be transformed to .py later?)
1873 sys.modules['params'].frame = frame
1874 frame.res = wxXmlResource('')
1875 frame.res.Load(os.path.join(sys.path[0], 'xrced.xrc'))
1876
1877 # Load file after showing
1878 if args:
1879 conf.panic = false
1880 frame.open = frame.Open(args[0])
1881
1882 return true
1883
1884 def OnExit(self):
1885 # Write config
1886 wc = wxConfigBase_Get()
1887 wc.WriteInt('autorefresh', conf.autoRefresh)
1888 wc.WriteInt('x', conf.x)
1889 wc.WriteInt('y', conf.y)
1890 wc.WriteInt('width', conf.width)
1891 wc.WriteInt('height', conf.height)
1892 wc.WriteInt('embedPanel', conf.embedPanel)
1893 if not conf.embedPanel:
1894 wc.WriteInt('panelX', conf.panelX)
1895 wc.WriteInt('panelY', conf.panelY)
1896 wc.WriteInt('sashPos', conf.sashPos)
1897 wc.WriteInt('panelWidth', conf.panelWidth)
1898 wc.WriteInt('panelHeight', conf.panelHeight)
1899 wc.WriteInt('nopanic', 1)
1900 wc.Flush()
1901
1902 def main():
1903 app = App()
1904 app.MainLoop()
1905 app.OnExit()
1906
1907 if __name__ == '__main__':
1908 main()