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