]> git.saurik.com Git - wxWidgets.git/blob - wxPython/tools/XRCed/xrced.py
Updated XRCed
[wxWidgets.git] / wxPython / tools / XRCed / xrced.py
1 # Name: xrced.py
2 # Purpose: XRC editor, main module
3 # Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be>
4 # Created: 20.08.2001
5 # RCS-ID: $Id$
6
7 from wxPython.wx import *
8 from wxPython.xrc import *
9 from wxPython.html import *
10 import wxPython.lib.wxpTag
11 from xml.dom import minidom
12 import os
13 import tempfile
14
15 import images
16
17 # String constants
18
19 faceColour = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_3DFACE)
20 # Stange wxHtmlWindow behavior: when bgcolor is exact, it turns white
21 bgcolor = (faceColour.Red()-1, faceColour.Green()-1, faceColour.Blue()-1)
22 htmlHeader = '<html><body bgcolor="#%02x%02x%02x">\n' % bgcolor
23 htmlFooter = '</body></html>\n'
24 progname = 'XRCed'
25 version = '0.0.5'
26
27 # Local modules
28 from xxx import *
29
30 # Globals
31 testWin = None
32 testWinPos = wxDefaultPosition
33
34 # 1 adds CMD command to Help menu
35 debug = 1
36
37 if debug:
38 import traceback
39 import time
40
41 # Set menu to list items.
42 # Each menu command is a tuple (id, label, help)
43 # submenus are lists [id, label, help, submenu]
44 # and separators are any other type
45 def SetMenu(m, list):
46 for l in list:
47 if type(l) == types.TupleType:
48 apply(m.Append, l)
49 elif type(l) == types.ListType:
50 subMenu = wxMenu()
51 SetMenu(subMenu, l[2:])
52 m.AppendMenu(wxNewId(), l[0], subMenu, l[1])
53 else: # separator
54 m.AppendSeparator()
55
56 ################################################################################
57
58 # Properties panel containing notebook
59 class Panel(wxNotebook):
60 def __init__(self, parent, id = -1):
61 wxNotebook.__init__(self, parent, id, style=wxNB_BOTTOM)
62 #self.SetBackgroundColour(wxColour(bgcolor))
63 sys.modules['params'].panel = self
64 self.page1 = HtmlPage(self)
65 self.AddPage(self.page1, 'Properties')
66 self.page2 = None
67 def SetData(self, xxx):
68 self.page1.SetPageData(xxx)
69 # Replace/remove style page
70 if self.page2:
71 self.RemovePage(1)
72 self.page2.Destroy()
73 if xxx and xxx.treeObject().hasStyle:
74 self.page2 = StylePage(self, xxx.treeObject())
75 self.AddPage(self.page2, 'Style')
76 else:
77 self.page2 = None
78 def Clear(self):
79 self.page1.Clear()
80 if self.page2:
81 self.RemovePage(1)
82 self.page2.Destroy()
83 self.page2 = None
84 # If some parameter on some page has changed
85 def IsModified(self):
86 return self.page1.IsModified() or self.page2 and self.page2.IsModified()
87 def SetModified(self, value):
88 self.page1.SetModified(value)
89 if self.page2:
90 self.page2.SetModified(value)
91
92 ################################################################################
93
94 # General class for notebook pages
95 class ParamPage:
96 def __init__(self):
97 # Register event handlers
98 for id in paramIDs.values():
99 EVT_CHECKBOX(self, id, self.OnCheckParams)
100 def OnCheckParams(self, evt):
101 selected = tree.GetSelection()
102 xxx = tree.GetPyData(selected)
103 winName = evt.GetEventObject().GetName()
104 sizerParam = false
105 if winName[0] == '_':
106 param = winName[7:]
107 sizerParam = true
108 else:
109 if xxx.hasChild: xxx = xxx.child
110 param = winName[6:]
111 # Set current object
112 if sizerParam:
113 w = self.FindWindowByName('_data_' + param)
114 else:
115 w = self.FindWindowByName('data_' + param)
116 elem = xxx.element
117 if evt.IsChecked():
118 # Ad new text node in order of allParams
119 w.SetValue('') # set empty (default) value
120 # For font element, we have to create another object
121 if param == 'font':
122 # Make XXX object
123 textElem = tree.dom.createElement('font')
124 font = xxxFont(xxx, textElem)
125 xxx.params['font'] = font
126 else:
127 textElem = tree.dom.createElement(param)
128 textNode = tree.dom.createTextNode('')
129 textElem.appendChild(textNode)
130 xxx.params[param] = textNode
131 # Find place to put new element: first present element after param
132 found = false
133 paramStyles = xxx.allParams + xxx.styles
134 for p in paramStyles[paramStyles.index(param) + 1:]:
135 # Content params don't have same type
136 if xxx.params.has_key(p) and p != 'content':
137 found = true
138 break
139 if found:
140 nextTextElem = xxx.params[p].parentNode
141 elem.insertBefore(textElem, nextTextElem)
142 else:
143 elem.appendChild(textElem)
144 else:
145 # Remove parameter element and following text node
146 textElem = xxx.params[param].parentNode
147 newline = textElem.nextSibling
148 if newline and newline.nodeType == minidom.Node.TEXT_NODE:
149 elem.removeChild(newline)
150 elem.removeChild(textElem)
151 del xxx.params[param]
152 w.SetValue('')
153 w.Enable(evt.IsChecked())
154 # Set modified flas
155 self.SetModified(true)
156
157
158 ################################################################################
159
160 # Properties panel notebook page
161 class HtmlPage(wxHtmlWindow, ParamPage):
162 def __init__(self, parent, id = -1):
163 wxHtmlWindow.__init__(self, parent, id)
164 ParamPage.__init__(self)
165 self.SetBorders(5)
166 if wxGetOsVersion()[1] == 1:
167 self.SetFonts('', '', [8, 10, 12, 14, 16, 19, 24])
168 else:
169 self.SetFonts("", "", [7, 8, 10, 12, 16, 22, 30])
170 self.modified = false
171 def Clear(self):
172 self.SetPage(htmlHeader + 'select a tree item on the left' + htmlFooter)
173 def SetPageData(self, xxx):
174 if not xxx:
175 self.SetPage(htmlHeader + 'this item has no properties' + htmlFooter)
176 return
177 self.SetPage(htmlHeader + xxx.generateHtml() + htmlFooter)
178 # Set values, checkboxes to false, disable defaults
179 if xxx.hasChild: prefix = '_'
180 else: prefix = ''
181 for param in xxx.allParams:
182 if xxx.params.has_key(param):
183 if param == 'content':
184 value = []
185 for text in xxx.params[param]:
186 value.append(str(text.data)) # convert from unicode
187 else:
188 value = xxx.params[param].data
189 self.FindWindowByName(prefix + 'data_' + param).SetValue(value)
190 if not param in xxx.required:
191 self.FindWindowByName(prefix + 'check_' + param).SetValue(true)
192 else:
193 self.FindWindowByName(prefix + 'data_' + param).Enable(false)
194 # Same for the child of sizeritem
195 if xxx.hasChild:
196 xxx = xxx.child
197 for param in xxx.allParams:
198 if xxx.params.has_key(param):
199 if param == 'content':
200 value = []
201 for text in xxx.params[param]:
202 value.append(str(text.data)) # convert from unicode
203 else:
204 value = xxx.params[param].data
205 self.FindWindowByName('data_' + param).SetValue(value)
206 if not param in xxx.required:
207 self.FindWindowByName('check_' + param).SetValue(true)
208 else:
209 self.FindWindowByName('data_' + param).Enable(false)
210 # If some parameter has changed
211 def IsModified(self):
212 return self.modified
213 def SetModified(self, value):
214 self.modified = value
215
216 ################################################################################
217
218 # Style notebook page
219 class StylePage(wxPanel, ParamPage):
220 def __init__(self, parent, xxx):
221 wxPanel.__init__(self, parent, -1)
222 ParamPage.__init__(self)
223 if wxGetOsVersion()[1] == 1:
224 self.SetFont(wxFont(12, wxDEFAULT, wxNORMAL, wxNORMAL))
225 else:
226 self.SetFont(wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL))
227 topSizer = wxBoxSizer(wxVERTICAL)
228 sizer = wxFlexGridSizer(len(xxx.styles), 2, 0, 1)
229 self.controls = {} # save python objects
230 for param in xxx.styles:
231 present = param in xxx.params.keys()
232 check = wxCheckBox(self, paramIDs[param],
233 param + ':', name = 'check_' + param)
234 check.SetValue(present)
235 control = paramDict[param](self, -1, '', (-1, -1),
236 'data_' + param)
237 if present:
238 control.SetValue(xxx.params[param].data)
239 else:
240 control.SetValue('')
241 control.Enable(present)
242 sizer.AddMany([ (check, 0, 0),
243 (control, 0, 0) ])
244 self.controls[param] = control
245 topSizer.Add(sizer, 1, wxALL, 5)
246 self.SetAutoLayout(true)
247 self.SetSizer(topSizer)
248
249 self.modified = false
250
251 # If some parameter has changed
252 def IsModified(self):
253 return self.modified
254 def SetModified(self, value):
255 self.modified = value
256
257 ################################################################################
258
259 class HightLightBox:
260 def __init__(self, pos, size):
261 w = testWin.panel
262 l1 = wxWindow(w, -1, pos, wxSize(size.x, 2))
263 l1.SetBackgroundColour(wxRED)
264 l2 = wxWindow(w, -1, pos, wxSize(2, size.y))
265 l2.SetBackgroundColour(wxRED)
266 l3 = wxWindow(w, -1, wxPoint(pos.x + size.x - 2, pos.y), wxSize(2, size.y))
267 l3.SetBackgroundColour(wxRED)
268 l4 = wxWindow(w, -1, wxPoint(pos.x, pos.y + size.y - 2), wxSize(size.x, 2))
269 l4.SetBackgroundColour(wxRED)
270 self.lines = [l1, l2, l3, l4]
271 # Move highlight to a new position
272 def Replace(self, pos, size):
273 self.lines[0].SetDimensions(pos.x, pos.y, size.x, 2, wxSIZE_ALLOW_MINUS_ONE)
274 self.lines[1].SetDimensions(pos.x, pos.y, 2, size.y, wxSIZE_ALLOW_MINUS_ONE)
275 self.lines[2].SetDimensions(pos.x + size.x - 2, pos.y, 2, size.y,
276 wxSIZE_ALLOW_MINUS_ONE)
277 self.lines[3].SetDimensions(pos.x, pos.y + size.y - 2, size.x, 2,
278 wxSIZE_ALLOW_MINUS_ONE)
279 # Remove it
280 def Remove(self):
281 map(wxWindow.Destroy, self.lines)
282 testWin.highLight = None
283
284 ################################################################################
285
286 class MemoryFile:
287 def __init__(self, name):
288 self.name = name
289 self.buffer = ''
290 def write(self, data):
291 self.buffer = self.buffer + data.encode()
292 def close(self):
293 f = open(self.name, 'w')
294 f.write(self.buffer)
295 f.close()
296 # !!! memory FS will work someday
297 #self.file = wxMemoryFSHandler_AddFile(self.name, self.buffer)
298
299 class XML_Tree(wxTreeCtrl):
300 def __init__(self, parent, id):
301 wxTreeCtrl.__init__(self, parent, id,
302 style=wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT)
303 self.SetBackgroundColour(wxColour(224, 248, 224))
304 EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
305 EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated)
306 EVT_RIGHT_DOWN(self, self.OnRightDown)
307 self.needUpdate = false
308 self.pendingHighLight = None
309 self.ctrl = false
310 self.dom = None
311 # Create image list
312 il = wxImageList(16, 16, true)
313 self.rootImage = il.AddIcon(wxIconFromXPMData(images.getTreeRootData()))
314 xxxObject.image = il.AddIcon(wxIconFromXPMData(images.getTreeDefaultData()))
315 xxxPanel.image = il.AddIcon(wxIconFromXPMData(images.getTreePanelData()))
316 xxxDialog.image = il.AddIcon(wxIconFromXPMData(images.getTreeDialogData()))
317 xxxFrame.image = il.AddIcon(wxIconFromXPMData(images.getTreeFrameData()))
318 xxxMenuBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuBarData()))
319 xxxMenu.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuData()))
320 xxxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeSizerHData()))
321 xxxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeSizerVData()))
322 xxxStaticBoxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerHData()))
323 xxxStaticBoxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerVData()))
324 xxxGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerGridData()))
325 xxxFlexGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerFlexGridData()))
326 self.il = il
327 self.SetImageList(il)
328
329 # !!! temporary solution for GetOldItem problem
330 def Unselect(self):
331 self.selection = wxTreeItemId()
332 wxTreeCtrl.Unselect(self)
333 def GetSelection(self):
334 return self.selection
335
336 def ExpandAll(self, item):
337 if self.ItemHasChildren(item):
338 self.Expand(item)
339 i, cookie = self.GetFirstChild(item, 0)
340 children = []
341 while i.IsOk():
342 children.append(i)
343 i, cookie = self.GetNextChild(item, cookie)
344 for i in children:
345 self.ExpandAll(i)
346
347 def SetData(self, dom):
348 self.dom = dom
349 # Find 'resource' child, add it's children
350 self.mainNode = dom.getElementsByTagName('resource')[0]
351 nodes = self.mainNode.childNodes[:]
352 for node in nodes:
353 if IsObject(node):
354 self.AddNode(self.GetRootItem(), None, node)
355 else:
356 self.mainNode.removeChild(node)
357 node.unlink()
358 self.Unselect()
359
360 # Add tree item for given parent item if node is DOM element node with
361 # 'object' tag. xxxParent is parent xxx object
362 def AddNode(self, itemParent, xxxParent, node):
363 # Set item data to current node
364 xxx = MakeXXXFromDOM(xxxParent, node)
365 treeObj = xxx.treeObject()
366 # Append tree item
367 item = self.AppendItem(itemParent, treeObj.treeName(),
368 image=treeObj.treeImage(),
369 data=wxTreeItemData(xxx))
370 # Try to find children objects
371 if treeObj.hasChildren:
372 nodes = treeObj.element.childNodes[:]
373 for n in nodes:
374 if IsObject(n):
375 self.AddNode(item, treeObj, n)
376 elif n.nodeType != minidom.Node.ELEMENT_NODE:
377 treeObj.element.removeChild(n)
378 n.unlink()
379
380
381 # Remove leaf of tree, return it's data object
382 def RemoveLeaf(self, leaf):
383 xxx = self.GetPyData(leaf)
384 node = xxx.element
385 parent = node.parentNode
386 parent.removeChild(node)
387 self.Delete(leaf)
388 # Update view?
389 #if testWin and self.GetItemAncestor(leaf) == testWin.item:
390 # if testWin.highLight:
391 # testWin.highLight.Remove()
392 # self.needUpdate = true
393 return node
394
395 # Find position relative to the top-level window
396 def FindNodePos(self, item):
397 itemParent = self.GetItemParent(item)
398 if itemParent == self.GetRootItem(): return wxPoint(0, 0)
399 obj = self.FindNodeObject(item)
400 # Find first ancestor which is a wxWindow (not a sizer)
401 winParent = itemParent
402 while self.GetPyData(winParent).isSizer:
403 winParent = self.GetItemParent(winParent)
404 parentPos = self.FindNodePos(winParent)
405 return parentPos + obj.GetPosition()
406
407 # Find window (or sizer) corresponding to a tree item.
408 def FindNodeObject(self, item):
409 itemParent = self.GetItemParent(item)
410 # If top-level, return testWin (or panel if wxFrame)
411 if itemParent == self.GetRootItem(): return testWin.panel
412 xxx = self.GetPyData(item).treeObject()
413 parentWin = self.FindNodeObject(itemParent)
414 # Top-level sizer? return window's sizer
415 if xxx.isSizer and isinstance(parentWin, wxWindowPtr):
416 return parentWin.GetSizer()
417 # Otherwise get parent's object and it's child
418 n = 0 # index of sibling
419 prev = self.GetPrevSibling(item)
420 while prev.IsOk():
421 prev = self.GetPrevSibling(prev)
422 n += 1
423 child = parentWin.GetChildren()[n]
424 # Return window or sizer for sizer items
425 if child.GetClassName() == 'wxSizerItem':
426 if child.IsWindow(): child = child.GetWindow()
427 elif child.IsSizer():
428 child = child.GetSizer()
429 # Test for notebook sizers
430 if isinstance(child, wxNotebookSizerPtr):
431 child = child.GetNotebook()
432 return child
433
434 def OnSelChanged(self, evt):
435 # Apply changes
436 # !!! problem with wxGTK
437 #oldItem = evt.GetOldItem()
438 oldItem = self.selection
439 if oldItem.IsOk():
440 xxx = self.GetPyData(oldItem)
441 # If some data was modified, apply changes
442 if xxx:
443 if panel.IsModified():
444 self.Apply(xxx, oldItem)
445 #if conf.autoRefresh:
446 if testWin and testWin.highLight:
447 testWin.highLight.Remove()
448 self.needUpdate = true
449 # Generate HTML view
450 item = evt.GetItem()
451 self.selection = item # !!! fix
452 xxx = self.GetPyData(item)
453 # Update panel
454 panel.SetData(xxx)
455 # Remove highlight?
456 if not xxx and testWin and testWin.highLight:
457 testWin.highLight.Remove()
458 return
459 # Clear flag
460 panel.SetModified(false)
461 # Hightlighting is done in OnIdle
462 tree.pendingHighLight = item
463
464 # Find top-level parent
465 def GetItemAncestor(self, item):
466 while self.GetItemParent(item) != self.GetRootItem():
467 item = self.GetItemParent(item)
468 return item
469
470 # Highlight selected item
471 def HighLight(self, item):
472 self.pendingHighLight = None
473 if not testWin or self.GetPyData(testWin.item).className \
474 not in ['wxDialog', 'wxPanel', 'wxFrame']:
475 return
476 # Top-level does not have highlight
477 if item == testWin.item:
478 if testWin.highLight: testWin.highLight.Remove()
479 return
480 # If a control from another window is selected, remove highlight
481 if self.GetItemAncestor(item) != testWin.item and testWin.highLight:
482 testWin.highLight.Remove()
483 return
484 # Get window/sizer object
485 obj, pos = self.FindNodeObject(item), self.FindNodePos(item)
486 size = obj.GetSize()
487 # For notebook, select item's page.
488 # For children of page, nothing happens (too much work)
489 if isinstance(self.GetPyData(item).parent, xxxNotebook):
490 notebook = self.FindNodeObject(self.GetItemParent(item))
491 # Find position
492 n = 0
493 prev = self.GetPrevSibling(item)
494 while prev.IsOk():
495 n += 1
496 prev = self.GetPrevSibling(prev)
497 notebook.SetSelection(n)
498 # Highlight
499 try: # finally I use exceptions
500 testWin.highLight.Replace(pos, size)
501 except AttributeError:
502 testWin.highLight = HightLightBox(pos, size)
503 testWin.highLight.item = item
504
505 # Double-click
506 def OnItemActivated(self, evt):
507 item = evt.GetItem()
508 xxx = self.GetPyData(item)
509 if not xxx: return # if root selected, do nothing
510 if panel.IsModified():
511 self.Apply(xxx, item) # apply changes
512 self.CreateTestWin(item)
513
514 # (re)create test window
515 def CreateTestWin(self, node):
516 global testWin
517 # Create a window with this resource
518 xxx = self.GetPyData(node).treeObject()
519 if not xxx: return # if root selected, do nothing
520 # If noname element, display error
521 if not xxx.hasName or not xxx.name:
522 wxLogError("Can't display a noname element")
523 return
524 # Close old window, remember where it was
525 highLight = None
526 if testWin:
527 pos = testWin.GetPosition()
528 if node == testWin.item:
529 # Remember highlight if same top-level window
530 if testWin.highLight:
531 highLight = testWin.highLight.item
532 # !!! if 0 is removed, refresh is broken (notebook not deleted?)
533 if 0 and xxx.className == 'wxPanel':
534 if testWin.highLight:
535 testWin.pendingHighLight = highLight
536 testWin.highLight.Remove()
537 testWin.panel.Destroy()
538 testWin.panel = None
539 else:
540 testWin.Destroy()
541 testWin = None
542 else:
543 testWin.Destroy()
544 testWin = None
545 else:
546 pos = testWinPos
547 # Save in temporary file before activating
548 memFile = MemoryFile(tempfile.mktemp('xrc'))
549 #memFile = MemoryFile('core.xrc') # to write debug file
550 # Create partial XML file - faster for big files
551
552 dom = minidom.Document()
553 mainNode = dom.createElement('resource')
554 dom.appendChild(mainNode)
555
556 # Remove temporarily from old parent
557 elem = xxx.element
558 parent = elem.parentNode
559 next = elem.nextSibling
560 parent.replaceChild(self.dummyNode, elem)
561 # Append to new DOM, write it
562 mainNode.appendChild(elem)
563 dom.writexml(memFile)
564 # Put back in place
565 mainNode.removeChild(elem)
566 dom.unlink()
567 parent.replaceChild(elem, self.dummyNode)
568
569 memFile.close() # write to wxMemoryFS
570 res = wxXmlResource('')
571 res.Load(memFile.name)
572 if xxx.className == 'wxFrame':
573 # Create new frame
574 testWin = wxPreFrame()
575 res.LoadFrame(testWin, frame, xxx.name)
576 testWin.panel = testWin
577 testWin.SetPosition(pos)
578 testWin.Show(true)
579 elif xxx.className == 'wxPanel':
580 # Create new frame
581 if not testWin:
582 testWin = wxFrame(frame, -1, 'Panel: ' + xxx.name, pos=pos)
583 testWin.panel = res.LoadPanel(testWin, xxx.name)
584 testWin.SetSize(testWin.panel.GetSize())
585 testWin.Show(true)
586 elif xxx.className == 'wxDialog':
587 # Create new frame
588 testWin = res.LoadDialog(None, xxx.name)
589 testWin.panel = testWin
590 testWin.SetPosition(pos)
591 testWin.Show(true)
592 elif xxx.className == 'wxMenuBar':
593 testWin = wxFrame(frame, -1, 'MenuBar: ' + xxx.name, pos=pos)
594 # Set status bar to display help
595 testWin.CreateStatusBar()
596 testWin.menuBar = res.LoadMenuBar(xxx.name)
597 testWin.SetMenuBar(testWin.menuBar)
598 testWin.Show(true)
599 else:
600 wxLogMessage('No view for this element yet')
601 return
602 os.unlink(memFile.name) # remove tmp file
603 testWin.item = node
604 testWin.Connect(testWin.GetId(), -1, wxEVT_CLOSE_WINDOW, self.OnCloseTestWin)
605 testWin.highLight = None
606 if highLight and not tree.pendingHighLight:
607 self.HighLight(highLight)
608
609 def OnCloseTestWin(self, evt):
610 global testWin, testWinPos
611 testWinPos = testWin.GetPosition()
612 testWin.Destroy()
613 testWin = None
614 evt.Skip()
615
616 # True if next item should be inserted after current (vs. appended to it)
617 def NeedInsert(self, item):
618 xxx = self.GetPyData(item)
619 if not xxx: return false # root item
620 if self.ctrl: return true # if Ctrl pressed, always insert
621 if xxx.hasChildren and not self.ItemHasChildren(item):
622 return false
623 return not (self.IsExpanded(item) and self.ItemHasChildren(item))
624
625 # Pull-down
626 def OnRightDown(self, evt):
627 # Setup menu
628 menu = wxMenu()
629
630 item = self.GetSelection()
631 if not item.IsOk():
632 menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand tree')
633 else:
634 self.ctrl = evt.ControlDown() # save Ctrl state
635 m = wxMenu() # create menu
636 if item != self.GetRootItem(): needInsert = self.NeedInsert(item)
637 if item == self.GetRootItem() or \
638 self.GetItemParent(item) == self.GetRootItem() and needInsert:
639 m.Append(pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel')
640 m.Append(pullDownMenu.ID_NEW_DIALOG, 'Dialog', 'Create dialog')
641 m.Append(pullDownMenu.ID_NEW_FRAME, 'Frame', 'Create frame')
642 m.AppendSeparator()
643 m.Append(pullDownMenu.ID_NEW_MENU_BAR, 'MenuBar', 'Create menu bar')
644 m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu')
645 else:
646 xxx = self.GetPyData(item)
647 if xxx.__class__ == xxxMenuBar:
648 m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu')
649 elif xxx.__class__ in [xxxMenu, xxxMenuItem]:
650 SetMenu(m, pullDownMenu.menuControls)
651 else:
652 SetMenu(m, pullDownMenu.controls)
653 if not (xxx.isSizer or \
654 xxx.parent and xxx.parent.isSizer):
655 m.Enable(pullDownMenu.ID_NEW_SPACER, false)
656 # Select correct label for create menu
657 if item == self.GetRootItem():
658 menu.AppendMenu(wxNewId(), 'Create', m, 'Create top-level object')
659 else:
660 if not needInsert:
661 menu.AppendMenu(wxNewId(), 'Create child', m,
662 'Create child object')
663 else:
664 menu.AppendMenu(wxNewId(), 'Create Sibling', m,
665 'Create sibling of selected object')
666 menu.AppendSeparator()
667 menu.Append(wxID_CUT, 'Cut', 'Cut to the clipboard')
668 menu.Append(wxID_COPY, 'Copy', 'Copy to the clipboard')
669 menu.Append(wxID_PASTE, 'Paste', 'Paste from the clipboard')
670 menu.Append(pullDownMenu.ID_DELETE,
671 'Delete', 'Delete object')
672 if item.IsOk() and self.ItemHasChildren(item):
673 menu.AppendSeparator()
674 menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand subtree')
675 self.PopupMenu(menu, evt.GetPosition())
676 menu.Destroy()
677
678 # Clear tree
679 def Clear(self):
680 self.DeleteAllItems()
681 # Add minimal structure
682 root = self.AddRoot('XML tree', self.rootImage)
683 self.Unselect()
684 if self.dom: self.dom.unlink()
685 self.dom = minidom.Document()
686 self.dummyNode = self.dom.createComment('dummy node')
687 # Create main node
688 self.mainNode = self.dom.createElement('resource')
689 self.dom.appendChild(self.mainNode)
690
691 # Apply changes
692 def Apply(self, xxx, item):
693 if not xxx: return
694 # !!! Save undo info
695 if xxx.undo: xxx.undo.unlink()
696 xxx.undo = xxx.element.cloneNode(false)
697 if xxx.hasName:
698 name = panel.page1.FindWindowByName('data_name').GetValue()
699 if xxx.name != name:
700 xxx.name = name
701 xxx.element.setAttribute('name', name)
702 self.SetItemText(item, xxx.treeName())
703 if xxx.hasChild: prefix = '_'
704 else: prefix = ''
705 for param, data in xxx.params.items():
706 value = panel.FindWindowByName(prefix + 'data_' + param).GetValue()
707 if param == 'content':
708 # If number if items is not the same, recreate children
709 if len(value) != len(data):
710 elem = xxx.element.getElementsByTagName('content')[0]
711 for n in elem.childNodes:
712 elem.removeChild(n)
713 data = []
714 for str in value:
715 itemElem = tree.dom.createElement('item')
716 itemText = tree.dom.createTextNode(str)
717 itemElem.appendChild(itemText)
718 elem.appendChild(itemElem)
719 data.append(itemText)
720 xxx.params[param] = data
721 else:
722 for i in range(len(value)):
723 data[i].data = value[i]
724 elif param == 'font':
725 data.updateXML(value)
726 else:
727 data.data = value
728 if xxx.hasChild:
729 self.Apply(xxx.child, item)
730 else:
731 # Change tree icon for sizers
732 if isinstance(xxx, xxxBoxSizer):
733 self.SetItemImage(item, xxx.treeImage())
734 # Set global modified state
735 frame.modified = true
736
737 class PullDownMenu:
738 ID_NEW_PANEL = wxNewId()
739 ID_NEW_DIALOG = wxNewId()
740 ID_NEW_FRAME = wxNewId()
741 ID_NEW_MENU_BAR = wxNewId()
742 ID_NEW_MENU = wxNewId()
743
744 ID_NEW_STATIC_TEXT = wxNewId()
745 ID_NEW_TEXT_CTRL = wxNewId()
746
747 ID_NEW_BUTTON = wxNewId()
748 ID_NEW_BITMAP_BUTTON = wxNewId()
749 ID_NEW_RADIO_BUTTON = wxNewId()
750 ID_NEW_SPIN_BUTTON = wxNewId()
751
752 ID_NEW_STATIC_BOX = wxNewId()
753 ID_NEW_CHECK_BOX = wxNewId()
754 ID_NEW_RADIO_BOX = wxNewId()
755 ID_NEW_COMBO_BOX = wxNewId()
756 ID_NEW_LIST_BOX = wxNewId()
757
758 ID_NEW_STATIC_LINE = wxNewId()
759 ID_NEW_CHOICE = wxNewId()
760 ID_NEW_SLIDER = wxNewId()
761 ID_NEW_GAUGE = wxNewId()
762 ID_NEW_SCROLL_BAR = wxNewId()
763 ID_NEW_TREE_CTRL = wxNewId()
764 ID_NEW_LIST_CTRL = wxNewId()
765 ID_NEW_CHECK_LIST = wxNewId()
766 ID_NEW_NOTEBOOK = wxNewId()
767 ID_NEW_HTML_WINDOW = wxNewId()
768 ID_NEW_CALENDAR = wxNewId()
769
770 ID_NEW_BOX_SIZER = wxNewId()
771 ID_NEW_STATIC_BOX_SIZER = wxNewId()
772 ID_NEW_GRID_SIZER = wxNewId()
773 ID_NEW_FLEX_GRID_SIZER = wxNewId()
774 ID_NEW_SPACER = wxNewId()
775 ID_NEW_MENU = wxNewId()
776 ID_NEW_MENU_ITEM = wxNewId()
777 ID_NEW_SEPARATOR = wxNewId()
778 ID_NEW_LAST = wxNewId()
779 ID_EXPAND = wxNewId()
780
781 def __init__(self, parent):
782 self.ID_DELETE = parent.ID_DELETE
783 EVT_MENU_RANGE(parent, self.ID_NEW_PANEL,
784 self.ID_NEW_LAST, parent.OnCreate)
785 EVT_MENU(parent, self.ID_EXPAND, parent.OnExpand)
786 # We connect to tree, but process in frame
787 EVT_MENU_HIGHLIGHT_ALL(tree, parent.OnPullDownHighlight)
788
789 class Frame(wxFrame):
790 def __init__(self, size):
791 wxFrame.__init__(self, None, -1, '', size=size)
792 self.CreateStatusBar()
793 self.SetIcon(wxIconFromXPMData(images.getIconData()))
794
795 # Make menus
796 menuBar = wxMenuBar()
797
798 menu = wxMenu()
799 menu.Append(wxID_NEW, '&New\tCtrl-N', 'New file')
800 menu.Append(wxID_OPEN, '&Open...\tCtrl-O', 'Open XRC file')
801 menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file')
802 menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name')
803 menu.AppendSeparator()
804 menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application')
805 menuBar.Append(menu, '&File')
806
807 menu = wxMenu()
808 menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo')
809 menu.Append(wxID_REDO, '&Redo\tCtrl-R', 'Redo')
810 menu.AppendSeparator()
811 menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard')
812 menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard')
813 menu.Append(wxID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard')
814 self.ID_DELETE = wxNewId()
815 menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object')
816 menuBar.Append(menu, '&Edit')
817
818 menu = wxMenu()
819 self.ID_REFRESH = wxNewId()
820 menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh view')
821 self.ID_AUTO_REFRESH = wxNewId()
822 menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A',
823 'Toggle auto-refresh mode', true)
824 menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
825 menuBar.Append(menu, '&View')
826
827 menu = wxMenu()
828 menu.Append(wxID_ABOUT, 'About...', 'About XCRed')
829 if debug:
830 self.ID_DEBUG_CMD = wxNewId()
831 menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line')
832 EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD)
833 menuBar.Append(menu, '&Help')
834
835 self.menuBar = menuBar
836 self.SetMenuBar(menuBar)
837
838 # Create toolbar
839 tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT)
840 tb.SetToolBitmapSize((24, 23))
841 tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file')
842 tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file')
843 tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file')
844 tb.AddSeparator()
845 tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut')
846 tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy')
847 tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste')
848 tb.AddSeparator()
849 tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(),
850 'Refresh', 'Refresh view')
851 tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(),
852 'Auto-refresh', 'Toggle auto-refresh mode', true)
853 tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
854 tb.Realize()
855 self.tb = tb
856
857 # File
858 EVT_MENU(self, wxID_NEW, self.OnNew)
859 EVT_MENU(self, wxID_OPEN, self.OnOpen)
860 EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs)
861 EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs)
862 EVT_MENU(self, wxID_EXIT, self.OnExit)
863 # Edit
864 EVT_MENU(self, wxID_UNDO, self.OnUndo)
865 EVT_MENU(self, wxID_REDO, self.OnRedo)
866 EVT_MENU(self, wxID_CUT, self.OnCut)
867 EVT_MENU(self, wxID_COPY, self.OnCopy)
868 EVT_MENU(self, wxID_PASTE, self.OnPaste)
869 EVT_MENU(self, self.ID_DELETE, self.OnDelete)
870 # View
871 EVT_MENU(self, self.ID_REFRESH, self.OnRefresh)
872 EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh)
873 # Help
874 EVT_MENU(self, wxID_ABOUT, self.OnAbout)
875
876 # Update events
877 EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI)
878 EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI)
879 EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI)
880 EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI)
881
882 # Build interface
883 sizer = wxBoxSizer(wxVERTICAL)
884 sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND)
885 splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH)
886 splitter.SetMinimumPaneSize(100)
887 # Create tree
888 global tree
889 tree = XML_Tree(splitter, -1)
890 sys.modules['xxx'].tree = tree
891 # Create panel for parameters
892 global panel
893 panel = Panel(splitter)
894 # Set plitter windows
895 splitter.SplitVertically(tree, panel, 200)
896 sizer.Add(splitter, 1, wxEXPAND)
897 self.SetAutoLayout(true)
898 self.SetSizer(sizer)
899
900 # Init pull-down menu data
901 global pullDownMenu
902 pullDownMenu = PullDownMenu(self)
903 # Mapping from IDs to element names
904 self.createMap = {
905 pullDownMenu.ID_NEW_PANEL: 'wxPanel',
906 pullDownMenu.ID_NEW_DIALOG: 'wxDialog',
907 pullDownMenu.ID_NEW_FRAME: 'wxFrame',
908 pullDownMenu.ID_NEW_MENU_BAR: 'wxMenuBar',
909 pullDownMenu.ID_NEW_MENU: 'wxMenu',
910
911 pullDownMenu.ID_NEW_STATIC_TEXT: 'wxStaticText',
912 pullDownMenu.ID_NEW_TEXT_CTRL: 'wxTextCtrl',
913
914 pullDownMenu.ID_NEW_BUTTON: 'wxButton',
915 pullDownMenu.ID_NEW_BITMAP_BUTTON: 'wxBitmapButton',
916 pullDownMenu.ID_NEW_RADIO_BUTTON: 'wxRadioButton',
917 pullDownMenu.ID_NEW_SPIN_BUTTON: 'wxSpinButton',
918
919 pullDownMenu.ID_NEW_STATIC_BOX: 'wxStaticBox',
920 pullDownMenu.ID_NEW_CHECK_BOX: 'wxCheckBox',
921 pullDownMenu.ID_NEW_RADIO_BOX: 'wxRadioBox',
922 pullDownMenu.ID_NEW_COMBO_BOX: 'wxComboBox',
923 pullDownMenu.ID_NEW_LIST_BOX: 'wxListBox',
924
925 pullDownMenu.ID_NEW_STATIC_LINE: 'wxStaticLine',
926 pullDownMenu.ID_NEW_CHOICE: 'wxChoice',
927 pullDownMenu.ID_NEW_SLIDER: 'wxSlider',
928 pullDownMenu.ID_NEW_GAUGE: 'wxGauge',
929 pullDownMenu.ID_NEW_SCROLL_BAR: 'wxScrollBar',
930 pullDownMenu.ID_NEW_TREE_CTRL: 'wxTreeCtrl',
931 pullDownMenu.ID_NEW_LIST_CTRL: 'wxListCtrl',
932 pullDownMenu.ID_NEW_CHECK_LIST: 'wxCheckList',
933 pullDownMenu.ID_NEW_NOTEBOOK: 'wxNotebook',
934 pullDownMenu.ID_NEW_HTML_WINDOW: 'wxHtmlWindow',
935 pullDownMenu.ID_NEW_CALENDAR: 'wxCalendar',
936
937 pullDownMenu.ID_NEW_BOX_SIZER: 'wxBoxSizer',
938 pullDownMenu.ID_NEW_STATIC_BOX_SIZER: 'wxStaticBoxSizer',
939 pullDownMenu.ID_NEW_GRID_SIZER: 'wxGridSizer',
940 pullDownMenu.ID_NEW_FLEX_GRID_SIZER: 'wxFlexGridSizer',
941 pullDownMenu.ID_NEW_SPACER: 'spacer',
942 pullDownMenu.ID_NEW_MENU: 'wxMenu',
943 pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem',
944 pullDownMenu.ID_NEW_SEPARATOR: 'separator',
945 }
946 pullDownMenu.controls = [
947 ['control', 'Various controls',
948 (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'),
949 (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'),
950 (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'),
951 (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'),
952 (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'),
953 (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'),
954 (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'),
955 (pullDownMenu.ID_NEW_TREE_CTRL, 'TreeCtrl', 'Create tree control'),
956 (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'),
957 (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckList', 'Create check list control'),
958 (pullDownMenu.ID_NEW_HTML_WINDOW, 'HtmlWindow', 'Create HTML window'),
959 (pullDownMenu.ID_NEW_CALENDAR, 'Calendar', 'Create calendar control'),
960 (pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel'),
961 (pullDownMenu.ID_NEW_NOTEBOOK, 'Notebook', 'Create notebook control'),
962 ],
963 ['button', 'Buttons',
964 (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'),
965 (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'),
966 (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'),
967 (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'),
968 ],
969 ['box', 'Boxes',
970 (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'),
971 (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'),
972 (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'),
973 (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'),
974 (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'),
975 ],
976 ['sizer', 'Sizers',
977 (pullDownMenu.ID_NEW_BOX_SIZER, 'BoxSizer', 'Create box sizer'),
978 (pullDownMenu.ID_NEW_STATIC_BOX_SIZER, 'StaticBoxSizer',
979 'Create static box sizer'),
980 (pullDownMenu.ID_NEW_GRID_SIZER, 'GridSizer', 'Create grid sizer'),
981 (pullDownMenu.ID_NEW_FLEX_GRID_SIZER, 'FlexGridSizer',
982 'Create flexgrid sizer'),
983 (pullDownMenu.ID_NEW_SPACER, 'Spacer', 'Create spacer'),
984 ]
985 ]
986 pullDownMenu.menuControls = [
987 (pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu'),
988 (pullDownMenu.ID_NEW_MENU_ITEM, 'MenuItem', 'Create menu item'),
989 (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'),
990 ]
991
992 # Initialize
993 self.Clear()
994
995 # Other events
996 EVT_IDLE(self, self.OnIdle)
997 EVT_CLOSE(self, self.OnCloseWindow)
998
999 def OnNew(self, evt):
1000 self.Clear()
1001
1002 def OnOpen(self, evt):
1003 if not self.AskSave(): return
1004 dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile),
1005 '', '*.xrc', wxOPEN | wxCHANGE_DIR)
1006 if dlg.ShowModal() == wxID_OK:
1007 path = dlg.GetPath()
1008 self.SetStatusText('Loading...')
1009 wxYield()
1010 wxBeginBusyCursor()
1011 self.Open(path)
1012 wxEndBusyCursor()
1013 self.SetStatusText('Ready')
1014 dlg.Destroy()
1015
1016 def OnSaveOrSaveAs(self, evt):
1017 if evt.GetId() == wxID_SAVEAS or not self.dataFile:
1018 if self.dataFile: defaultName = ''
1019 else: defaultName = 'UNTITLED.xrc'
1020 dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile),
1021 defaultName, '*.xrc',
1022 wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR)
1023 if dlg.ShowModal() == wxID_OK:
1024 path = dlg.GetPath()
1025 dlg.Destroy()
1026 else:
1027 dlg.Destroy()
1028 return
1029 else:
1030 path = self.dataFile
1031 self.SetStatusText('Saving...')
1032 wxYield()
1033 wxBeginBusyCursor()
1034 self.Save(path)
1035 self.dataFile = path
1036 wxEndBusyCursor()
1037 self.SetStatusText('Ready')
1038
1039 def OnExit(self, evt):
1040 self.Close()
1041
1042 def OnUndo(self, evt):
1043 print '*** being implemented'
1044 print self.lastOp, self.undo
1045 if self.lastOp == 'DELETE':
1046 parent, prev, elem = self.undo
1047 if prev.IsOk():
1048 xxx = MakeXXXFromDOM(tree.GetPyData(parent).treeObject(), elem)
1049 item = tree.InsertItem( parent, prev, xxx.treeObject().className,
1050 data=wxTreeItemData(xxx) )
1051
1052 def OnRedo(self, evt):
1053 print '*** being implemented'
1054
1055 def OnCut(self, evt):
1056 selected = tree.GetSelection()
1057 # Undo info
1058 self.lastOp = 'CUT'
1059 self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)]
1060 # Delete testWin?
1061 global testWin
1062 if testWin:
1063 # If deleting top-level item, delete testWin
1064 if selected == testWin.item:
1065 testWin.Destroy()
1066 testWin = None
1067 else:
1068 # Remove highlight, update testWin
1069 if tree.GetItemAncestor(selected) == testWin.item:
1070 if testWin.highLight: testWin.highLight.Remove()
1071 tree.needUpdate = true
1072 self.clipboard = tree.RemoveLeaf(selected)
1073 tree.pendingHighLight = None
1074 tree.Unselect()
1075 panel.Clear()
1076 self.modified = true
1077
1078 def OnCopy(self, evt):
1079 selected = tree.GetSelection()
1080 xxx = tree.GetPyData(selected)
1081 self.clipboard = xxx.element.cloneNode(true)
1082
1083 def OnPaste(self, evt):
1084 selected = tree.GetSelection()
1085 appendChild = not tree.NeedInsert(selected)
1086 xxx = tree.GetPyData(selected)
1087 if not appendChild:
1088 # If has next item, insert, else append to parent
1089 nextItem = tree.GetNextSibling(selected)
1090 if nextItem.IsOk():
1091 # Insert before nextItem
1092 parentLeaf = tree.GetItemParent(selected)
1093 else: # last child: change selected to parent
1094 appendChild = true
1095 selected = tree.GetItemParent(selected)
1096 # Expanded container (must have children)
1097 elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected):
1098 appendChild = false
1099 nextItem = tree.GetFirstChild(selected, 0)[0]
1100 parentLeaf = selected
1101 # Parent should be tree element or None
1102 if appendChild:
1103 parent = tree.GetPyData(selected)
1104 else:
1105 parent = tree.GetPyData(parentLeaf)
1106 if parent and parent.hasChild: parent = parent.child
1107
1108 # Create a copy of clipboard element
1109 elem = self.clipboard.cloneNode(true)
1110 # Tempopary xxx object to test things
1111 xxx = MakeXXXFromDOM(parent, elem)
1112 className = xxx.treeObject().className
1113 # Check parent and child relationships
1114 # Parent is sizer or notebook, child is of wrong class or
1115 # parent is normal window, child is child container: detach child
1116 isChildContainer = isinstance(xxx, xxxChildContainer)
1117 if not parent and isChildContainer or \
1118 (parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
1119 (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
1120 (not parent.isSizer and not isinstance(parent, xxxNotebook) and \
1121 isChildContainer):
1122 if isChildContainer:
1123 elem.removeChild(xxx.child.element) # detach child
1124 elem.unlink() # delete child container
1125 elem = xxx.child.element # replace
1126 # This should help garbage collection (!!! maybe not needed?)
1127 xxx.child.parent = None
1128 xxx.child = None
1129 if parent:
1130 # Parent is sizer or notebook, child is not child container
1131 if parent.isSizer and not isChildContainer and \
1132 not isinstance(xxx, xxxSpacer):
1133 # Create sizer item element
1134 sizerItemElem = MakeEmptyDOM('sizeritem')
1135 sizerItemElem.appendChild(elem)
1136 elem = sizerItemElem
1137 elif isinstance(parent, xxxNotebook) and not isChildContainer:
1138 pageElem = MakeEmptyDOM('notebookpage')
1139 pageElem.appendChild(elem)
1140 elem = pageElem
1141 xxx = MakeXXXFromDOM(parent, elem)
1142 # Figure out if we must append a new child or sibling
1143 if appendChild:
1144 if parent: node = parent.element
1145 else: node = tree.mainNode
1146 node.appendChild(elem)
1147 newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(),
1148 data=wxTreeItemData(xxx))
1149 else:
1150 node = tree.GetPyData(nextItem).element
1151 if parent:
1152 elemParent = parent.element
1153 else:
1154 elemParent = tree.mainNode
1155 elemParent.insertBefore(elem, node)
1156 # Inserting before is difficult, se we insert after or first child
1157 newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(),
1158 image=xxx.treeImage(), data=wxTreeItemData(xxx))
1159 # Add children items
1160 if xxx.hasChildren:
1161 treeObj = xxx.treeObject()
1162 for n in treeObj.element.childNodes:
1163 if IsObject(n):
1164 tree.AddNode(newItem, treeObj, n)
1165 # Scroll to show new item
1166 tree.EnsureVisible(newItem)
1167 tree.SelectItem(newItem)
1168 if not tree.IsVisible(newItem):
1169 tree.ScrollTo(newItem)
1170 tree.Refresh()
1171 # Update view?
1172 if testWin and tree.GetItemAncestor(newItem) == testWin.item:
1173 if conf.autoRefresh:
1174 tree.needUpdate = true
1175 tree.pendingHighLight = newItem
1176 else:
1177 tree.pendingHighLight = None
1178 self.modified = true
1179
1180 def OnDelete(self, evt):
1181 selected = tree.GetSelection()
1182 # Undo info
1183 self.lastOp = 'DELETE'
1184 self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)]
1185 # Delete testWin?
1186 global testWin
1187 if testWin:
1188 # If deleting top-level item, delete testWin
1189 if selected == testWin.item:
1190 testWin.Destroy()
1191 testWin = None
1192 else:
1193 # Remove highlight, update testWin
1194 if tree.GetItemAncestor(selected) == testWin.item:
1195 if testWin.highLight: testWin.highLight.Remove()
1196 tree.needUpdate = true
1197 xnode = tree.RemoveLeaf(selected)
1198 self.undo.append(xnode.cloneNode(true))
1199 xnode.unlink()
1200 tree.pendingHighLight = None
1201 tree.Unselect()
1202 panel.Clear()
1203 self.modified = true
1204
1205 def OnRefresh(self, evt):
1206 # If modified, apply first
1207 selection = tree.GetSelection()
1208 if selection.IsOk():
1209 xxx = tree.GetPyData(selection)
1210 if xxx and panel.IsModified():
1211 tree.Apply(xxx, selection)
1212 if testWin:
1213 # (re)create
1214 tree.CreateTestWin(testWin.item)
1215 tree.needUpdate = false
1216
1217 def OnAutoRefresh(self, evt):
1218 conf.autoRefresh = evt.IsChecked()
1219 self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh)
1220 self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh)
1221
1222 def OnAbout(self, evt):
1223 wxMessageDialog(self, '%s %s\n\nRoman Rolinsky <rolinsky@mema.ucl.ac.be>' % \
1224 (progname, version),
1225 'About %s' % progname, wxOK | wxCENTRE).ShowModal()
1226
1227 # Simple emulation of python command line
1228 def OnDebugCMD(self, evt):
1229 while 1:
1230 try:
1231 exec raw_input('C:\> ')
1232 except EOFError:
1233 print '^D'
1234 break
1235 except:
1236 (etype, value, tb) =sys.exc_info()
1237 tblist =traceback.extract_tb(tb)[1:]
1238 msg =string.join(traceback.format_exception_only(etype, value)
1239 +traceback.format_list(tblist))
1240 print msg
1241
1242 def OnCreate(self, evt):
1243 selected = tree.GetSelection()
1244 appendChild = not tree.NeedInsert(selected)
1245 xxx = tree.GetPyData(selected)
1246 if not appendChild:
1247 # If has next item, insert, else append to parent
1248 nextItem = tree.GetNextSibling(selected)
1249 if nextItem.IsOk():
1250 # Insert before nextItem
1251 parentLeaf = tree.GetItemParent(selected)
1252 else: # last child: change selected to parent
1253 appendChild = true
1254 selected = tree.GetItemParent(selected)
1255 # Expanded container (must have children)
1256 elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected):
1257 appendChild = false
1258 nextItem = tree.GetFirstChild(selected, 0)[0]
1259 parentLeaf = selected
1260 # Parent should be tree element or None
1261 if appendChild:
1262 parent = tree.GetPyData(selected)
1263 else:
1264 parent = tree.GetPyData(parentLeaf)
1265 if parent and parent.hasChild: parent = parent.child
1266
1267 # Create element
1268 className = self.createMap[evt.GetId()]
1269 xxx = MakeEmptyXXX(parent, className)
1270 # Figure out if we must append a new child or sibling
1271 elem = xxx.element
1272 if appendChild:
1273 if parent: node = parent.element
1274 else: node = tree.mainNode
1275 # Insert newline for debug purposes
1276 node.appendChild(tree.dom.createTextNode('\n'))
1277 node.appendChild(elem)
1278 newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(),
1279 data=wxTreeItemData(xxx))
1280 else:
1281 node = tree.GetPyData(nextItem).element
1282 if parent:
1283 elemParent = parent.element
1284 else:
1285 elemParent = tree.mainNode
1286 elemParent.insertBefore(tree.dom.createTextNode('\n'), node)
1287 elemParent.insertBefore(elem, node)
1288 # Inserting before is difficult, se we insert after or first child
1289 newItem = tree.InsertItem(parentLeaf, selected,
1290 xxx.treeName(), image=xxx.treeImage(),
1291 data=wxTreeItemData(xxx))
1292 tree.EnsureVisible(newItem)
1293 tree.SelectItem(newItem)
1294 if not tree.IsVisible(newItem):
1295 tree.ScrollTo(newItem)
1296 tree.Refresh()
1297 # Update view?
1298 if testWin and tree.GetItemAncestor(newItem) == testWin.item:
1299 if conf.autoRefresh:
1300 tree.needUpdate = true
1301 tree.pendingHighLight = newItem
1302 else:
1303 tree.pendingHighLight = None
1304
1305 def OnExpand(self, evt):
1306 if tree.GetSelection().IsOk():
1307 tree.ExpandAll(tree.GetSelection())
1308 else:
1309 tree.ExpandAll(tree.GetRootItem())
1310
1311 def OnPullDownHighlight(self, evt):
1312 menuId = evt.GetMenuId()
1313 if menuId != -1:
1314 menu = evt.GetEventObject()
1315 help = menu.GetHelpString(menuId)
1316 self.SetStatusText(help)
1317 else:
1318 self.SetStatusText('')
1319
1320 def OnUpdateUI(self, evt):
1321 if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]:
1322 enable = tree.GetSelection().IsOk() and \
1323 tree.GetSelection() != tree.GetRootItem()
1324 evt.Enable(enable)
1325 elif evt.GetId() == wxID_PASTE:
1326 enable = self.clipboard != None
1327 evt.Enable(enable)
1328
1329 def OnIdle(self, evt):
1330 if tree.needUpdate:
1331 if conf.autoRefresh:
1332 if testWin:
1333 # (re)create
1334 tree.CreateTestWin(testWin.item)
1335 tree.needUpdate = false
1336 elif tree.pendingHighLight:
1337 tree.HighLight(tree.pendingHighLight)
1338 evt.Skip()
1339
1340 def OnCloseWindow(self, evt):
1341 if not self.AskSave(): return
1342 if testWin: testWin.Destroy()
1343 conf.width, conf.height = self.GetSize()
1344 evt.Skip()
1345
1346 def Clear(self):
1347 self.dataFile = ''
1348 self.clipboard = None
1349 self.modified = false
1350 panel.SetModified(false)
1351 panel.Clear()
1352 tree.Clear()
1353 global testWin
1354 if testWin:
1355 testWin.Destroy()
1356 testWin = None
1357 self.SetTitle(progname)
1358
1359 def Open(self, path):
1360 # Try to read the file
1361 try:
1362 open(path)
1363 self.Clear()
1364 # Build wx tree
1365 dom = minidom.parse(path)
1366 tree.SetData(dom)
1367 self.dataFile = path
1368 self.SetTitle(progname + ': ' + os.path.basename(path))
1369 except:
1370 wxLogError('Error reading file: ' + path)
1371 raise
1372
1373 def Save(self, path):
1374 try:
1375 self.OnRefresh(wxCommandEvent())
1376 memFile = MemoryFile(path)
1377 tree.dom.writexml(memFile)
1378 memFile.close()
1379 self.modified = false
1380 panel.SetModified(false)
1381 except:
1382 wxLogError('Error writing file: ' + path)
1383 raise
1384
1385 def AskSave(self):
1386 if not (self.modified or panel.IsModified()): return true
1387 flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE
1388 say = wxMessageDialog( self, 'File is modified. Save before exit?',
1389 'Save before too late?', flags ).ShowModal()
1390 if say == wxID_YES:
1391 self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE))
1392 # If save was successful, modified flag is unset
1393 if not self.modified: return true
1394 elif say == wxID_NO:
1395 self.modified = false
1396 panel.SetModified(false)
1397 return true
1398 return false
1399
1400 ################################################################################
1401
1402 class App(wxApp):
1403 def OnInit(self):
1404 self.SetAppName("xrced")
1405 # Settings
1406 global conf
1407 # !!! wxConfigBase_Get doesn't seem to work
1408 conf = wxConfig(style=wxCONFIG_USE_LOCAL_FILE)
1409 conf.autoRefresh = conf.ReadInt('autorefresh', true)
1410 size = conf.ReadInt('width', 800), conf.ReadInt('height', 600)
1411 # Add handlers
1412 wxFileSystem_AddHandler(wxMemoryFSHandler())
1413 wxInitAllImageHandlers()
1414 # Create main frame
1415 global frame
1416 frame = self.frame = Frame(size)
1417 self.frame.Show(true)
1418 # Load resources from XRC file (!!! should be transformed to .py later)
1419 sys.modules['params'].frame = frame
1420 frame.res = wxXmlResource('')
1421 frame.res.Load(os.path.join(sys.path[0], 'xrced.xrc'))
1422 return true
1423
1424 def OnExit(self):
1425 # Write config
1426 wc = wxConfigBase_Get()
1427 wc.WriteInt('autorefresh', conf.autoRefresh)
1428 wc.WriteInt('width', conf.width)
1429 wc.WriteInt('height', conf.height)
1430 wc.Flush()
1431
1432 def main():
1433 app = App()
1434 app.MainLoop()
1435 app.OnExit()
1436
1437 if __name__ == '__main__':
1438 main()