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