]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/tools/XRCed/xrced.py
0584f63043a5c2d292eb5ca66b8c113fd3d9bb58
[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.LoadFrame(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 self.SetIcon(icon)
1006
1007 # Idle flag
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()[0] == wxGTK:
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 self.minWidth = tb.GetSize()[0] # minimal width is the size of toolbar
1084
1085 # File
1086 EVT_MENU(self, wxID_NEW, self.OnNew)
1087 EVT_MENU(self, wxID_OPEN, self.OnOpen)
1088 EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs)
1089 EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs)
1090 EVT_MENU(self, wxID_EXIT, self.OnExit)
1091 # Edit
1092 EVT_MENU(self, wxID_UNDO, self.OnUndo)
1093 EVT_MENU(self, wxID_REDO, self.OnRedo)
1094 EVT_MENU(self, wxID_CUT, self.OnCut)
1095 EVT_MENU(self, wxID_COPY, self.OnCopy)
1096 EVT_MENU(self, wxID_PASTE, self.OnPaste)
1097 EVT_MENU(self, self.ID_DELETE, self.OnDelete)
1098 # View
1099 EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel)
1100 EVT_MENU(self, self.ID_TEST, self.OnTest)
1101 EVT_MENU(self, self.ID_REFRESH, self.OnRefresh)
1102 EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh)
1103 # Help
1104 EVT_MENU(self, wxID_ABOUT, self.OnAbout)
1105 EVT_MENU(self, self.ID_README, self.OnReadme)
1106
1107 # Update events
1108 EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI)
1109 EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI)
1110 EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI)
1111 EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI)
1112 EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI)
1113 EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI)
1114
1115 # Build interface
1116 sizer = wxBoxSizer(wxVERTICAL)
1117 sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND)
1118 splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH)
1119 self.splitter = splitter
1120 splitter.SetMinimumPaneSize(100)
1121
1122 # Create tree
1123 global tree
1124 tree = XML_Tree(splitter, -1)
1125 ##sys.modules['xxx'].tree = tree
1126 import xxx
1127 xxx.tree = tree
1128
1129 # !!! frame styles are broken
1130 # Miniframe for not embedded mode
1131 miniFrame = wxFrame(self, -1, 'Properties Panel',
1132 (conf.panelX, conf.panelY),
1133 (conf.panelWidth, conf.panelHeight))
1134 self.miniFrame = miniFrame
1135 sizer2 = wxBoxSizer()
1136 miniFrame.SetAutoLayout(true)
1137 miniFrame.SetSizer(sizer2)
1138 EVT_CLOSE(self.miniFrame, self.OnCloseMiniFrame)
1139 # Create panel for parameters
1140 global panel
1141 if conf.embedPanel:
1142 panel = Panel(splitter)
1143 # Set plitter windows
1144 splitter.SplitVertically(tree, panel, conf.sashPos)
1145 else:
1146 panel = Panel(miniFrame)
1147 sizer2.Add(panel, 1, wxEXPAND)
1148 miniFrame.Show(true)
1149 splitter.Initialize(tree)
1150 sizer.Add(splitter, 1, wxEXPAND)
1151 self.SetAutoLayout(true)
1152 self.SetSizer(sizer)
1153
1154 # Init pull-down menu data
1155 global pullDownMenu
1156 pullDownMenu = PullDownMenu(self)
1157 # Mapping from IDs to element names
1158 self.createMap = {
1159 pullDownMenu.ID_NEW_PANEL: 'wxPanel',
1160 pullDownMenu.ID_NEW_DIALOG: 'wxDialog',
1161 pullDownMenu.ID_NEW_FRAME: 'wxFrame',
1162 pullDownMenu.ID_NEW_TOOL_BAR: 'wxToolBar',
1163 pullDownMenu.ID_NEW_TOOL: 'tool',
1164 pullDownMenu.ID_NEW_MENU_BAR: 'wxMenuBar',
1165 pullDownMenu.ID_NEW_MENU: 'wxMenu',
1166 pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem',
1167 pullDownMenu.ID_NEW_SEPARATOR: 'separator',
1168
1169 pullDownMenu.ID_NEW_STATIC_TEXT: 'wxStaticText',
1170 pullDownMenu.ID_NEW_TEXT_CTRL: 'wxTextCtrl',
1171
1172 pullDownMenu.ID_NEW_BUTTON: 'wxButton',
1173 pullDownMenu.ID_NEW_BITMAP_BUTTON: 'wxBitmapButton',
1174 pullDownMenu.ID_NEW_RADIO_BUTTON: 'wxRadioButton',
1175 pullDownMenu.ID_NEW_SPIN_BUTTON: 'wxSpinButton',
1176
1177 pullDownMenu.ID_NEW_STATIC_BOX: 'wxStaticBox',
1178 pullDownMenu.ID_NEW_CHECK_BOX: 'wxCheckBox',
1179 pullDownMenu.ID_NEW_RADIO_BOX: 'wxRadioBox',
1180 pullDownMenu.ID_NEW_COMBO_BOX: 'wxComboBox',
1181 pullDownMenu.ID_NEW_LIST_BOX: 'wxListBox',
1182
1183 pullDownMenu.ID_NEW_STATIC_LINE: 'wxStaticLine',
1184 pullDownMenu.ID_NEW_STATIC_BITMAP: 'wxStaticBitmap',
1185 pullDownMenu.ID_NEW_CHOICE: 'wxChoice',
1186 pullDownMenu.ID_NEW_SLIDER: 'wxSlider',
1187 pullDownMenu.ID_NEW_GAUGE: 'wxGauge',
1188 pullDownMenu.ID_NEW_SCROLL_BAR: 'wxScrollBar',
1189 pullDownMenu.ID_NEW_TREE_CTRL: 'wxTreeCtrl',
1190 pullDownMenu.ID_NEW_LIST_CTRL: 'wxListCtrl',
1191 pullDownMenu.ID_NEW_CHECK_LIST: 'wxCheckList',
1192 pullDownMenu.ID_NEW_NOTEBOOK: 'wxNotebook',
1193 pullDownMenu.ID_NEW_HTML_WINDOW: 'wxHtmlWindow',
1194 pullDownMenu.ID_NEW_CALENDAR: 'wxCalendar',
1195
1196 pullDownMenu.ID_NEW_BOX_SIZER: 'wxBoxSizer',
1197 pullDownMenu.ID_NEW_STATIC_BOX_SIZER: 'wxStaticBoxSizer',
1198 pullDownMenu.ID_NEW_GRID_SIZER: 'wxGridSizer',
1199 pullDownMenu.ID_NEW_FLEX_GRID_SIZER: 'wxFlexGridSizer',
1200 pullDownMenu.ID_NEW_SPACER: 'spacer',
1201 }
1202 pullDownMenu.controls = [
1203 ['control', 'Various controls',
1204 (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'),
1205 (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'),
1206 (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'),
1207 (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'),
1208 (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'),
1209 (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'),
1210 (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'),
1211 (pullDownMenu.ID_NEW_TREE_CTRL, 'TreeCtrl', 'Create tree control'),
1212 (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'),
1213 (pullDownMenu.ID_NEW_HTML_WINDOW, 'HtmlWindow', 'Create HTML window'),
1214 (pullDownMenu.ID_NEW_CALENDAR, 'Calendar', 'Create calendar control'),
1215 (pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel'),
1216 (pullDownMenu.ID_NEW_NOTEBOOK, 'Notebook', 'Create notebook control'),
1217 ],
1218 ['button', 'Buttons',
1219 (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'),
1220 (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'),
1221 (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'),
1222 (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'),
1223 ],
1224 ['box', 'Boxes',
1225 (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'),
1226 (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'),
1227 (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'),
1228 (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'),
1229 (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'),
1230 (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox',
1231 'Create check list control'),
1232 ],
1233 ['sizer', 'Sizers',
1234 (pullDownMenu.ID_NEW_BOX_SIZER, 'BoxSizer', 'Create box sizer'),
1235 (pullDownMenu.ID_NEW_STATIC_BOX_SIZER, 'StaticBoxSizer',
1236 'Create static box sizer'),
1237 (pullDownMenu.ID_NEW_GRID_SIZER, 'GridSizer', 'Create grid sizer'),
1238 (pullDownMenu.ID_NEW_FLEX_GRID_SIZER, 'FlexGridSizer',
1239 'Create flexgrid sizer'),
1240 (pullDownMenu.ID_NEW_SPACER, 'Spacer', 'Create spacer'),
1241 ]
1242 ]
1243 pullDownMenu.menuControls = [
1244 (pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu'),
1245 (pullDownMenu.ID_NEW_MENU_ITEM, 'MenuItem', 'Create menu item'),
1246 (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'),
1247 ]
1248 pullDownMenu.toolBarControls = [
1249 (pullDownMenu.ID_NEW_TOOL, 'Tool', 'Create tool'),
1250 (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'),
1251 ['control', 'Various controls',
1252 (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'),
1253 (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'),
1254 (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'),
1255 (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'),
1256 (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'),
1257 (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'),
1258 (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'),
1259 (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'),
1260 ],
1261 ['button', 'Buttons',
1262 (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'),
1263 (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'),
1264 (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'),
1265 (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'),
1266 ],
1267 ['box', 'Boxes',
1268 (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'),
1269 (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'),
1270 (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'),
1271 (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'),
1272 (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'),
1273 (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox',
1274 'Create check list control'),
1275 ],
1276 ]
1277
1278 # Initialize
1279 self.Clear()
1280
1281 # Other events
1282 EVT_IDLE(self, self.OnIdle)
1283 EVT_CLOSE(self, self.OnCloseWindow)
1284
1285 def OnNew(self, evt):
1286 self.Clear()
1287
1288 def OnOpen(self, evt):
1289 if not self.AskSave(): return
1290 dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile),
1291 '', '*.xrc', wxOPEN | wxCHANGE_DIR)
1292 if dlg.ShowModal() == wxID_OK:
1293 path = dlg.GetPath()
1294 self.SetStatusText('Loading...')
1295 wxYield()
1296 wxBeginBusyCursor()
1297 try:
1298 self.Open(path)
1299 self.SetStatusText('Data loaded')
1300 except:
1301 self.SetStatusText('Failed')
1302 raise
1303 wxEndBusyCursor()
1304 dlg.Destroy()
1305
1306 def OnSaveOrSaveAs(self, evt):
1307 if evt.GetId() == wxID_SAVEAS or not self.dataFile:
1308 if self.dataFile: defaultName = ''
1309 else: defaultName = 'UNTITLED.xrc'
1310 dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile),
1311 defaultName, '*.xrc',
1312 wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR)
1313 if dlg.ShowModal() == wxID_OK:
1314 path = dlg.GetPath()
1315 dlg.Destroy()
1316 else:
1317 dlg.Destroy()
1318 return
1319 else:
1320 path = self.dataFile
1321 self.SetStatusText('Saving...')
1322 wxYield()
1323 wxBeginBusyCursor()
1324 try:
1325 self.Save(path)
1326 self.dataFile = path
1327 self.SetStatusText('Data saved')
1328 except IOError:
1329 self.SetStatusText('Failed')
1330 wxEndBusyCursor()
1331
1332 def OnExit(self, evt):
1333 self.Close()
1334
1335 def OnUndo(self, evt):
1336 print '*** being implemented'
1337 return
1338 print self.lastOp, self.undo
1339 if self.lastOp == 'DELETE':
1340 parent, prev, elem = self.undo
1341 if prev.IsOk():
1342 xxx = MakeXXXFromDOM(tree.GetPyData(parent).treeObject(), elem)
1343 item = tree.InsertItem( parent, prev, xxx.treeObject().className,
1344 data=wxTreeItemData(xxx) )
1345
1346 def OnRedo(self, evt):
1347 print '*** being implemented'
1348
1349 def OnCut(self, evt):
1350 selected = tree.selection
1351 if not selected: return # key pressed event
1352 # Undo info
1353 self.lastOp = 'CUT'
1354 self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)]
1355 # Delete testWin?
1356 global testWin
1357 if testWin:
1358 # If deleting top-level item, delete testWin
1359 if selected == testWin.item:
1360 testWin.Destroy()
1361 testWin = None
1362 else:
1363 # Remove highlight, update testWin
1364 if not tree.IsHighlatable(selected):
1365 if testWin.highLight: testWin.highLight.Remove()
1366 tree.needUpdate = true
1367 self.clipboard = tree.RemoveLeaf(selected)
1368 tree.pendingHighLight = None
1369 tree.Unselect()
1370 panel.Clear()
1371 self.modified = true
1372 self.SetStatusText('Removed to clipboard')
1373
1374 def OnCopy(self, evt):
1375 selected = tree.selection
1376 if not selected: return # key pressed event
1377 xxx = tree.GetPyData(selected)
1378 self.clipboard = xxx.element.cloneNode(true)
1379 self.SetStatusText('Copied')
1380
1381 def OnPaste(self, evt):
1382 selected = tree.selection
1383 if not selected: return # key pressed event
1384 # For pasting with Ctrl pressed
1385 if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = false
1386 else: appendChild = not tree.NeedInsert(selected)
1387 xxx = tree.GetPyData(selected)
1388 if not appendChild:
1389 # If has next item, insert, else append to parent
1390 nextItem = tree.GetNextSibling(selected)
1391 if nextItem.IsOk():
1392 # Insert before nextItem
1393 parentLeaf = tree.GetItemParent(selected)
1394 else: # last child: change selected to parent
1395 appendChild = true
1396 selected = tree.GetItemParent(selected)
1397 # Expanded container (must have children)
1398 elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, false):
1399 appendChild = false
1400 nextItem = tree.GetFirstChild(selected, 0)[0]
1401 parentLeaf = selected
1402 # Parent should be tree element or None
1403 if appendChild:
1404 parent = tree.GetPyData(selected)
1405 else:
1406 parent = tree.GetPyData(parentLeaf)
1407 if parent.hasChild: parent = parent.child
1408
1409 # Create a copy of clipboard element
1410 elem = self.clipboard.cloneNode(true)
1411 # Tempopary xxx object to test things
1412 xxx = MakeXXXFromDOM(parent, elem)
1413
1414 # Check compatibility
1415 error = false
1416 # Top-level
1417 x = xxx.treeObject()
1418 if x.__class__ in [xxxDialog, xxxFrame, xxxMenuBar, xxxToolBar]:
1419 if parent.__class__ != xxxMainNode: error = true
1420 elif x.__class__ == xxxPanel and parent.__class__ == xxxMainNode:
1421 pass
1422 elif x.__class__ == xxxSpacer:
1423 if not parent.isSizer: error = true
1424 elif x.__class__ == xxxSeparator:
1425 if not parent.__class__ in [xxxMenu, xxxToolBar]: error = true
1426 elif x.__class__ == xxxTool:
1427 if parent.__class__ != xxxToolBar: error = true
1428 elif x.__class__ == xxxMenuItem:
1429 if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = true
1430 elif x.isSizer and parent.__class__ == xxxNotebook: error = true
1431 else: # normal controls can be almost anywhere
1432 if parent.__class__ == xxxMainNode or \
1433 parent.__class__ in [xxxMenuBar, xxxMenu]: error = true
1434 if error:
1435 if parent.__class__ == xxxMainNode: parentClass = 'root'
1436 else: parentClass = parent.className
1437 wxLogError('Incompatible parent/child: parent is %s, child is %s!' %
1438 (parentClass, x.className))
1439 return
1440
1441 # Check parent and child relationships.
1442 # If parent is sizer or notebook, child is of wrong class or
1443 # parent is normal window, child is child container then detach child.
1444 isChildContainer = isinstance(xxx, xxxChildContainer)
1445 if isChildContainer and \
1446 ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
1447 (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
1448 not (parent.isSizer or isinstance(parent, xxxNotebook))):
1449 elem.removeChild(xxx.child.element) # detach child
1450 elem.unlink() # delete child container
1451 elem = xxx.child.element # replace
1452 # This may help garbage collection
1453 xxx.child.parent = None
1454 isChildContainer = false
1455 # Parent is sizer or notebook, child is not child container
1456 if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer):
1457 # Create sizer item element
1458 sizerItemElem = MakeEmptyDOM('sizeritem')
1459 sizerItemElem.appendChild(elem)
1460 elem = sizerItemElem
1461 elif isinstance(parent, xxxNotebook) and not isChildContainer:
1462 pageElem = MakeEmptyDOM('notebookpage')
1463 pageElem.appendChild(elem)
1464 elem = pageElem
1465 xxx = MakeXXXFromDOM(parent, elem)
1466 # Figure out if we must append a new child or sibling
1467 if appendChild:
1468 parent.element.appendChild(elem)
1469 newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(),
1470 data=wxTreeItemData(xxx))
1471 else:
1472 node = tree.GetPyData(nextItem).element
1473 parent.element.insertBefore(elem, node)
1474 # Inserting before is difficult, se we insert after or first child
1475 index = tree.ItemIndex(parentLeaf, nextItem)
1476 newItem = tree.InsertItemBefore(parentLeaf, index,
1477 xxx.treeName(), image=xxx.treeImage())
1478 tree.SetPyData(newItem, xxx)
1479 # newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(),
1480 # image=xxx.treeImage(), data=wxTreeItemData(xxx))
1481 # Add children items
1482 if xxx.hasChildren:
1483 treeObj = xxx.treeObject()
1484 for n in treeObj.element.childNodes:
1485 if IsObject(n):
1486 tree.AddNode(newItem, treeObj, n)
1487 # Scroll to show new item
1488 tree.EnsureVisible(newItem)
1489 tree.SelectItem(newItem)
1490 if not tree.IsVisible(newItem):
1491 tree.ScrollTo(newItem)
1492 tree.Refresh()
1493 # Update view?
1494 if testWin and tree.IsHighlatable(newItem):
1495 if conf.autoRefresh:
1496 tree.needUpdate = true
1497 tree.pendingHighLight = newItem
1498 else:
1499 tree.pendingHighLight = None
1500 self.modified = true
1501 self.SetStatusText('Pasted')
1502
1503 def OnDelete(self, evt):
1504 selected = tree.selection
1505 if not selected: return # key pressed event
1506 # Undo info
1507 self.lastOp = 'DELETE'
1508 self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)]
1509 # Delete testWin?
1510 global testWin
1511 if testWin:
1512 # If deleting top-level item, delete testWin
1513 if selected == testWin.item:
1514 testWin.Destroy()
1515 testWin = None
1516 else:
1517 # Remove highlight, update testWin
1518 if not tree.IsHighlatable(selected):
1519 if testWin.highLight: testWin.highLight.Remove()
1520 tree.needUpdate = true
1521 xnode = tree.RemoveLeaf(selected)
1522 # !!! cloneNode is broken, or something is wrong
1523 # self.undo.append(xnode.cloneNode(true))
1524 xnode.unlink()
1525 tree.pendingHighLight = None
1526 tree.Unselect()
1527 panel.Clear()
1528 self.modified = true
1529 self.SetStatusText('Deleted')
1530
1531 def OnEmbedPanel(self, evt):
1532 conf.embedPanel = evt.IsChecked()
1533 if conf.embedPanel:
1534 # Remember last dimentions
1535 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
1536 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
1537 size = self.GetSize()
1538 pos = self.GetPosition()
1539 sizePanel = panel.GetSize()
1540 panel.Reparent(self.splitter)
1541 self.miniFrame.GetSizer().RemoveWindow(panel)
1542 wxYield()
1543 # Widen
1544 self.SetDimensions(pos.x, pos.y, size.x + sizePanel.x, size.y)
1545 self.splitter.SplitVertically(tree, panel, conf.sashPos)
1546 self.miniFrame.Show(false)
1547 else:
1548 conf.sashPos = self.splitter.GetSashPosition()
1549 pos = self.GetPosition()
1550 size = self.GetSize()
1551 sizePanel = panel.GetSize()
1552 self.splitter.Unsplit(panel)
1553 sizer = self.miniFrame.GetSizer()
1554 panel.Reparent(self.miniFrame)
1555 panel.Show(true)
1556 sizer.Add(panel, 1, wxEXPAND)
1557 self.miniFrame.Show(true)
1558 self.miniFrame.SetDimensions(conf.panelX, conf.panelY,
1559 conf.panelWidth, conf.panelHeight)
1560 wxYield()
1561 # Reduce width
1562 self.SetDimensions(pos.x, pos.y,
1563 max(size.x - sizePanel.x, self.minWidth), size.y)
1564
1565 def OnTest(self, evt):
1566 if not tree.selection: return # key pressed event
1567 tree.ShowTestWindow(tree.selection)
1568
1569 def OnRefresh(self, evt):
1570 # If modified, apply first
1571 selection = tree.selection
1572 if selection:
1573 xxx = tree.GetPyData(selection)
1574 if xxx and panel.IsModified():
1575 tree.Apply(xxx, selection)
1576 if testWin:
1577 # (re)create
1578 tree.CreateTestWin(testWin.item)
1579 tree.needUpdate = false
1580
1581 def OnAutoRefresh(self, evt):
1582 conf.autoRefresh = evt.IsChecked()
1583 self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
1584 self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
1585
1586 def OnAbout(self, evt):
1587 str = '%s %s\n\nRoman Rolinsky <rolinsky@mema.ucl.ac.be>' % \
1588 (progname, version)
1589 dlg = wxMessageDialog(self, str, 'About ' + progname, wxOK | wxCENTRE)
1590 dlg.ShowModal()
1591 dlg.Destroy()
1592
1593 def OnReadme(self, evt):
1594 text = open(os.path.join(sys.path[0], 'README'), 'r').read()
1595 dlg = ScrolledMessageDialog(self, text, "XRCed README")
1596 dlg.ShowModal()
1597 dlg.Destroy()
1598
1599
1600 # Simple emulation of python command line
1601 def OnDebugCMD(self, evt):
1602 import traceback
1603 while 1:
1604 try:
1605 exec raw_input('C:\> ')
1606 except EOFError:
1607 print '^D'
1608 break
1609 except:
1610 (etype, value, tb) =sys.exc_info()
1611 tblist =traceback.extract_tb(tb)[1:]
1612 msg =string.join(traceback.format_exception_only(etype, value)
1613 +traceback.format_list(tblist))
1614 print msg
1615
1616 def OnCreate(self, evt):
1617 selected = tree.selection
1618 if tree.ctrl: appendChild = false
1619 else: appendChild = not tree.NeedInsert(selected)
1620 xxx = tree.GetPyData(selected)
1621 if not appendChild:
1622 # If insert before
1623 if tree.shift:
1624 # If has previous item, insert after it, else append to parent
1625 nextItem = selected
1626 parentLeaf = tree.GetItemParent(selected)
1627 else:
1628 # If has next item, insert, else append to parent
1629 nextItem = tree.GetNextSibling(selected)
1630 if nextItem.IsOk():
1631 # Insert before nextItem
1632 parentLeaf = tree.GetItemParent(selected)
1633 else: # last child: change selected to parent
1634 appendChild = true
1635 selected = tree.GetItemParent(selected)
1636 # Expanded container (must have children)
1637 elif tree.shift and tree.IsExpanded(selected) \
1638 and tree.GetChildrenCount(selected, false):
1639 appendChild = false
1640 nextItem = tree.GetFirstChild(selected, 0)[0]
1641 parentLeaf = selected
1642 # Parent should be tree element or None
1643 if appendChild:
1644 parent = tree.GetPyData(selected)
1645 else:
1646 parent = tree.GetPyData(parentLeaf)
1647 if parent.hasChild: parent = parent.child
1648
1649 # Create element
1650 className = self.createMap[evt.GetId()]
1651 xxx = MakeEmptyXXX(parent, className)
1652
1653 # Set default name for top-level windows
1654 if parent.__class__ == xxxMainNode:
1655 cl = xxx.treeObject().__class__
1656 frame.maxIDs[cl] += 1
1657 xxx.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl])
1658 xxx.treeObject().element.setAttribute('name', xxx.treeObject().name)
1659
1660 # Figure out if we must append a new child or sibling
1661 elem = xxx.element
1662 if appendChild:
1663 # Insert newline for debug purposes
1664 parent.element.appendChild(elem)
1665 newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(),
1666 data=wxTreeItemData(xxx))
1667 else:
1668 node = tree.GetPyData(nextItem).element
1669 parent.element.insertBefore(elem, node)
1670 # !!! There is a different behavious on Win and GTK
1671 # !!! On Win InsertItem(parent, parent, ...) inserts at the end.
1672 index = tree.ItemIndex(parentLeaf, nextItem)
1673 newItem = tree.InsertItemBefore(parentLeaf, index,
1674 xxx.treeName(), image=xxx.treeImage())
1675 # data=wxTreeItemData(xxx)) # does not work
1676 tree.SetPyData(newItem, xxx)
1677 # newItem = tree.InsertItem(parentLeaf, selected,
1678 # xxx.treeName(), image=xxx.treeImage(),
1679 # data=wxTreeItemData(xxx))
1680 tree.EnsureVisible(newItem)
1681 tree.SelectItem(newItem)
1682 if not tree.IsVisible(newItem):
1683 tree.ScrollTo(newItem)
1684 tree.Refresh()
1685 # Update view?
1686 if testWin and tree.IsHighlatable(newItem):
1687 if conf.autoRefresh:
1688 tree.needUpdate = true
1689 tree.pendingHighLight = newItem
1690 else:
1691 tree.pendingHighLight = None
1692
1693 # Expand/collapse subtree
1694 def OnExpand(self, evt):
1695 if tree.selection: tree.ExpandAll(tree.selection)
1696 else: tree.ExpandAll(tree.root)
1697 def OnCollapse(self, evt):
1698 if tree.selection: tree.CollapseAll(tree.selection)
1699 else: tree.CollapseAll(tree.root)
1700
1701 def OnPullDownHighlight(self, evt):
1702 menuId = evt.GetMenuId()
1703 if menuId != -1:
1704 menu = evt.GetEventObject()
1705 help = menu.GetHelpString(menuId)
1706 self.SetStatusText(help)
1707 else:
1708 self.SetStatusText('')
1709
1710 def OnUpdateUI(self, evt):
1711 if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]:
1712 evt.Enable(tree.selection != tree.root)
1713 elif evt.GetId() == wxID_PASTE:
1714 evt.Enable((self.clipboard and tree.selection) != None)
1715 elif evt.GetId() == self.ID_TEST:
1716 evt.Enable(tree.selection != tree.root)
1717
1718 def OnIdle(self, evt):
1719 if self.inIdle: return # Recursive call protection
1720 self.inIdle = true
1721 if tree.needUpdate:
1722 if conf.autoRefresh:
1723 if testWin:
1724 self.SetStatusText('Refreshing test window...')
1725 # (re)create
1726 tree.CreateTestWin(testWin.item)
1727 wxYield()
1728 self.SetStatusText('')
1729 tree.needUpdate = false
1730 elif tree.pendingHighLight:
1731 tree.HighLight(tree.pendingHighLight)
1732 else:
1733 evt.Skip()
1734 self.inIdle = false
1735
1736 # We don't let close panel window
1737 def OnCloseMiniFrame(self, evt):
1738 return
1739
1740 def OnCloseWindow(self, evt):
1741 if not self.AskSave(): return
1742 if testWin: testWin.Destroy()
1743 # Destroy cached windows
1744 panel.cacheParent.Destroy()
1745 if not panel.GetPageCount() == 2:
1746 panel.page2.Destroy()
1747 conf.x, conf.y = self.GetPosition()
1748 conf.width, conf.height = self.GetSize()
1749 if conf.embedPanel:
1750 conf.sashPos = self.splitter.GetSashPosition()
1751 else:
1752 conf.panelX, conf.panelY = self.miniFrame.GetPosition()
1753 conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize()
1754 evt.Skip()
1755
1756 def Clear(self):
1757 self.dataFile = ''
1758 self.clipboard = None
1759 self.modified = false
1760 panel.SetModified(false)
1761 tree.Clear()
1762 panel.Clear()
1763 global testWin
1764 if testWin:
1765 testWin.Destroy()
1766 testWin = None
1767 self.SetTitle(progname)
1768 # Numbers for new controls
1769 self.maxIDs = {}
1770 self.maxIDs[xxxPanel] = self.maxIDs[xxxDialog] = self.maxIDs[xxxFrame] = \
1771 self.maxIDs[xxxMenuBar] = self.maxIDs[xxxMenu] = self.maxIDs[xxxToolBar] = 0
1772
1773 def Open(self, path):
1774 # Try to read the file
1775 try:
1776 open(path)
1777 self.Clear()
1778 # Build wx tree
1779 dom = minidom.parse(path)
1780 tree.SetData(dom)
1781 self.dataFile = path
1782 self.SetTitle(progname + ': ' + os.path.basename(path))
1783 except:
1784 wxLogError('Error reading file: %s' % path)
1785 raise
1786
1787 def Indent(self, node, indent = 0):
1788 # Copy child list because it will change soon
1789 children = node.childNodes[:]
1790 # Main node doesn't need to be indented
1791 if indent:
1792 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1793 node.parentNode.insertBefore(text, node)
1794 if children:
1795 # Append newline after last child, except for text nodes
1796 if children[-1].nodeType == minidom.Node.ELEMENT_NODE:
1797 text = self.domCopy.createTextNode('\n' + ' ' * indent)
1798 node.appendChild(text)
1799 # Indent children which are elements
1800 for n in children:
1801 if n.nodeType == minidom.Node.ELEMENT_NODE:
1802 self.Indent(n, indent + 2)
1803
1804 def Save(self, path):
1805 try:
1806 # Apply changes
1807 self.OnRefresh(wxCommandEvent())
1808 f = open(path, 'w')
1809 # Make temporary copy
1810 # !!! We can't clone dom node, it works only once
1811 #self.domCopy = tree.dom.cloneNode(true)
1812 self.domCopy = minidom.Document()
1813 mainNode = self.domCopy.appendChild(tree.mainNode.cloneNode(true))
1814 self.Indent(mainNode)
1815 self.domCopy.writexml(f)
1816 f.close()
1817 self.domCopy.unlink()
1818 self.domCopy = None
1819 self.modified = false
1820 panel.SetModified(false)
1821 except:
1822 wxLogError('Error writing file: %s' % path)
1823 raise
1824
1825 def AskSave(self):
1826 if not (self.modified or panel.IsModified()): return true
1827 flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
1828 dlg = wxMessageDialog( self, 'File is modified. Save before exit?',
1829 'Save before too late?', flags )
1830 say = dlg.ShowModal()
1831 dlg.Destroy()
1832 if say == wxID_YES:
1833 self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE))
1834 # If save was successful, modified flag is unset
1835 if not self.modified: return true
1836 elif say == wxID_NO:
1837 self.modified = false
1838 panel.SetModified(false)
1839 return true
1840 return false
1841
1842 ################################################################################
1843
1844 def usage():
1845 print >> sys.stderr, 'usage: xrced [-dvh] [file]'
1846
1847 class App(wxApp):
1848 def OnInit(self):
1849 global debug, verbose
1850 # Process comand-line
1851 try:
1852 opts, args = getopt.getopt(sys.argv[1:], 'dvh')
1853 except getopt.GetoptError:
1854 print >> sys.stderr, 'Unknown option'
1855 usage()
1856 sys.exit(1)
1857 for o,a in opts:
1858 if o == '-h':
1859 usage()
1860 sys.exit(0)
1861 elif o == '-d':
1862 debug = true
1863 elif o == '-v':
1864 print 'XRCed version', version
1865 sys.exit(0)
1866
1867 self.SetAppName('xrced')
1868 # Settings
1869 global conf
1870 conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE)
1871 conf.autoRefresh = conf.ReadInt('autorefresh', true)
1872 pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1)
1873 size = conf.ReadInt('width', 800), conf.ReadInt('height', 600)
1874 conf.embedPanel = conf.ReadInt('embedPanel', true)
1875 conf.sashPos = conf.ReadInt('sashPos', 200)
1876 if not conf.embedPanel:
1877 conf.panelX = conf.ReadInt('panelX', -1)
1878 conf.panelY = conf.ReadInt('panelY', -1)
1879 else:
1880 conf.panelX = conf.panelY = -1
1881 conf.panelWidth = conf.ReadInt('panelWidth', 200)
1882 conf.panelHeight = conf.ReadInt('panelHeight', 200)
1883 conf.panic = not conf.HasEntry('nopanic')
1884 # Add handlers
1885 wxFileSystem_AddHandler(wxMemoryFSHandler())
1886 wxInitAllImageHandlers()
1887 # Create main frame
1888 frame = Frame(pos, size)
1889 frame.Show(true)
1890 # Load resources from XRC file (!!! should be transformed to .py later?)
1891 ##sys.modules['params'].frame = frame
1892 import params
1893 params.frame = frame
1894 frame.res = wxXmlResource('')
1895 frame.res.Load(os.path.join(sys.path[0], 'xrced.xrc'))
1896
1897 # Load file after showing
1898 if args:
1899 conf.panic = false
1900 frame.open = frame.Open(args[0])
1901
1902 return true
1903
1904 def OnExit(self):
1905 # Write config
1906 global conf
1907 wc = wxConfigBase_Get()
1908 wc.WriteInt('autorefresh', conf.autoRefresh)
1909 wc.WriteInt('x', conf.x)
1910 wc.WriteInt('y', conf.y)
1911 wc.WriteInt('width', conf.width)
1912 wc.WriteInt('height', conf.height)
1913 wc.WriteInt('embedPanel', conf.embedPanel)
1914 if not conf.embedPanel:
1915 wc.WriteInt('panelX', conf.panelX)
1916 wc.WriteInt('panelY', conf.panelY)
1917 wc.WriteInt('sashPos', conf.sashPos)
1918 wc.WriteInt('panelWidth', conf.panelWidth)
1919 wc.WriteInt('panelHeight', conf.panelHeight)
1920 wc.WriteInt('nopanic', 1)
1921 wc.Flush()
1922 del conf
1923
1924 def main():
1925 app = App()
1926 app.MainLoop()
1927 app.OnExit()
1928
1929 if __name__ == '__main__':
1930 main()