# Name: xrced.py # Purpose: XRC editor, main module # Author: Roman Rolinsky # Created: 20.08.2001 # RCS-ID: $Id$ from wxPython.wx import * from wxPython.xrc import * from wxPython.html import wxHtmlWindow from xml.dom import minidom import os import getopt # Icons import images # Constants # Return code from wxGetOsVersion wxGTK = 9 if wxGetOsVersion()[0] == wxGTK: labelFont = wxFont(12, wxDEFAULT, wxNORMAL, wxBOLD) modernFont = wxFont(12, wxMODERN, wxNORMAL, wxNORMAL) else: labelFont = wxFont(10, wxDEFAULT, wxNORMAL, wxBOLD) modernFont = wxFont(10, wxMODERN, wxNORMAL, wxNORMAL) progname = 'XRCed' version = '0.0.7-3' # Local modules from xxx import * # Globals testWin = None testWinPos = wxDefaultPosition # 1 adds CMD command to Help menu debug = 0 helpText = """\

Welcome to XRCed!

DON'T PANIC :)

To start select tree root, then popup menu with your right mouse button, select "Append Child", and then any command.

Enter XML ID, change properties, create children.

To test your interface select Test command (View menu).

Consult README file for the details. """ defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME', xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR'} # Set menu to list items. # Each menu command is a tuple (id, label, help) # submenus are lists [id, label, help, submenu] # and separators are any other type def SetMenu(m, list): for l in list: if type(l) == types.TupleType: apply(m.Append, l) elif type(l) == types.ListType: subMenu = wxMenu() SetMenu(subMenu, l[2:]) m.AppendMenu(wxNewId(), l[0], subMenu, l[1]) else: # separator m.AppendSeparator() ################################################################################ # Properties panel containing notebook class Panel(wxNotebook): def __init__(self, parent, id = -1): wxNotebook.__init__(self, parent, id, style=wxNB_BOTTOM) sys.modules['params'].panel = self # List of child windows self.pages = [] # Create scrolled windows for pages self.page1 = wxScrolledWindow(self, -1) sizer = wxBoxSizer() sizer.Add(wxBoxSizer()) # dummy sizer self.page1.SetAutoLayout(true) self.page1.SetSizer(sizer) self.AddPage(self.page1, 'Properties') # Second page self.page2 = wxScrolledWindow(self, -1) sizer = wxBoxSizer() sizer.Add(wxBoxSizer()) # dummy sizer self.page2.SetAutoLayout(true) self.page2.SetSizer(sizer) # Cache for already used panels self.pageCache = {} # cached property panels self.stylePageCache = {} # cached style panels # Dummy parent window for cache pages self.cacheParent = wxFrame(None, -1, 'non visible') # Delete child windows and recreate page sizer def ResetPage(self, page): topSizer = page.GetSizer() sizer = topSizer.GetChildren()[0].GetSizer() for w in page.GetChildren(): sizer.RemoveWindow(w) if isinstance(w, ParamPage): # With SetParent, we wouldn't need this w.Reparent(self.cacheParent) else: w.Destroy() topSizer.RemoveSizer(sizer) # Create new windows sizer = wxBoxSizer(wxVERTICAL) # Special case - resize html window if conf.panic: topSizer.Add(sizer, 1, wxEXPAND) else: topSizer.Add(sizer, 0, wxALL, 5) return sizer def SetData(self, xxx): self.pages = [] # First page # Set cached or new page # Remove current objects and sizer sizer = self.ResetPage(self.page1) if not xxx or (not xxx.allParams and not xxx.hasName): if tree.selection: sizer.Add(wxStaticText(self.page1, -1, 'This item has no properties.')) else: # nothing selected # If first time, show some help if conf.panic: html = wxHtmlWindow(self.page1, -1, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER) html.SetPage(helpText) sizer.Add(html, 1, wxEXPAND) conf.panic = false else: sizer.Add(wxStaticText(self.page1, -1, 'Select a tree item.')) else: SetCurrentXXX(xxx.treeObject()) try: page = self.pageCache[xxx.__class__] page.Reparent(self.page1) except KeyError: page = PropPage(self.page1, xxx.className, xxx) self.pageCache[xxx.__class__] = page page.SetValues(xxx) self.pages.append(page) sizer.Add(page, 1, wxEXPAND) if xxx.hasChild: # Special label for child objects - they may have different GUI cacheID = (xxx.child.__class__, xxx.__class__) try: page = self.pageCache[cacheID] page.Reparent(self.page1) except KeyError: page = PropPage(self.page1, xxx.child.className, xxx.child) self.pageCache[cacheID] = page page.SetValues(xxx.child) self.pages.append(page) sizer.Add(page, 0, wxEXPAND | wxTOP, 5) self.page1.Layout() size = self.page1.GetSizer().GetMinSize() self.page1.SetScrollbars(1, 1, size.x, size.y, 0, 0, true) # Second page # Create if does not exist if xxx and xxx.treeObject().hasStyle: xxx = xxx.treeObject() # Simplest case: set data if class is the same sizer = self.ResetPage(self.page2) try: page = self.stylePageCache[xxx.__class__] page.Reparent(self.page2) except KeyError: page = StylePage(self.page2, xxx.className + ' style', xxx) self.stylePageCache[xxx.__class__] = page page.SetValues(xxx) self.pages.append(page) sizer.Add(page, 0, wxEXPAND) # Add page if not exists if not self.GetPageCount() == 2: self.AddPage(self.page2, 'Style') self.page2.Layout() size = self.page2.GetSizer().GetMinSize() self.page2.SetScrollbars(1, 1, size.x, size.y, 0, 0, true) else: # Remove page if exists if self.GetPageCount() == 2: self.SetSelection(0) self.page1.Refresh() self.RemovePage(1) def Clear(self): self.SetData(None) # Check if some parameter on some page has changed def IsModified(self): for p in self.pages: if p.IsModified(): return true return false # Reset changed state def SetModified(self, value): for p in self.pages: p.SetModified(value) def Apply(self): for p in self.pages: p.Apply() ################################################################################ # General class for notebook pages class ParamPage(wxPanel): def __init__(self, parent, xxx): wxPanel.__init__(self, parent, -1) self.xxx = xxx # Register event handlers for id in paramIDs.values(): EVT_CHECKBOX(self, id, self.OnCheckParams) self.modified = false self.checks = {} self.controls = {} # save python objects self.controlName = None def OnCheckParams(self, evt): xxx = self.xxx param = evt.GetEventObject().GetName() w = self.controls[param] objElem = xxx.element if evt.IsChecked(): # Ad new text node in order of allParams w.SetValue('') # set empty (default) value w.SetModified() # mark as changed elem = tree.dom.createElement(param) # Some classes are special if param == 'font': xxx.params[param] = xxxParamFont(xxx.element, elem) else: xxx.params[param] = xxxParam(elem) # Find place to put new element: first present element after param found = false paramStyles = xxx.allParams + xxx.styles for p in paramStyles[paramStyles.index(param) + 1:]: # Content params don't have same type if xxx.params.has_key(p) and p != 'content': found = true break if found: nextTextElem = xxx.params[p].node objElem.insertBefore(elem, nextTextElem) else: objElem.appendChild(elem) else: # Remove parameter xxx.params[param].remove() del xxx.params[param] w.SetValue('') w.modified = false # mark as not changed # Set modified flas self.SetModified(true) w.Enable(evt.IsChecked()) # If some parameter has changed def IsModified(self): return self.modified def SetModified(self, value): self.modified = value def Apply(self): xxx = self.xxx # !!! Save undo info # if xxx.undo: xxx.undo.unlink() # xxx.undo = xxx.element.cloneNode(false) if self.controlName: name = self.controlName.GetValue() if xxx.name != name: xxx.name = name xxx.element.setAttribute('name', name) for param, w in self.controls.items(): if w.modified: paramObj = xxx.params[param] value = w.GetValue() if param in xxx.specials: xxx.setSpecial(param, value) else: paramObj.update(value) ################################################################################ # Panel for displaying properties class PropPage(ParamPage): def __init__(self, parent, label, xxx): ParamPage.__init__(self, parent, xxx) box = wxStaticBox(self, -1, label) box.SetFont(labelFont) topSizer = wxStaticBoxSizer(box, wxVERTICAL) sizer = wxFlexGridSizer(len(xxx.allParams), 2, 1, 1) if xxx.hasName: label = wxStaticText(self, -1, 'XML ID:', size=(100,-1)) control = ParamText(self, name='XML_name') sizer.AddMany([ (label, 0, wxALIGN_CENTER_VERTICAL), (control, 0, wxALIGN_CENTER_VERTICAL) ]) self.controlName = control for param in xxx.allParams: present = param in xxx.params if param in xxx.required: label = wxStaticText(self, paramIDs[param], param + ':', size = (100,-1), name = param) else: # Notebook has one very loooooong parameter if param == 'usenotebooksizer': sParam = 'usesizer:' else: sParam = param + ':' label = wxCheckBox(self, paramIDs[param], sParam, size = (100,-1), name = param) self.checks[param] = label try: typeClass = xxx.paramDict[param] except KeyError: try: # Standart type typeClass = paramDict[param] except KeyError: # Default typeClass = ParamText control = typeClass(self, param) control.Enable(present) sizer.AddMany([ (label, 0, wxALIGN_CENTER_VERTICAL), (control, 0, wxALIGN_CENTER_VERTICAL) ]) self.controls[param] = control topSizer.Add(sizer, 1, wxALL | wxEXPAND, 3) self.SetAutoLayout(true) self.SetSizer(topSizer) topSizer.Fit(self) def SetValues(self, xxx): self.xxx = xxx # Set values, checkboxes to false, disable defaults if xxx.hasName: self.controlName.SetValue(xxx.name) for param in xxx.allParams: w = self.controls[param] w.modified = false try: value = xxx.params[param].value() w.Enable(true) w.SetValue(value) if not param in xxx.required: self.checks[param].SetValue(true) except KeyError: self.checks[param].SetValue(false) w.SetValue('') w.Enable(false) self.SetModified(false) ################################################################################ # Style notebook page class StylePage(ParamPage): def __init__(self, parent, label, xxx): ParamPage.__init__(self, parent, xxx) box = wxStaticBox(self, -1, label) box.SetFont(labelFont) topSizer = wxStaticBoxSizer(box, wxVERTICAL) sizer = wxFlexGridSizer(len(xxx.styles), 2, 1, 1) for param in xxx.styles: present = param in xxx.params.keys() check = wxCheckBox(self, paramIDs[param], param + ':', size = (100,-1), name = param) check.SetValue(present) control = paramDict[param](self, name = param) control.Enable(present) sizer.AddMany([ (check, 0, wxALIGN_CENTER_VERTICAL), (control, 0, wxALIGN_CENTER_VERTICAL) ]) self.checks[param] = check self.controls[param] = control topSizer.Add(sizer, 1, wxALL | wxEXPAND, 3) self.SetAutoLayout(true) self.SetSizer(topSizer) topSizer.Fit(self) # Set data for a cahced page def SetValues(self, xxx): self.xxx = xxx for param in xxx.styles: present = param in xxx.params.keys() check = self.checks[param] check.SetValue(present) w = self.controls[param] w.modified = false if present: w.SetValue(xxx.params[param].value()) else: w.SetValue('') w.Enable(present) self.SetModified(false) ################################################################################ class HightLightBox: def __init__(self, pos, size): w = testWin.panel l1 = wxWindow(w, -1, pos, wxSize(size.x, 2)) l1.SetBackgroundColour(wxRED) l2 = wxWindow(w, -1, pos, wxSize(2, size.y)) l2.SetBackgroundColour(wxRED) l3 = wxWindow(w, -1, wxPoint(pos.x + size.x - 2, pos.y), wxSize(2, size.y)) l3.SetBackgroundColour(wxRED) l4 = wxWindow(w, -1, wxPoint(pos.x, pos.y + size.y - 2), wxSize(size.x, 2)) l4.SetBackgroundColour(wxRED) self.lines = [l1, l2, l3, l4] # Move highlight to a new position def Replace(self, pos, size): self.lines[0].SetDimensions(pos.x, pos.y, size.x, 2, wxSIZE_ALLOW_MINUS_ONE) self.lines[1].SetDimensions(pos.x, pos.y, 2, size.y, wxSIZE_ALLOW_MINUS_ONE) self.lines[2].SetDimensions(pos.x + size.x - 2, pos.y, 2, size.y, wxSIZE_ALLOW_MINUS_ONE) self.lines[3].SetDimensions(pos.x, pos.y + size.y - 2, size.x, 2, wxSIZE_ALLOW_MINUS_ONE) # Remove it def Remove(self): map(wxWindow.Destroy, self.lines) testWin.highLight = None ################################################################################ class MemoryFile: def __init__(self, name): self.name = name self.buffer = '' def write(self, data): self.buffer += data.encode() def close(self): wxMemoryFSHandler_AddFile(self.name, self.buffer) class XML_Tree(wxTreeCtrl): def __init__(self, parent, id): wxTreeCtrl.__init__(self, parent, id, style = wxTR_HAS_BUTTONS) self.SetBackgroundColour(wxColour(224, 248, 224)) EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged) # One works on Linux, another on Windows if wxGetOsVersion()[0] == wxGTK: EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) else: EVT_LEFT_DCLICK(self, self.OnDClick) EVT_RIGHT_DOWN(self, self.OnRightDown) self.needUpdate = false self.pendingHighLight = None self.ctrl = self.shift = false self.dom = None # Create image list il = wxImageList(16, 16, true) self.rootImage = il.AddIcon(wxIconFromXPMData(images.getTreeRootData())) xxxObject.image = il.AddIcon(wxIconFromXPMData(images.getTreeDefaultData())) xxxPanel.image = il.AddIcon(wxIconFromXPMData(images.getTreePanelData())) xxxDialog.image = il.AddIcon(wxIconFromXPMData(images.getTreeDialogData())) xxxFrame.image = il.AddIcon(wxIconFromXPMData(images.getTreeFrameData())) xxxMenuBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuBarData())) xxxToolBar.image = il.AddIcon(wxIconFromXPMData(images.getTreeToolBarData())) xxxMenu.image = il.AddIcon(wxIconFromXPMData(images.getTreeMenuData())) xxxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeSizerHData())) xxxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeSizerVData())) xxxStaticBoxSizer.imageH = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerHData())) xxxStaticBoxSizer.imageV = il.AddIcon(wxIconFromXPMData(images.getTreeStaticBoxSizerVData())) xxxGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerGridData())) xxxFlexGridSizer.image = il.AddIcon(wxIconFromXPMData(images.getTreeSizerFlexGridData())) self.il = il self.SetImageList(il) def Unselect(self): self.selection = None wxTreeCtrl.Unselect(self) def ExpandAll(self, item): if self.ItemHasChildren(item): self.Expand(item) i, cookie = self.GetFirstChild(item, 0) children = [] while i.IsOk(): children.append(i) i, cookie = self.GetNextChild(item, cookie) for i in children: self.ExpandAll(i) def CollapseAll(self, item): if self.ItemHasChildren(item): i, cookie = self.GetFirstChild(item, 0) children = [] while i.IsOk(): children.append(i) i, cookie = self.GetNextChild(item, cookie) for i in children: self.CollapseAll(i) self.Collapse(item) # Clear tree def Clear(self): self.DeleteAllItems() # Add minimal structure if self.dom: self.dom.unlink() self.dom = minidom.Document() self.dummyNode = self.dom.createComment('dummy node') # Create main node self.mainNode = self.dom.createElement('resource') self.dom.appendChild(self.mainNode) xxx = xxxMainNode(None, self.mainNode) self.root = self.AddRoot('XML tree', self.rootImage, data=wxTreeItemData(xxx)) self.SetItemHasChildren(self.root) self.Expand(self.root) self.Unselect() # Clear old data and set new def SetData(self, dom): self.DeleteAllItems() # Add minimal structure if self.dom: self.dom.unlink() self.dom = dom self.dummyNode = self.dom.createComment('dummy node') # Find 'resource' child, add it's children self.mainNode = dom.getElementsByTagName('resource')[0] xxx = xxxMainNode(None, self.mainNode) self.root = self.AddRoot('XML tree', self.rootImage, data=wxTreeItemData(xxx)) self.SetItemHasChildren(self.root) nodes = self.mainNode.childNodes[:] for node in nodes: if IsObject(node): self.AddNode(self.root, None, node) else: self.mainNode.removeChild(node) node.unlink() self.Expand(self.root) self.Unselect() # Add tree item for given parent item if node is DOM element node with # 'object' tag. xxxParent is parent xxx object def AddNode(self, itemParent, xxxParent, node): # Set item data to current node try: xxx = MakeXXXFromDOM(xxxParent, node) except: print 'ERROR: MakeXXXFromDom(%s, %s)' % (xxxParent, node) raise treeObj = xxx.treeObject() # Append tree item item = self.AppendItem(itemParent, treeObj.treeName(), image=treeObj.treeImage(), data=wxTreeItemData(xxx)) # Try to find children objects if treeObj.hasChildren: nodes = treeObj.element.childNodes[:] for n in nodes: if IsObject(n): self.AddNode(item, treeObj, n) elif n.nodeType != minidom.Node.ELEMENT_NODE: treeObj.element.removeChild(n) n.unlink() # Remove leaf of tree, return it's data object def RemoveLeaf(self, leaf): xxx = self.GetPyData(leaf) node = xxx.element parent = node.parentNode parent.removeChild(node) self.Delete(leaf) # Reset selection object self.selection = None return node # Find position relative to the top-level window def FindNodePos(self, item): # Root at (0,0) if item == testWin.item: return wxPoint(0, 0) itemParent = self.GetItemParent(item) # Select NB page obj = self.FindNodeObject(item) if self.GetPyData(itemParent).treeObject().__class__ == xxxNotebook: notebook = self.FindNodeObject(itemParent) # Find position for i in range(notebook.GetPageCount()): if notebook.GetPage(i) == obj: if notebook.GetSelection() != i: notebook.SetSelection(i) break # Find first ancestor which is a wxWindow (not a sizer) winParent = itemParent while self.GetPyData(winParent).isSizer: winParent = self.GetItemParent(winParent) parentPos = self.FindNodePos(winParent) # Position (-1,-1) is really (0,0) pos = obj.GetPosition() if pos == (-1,-1): pos = (0,0) return parentPos + pos # Find window (or sizer) corresponding to a tree item. def FindNodeObject(self, item): if item == testWin.item: return testWin.panel itemParent = self.GetItemParent(item) # If top-level, return testWin (or panel if wxFrame) xxx = self.GetPyData(item).treeObject() parentWin = self.FindNodeObject(itemParent) # Top-level sizer? return window's sizer if xxx.isSizer and isinstance(parentWin, wxWindowPtr): return parentWin.GetSizer() # Otherwise get parent's object and it's child n = 0 # index of sibling prev = self.GetPrevSibling(item) while prev.IsOk(): prev = self.GetPrevSibling(prev) n += 1 child = parentWin.GetChildren()[n] # Return window or sizer for sizer items if child.GetClassName() == 'wxSizerItem': if child.IsWindow(): child = child.GetWindow() elif child.IsSizer(): child = child.GetSizer() # Test for notebook sizers if isinstance(child, wxNotebookSizerPtr): child = child.GetNotebook() return child def OnSelChanged(self, evt): # Apply changes # !!! problem with wxGTK - GetOldItem is Ok if nothing selected #oldItem = evt.GetOldItem() status = '' oldItem = self.selection if oldItem: xxx = self.GetPyData(oldItem) # If some data was modified, apply changes if panel.IsModified(): self.Apply(xxx, oldItem) #if conf.autoRefresh: if testWin: if testWin.highLight and not tree.IsHighlatable(oldItem): testWin.highLight.Remove() self.needUpdate = true status = 'Changes were applied' frame.SetStatusText(status) # Generate view self.selection = evt.GetItem() if not self.selection.IsOk(): self.selection = None return xxx = self.GetPyData(self.selection) # Update panel panel.SetData(xxx) # Clear flag panel.SetModified(false) # Hightlighting is done in OnIdle tree.pendingHighLight = self.selection # Check if item is in testWin subtree def IsHighlatable(self, item): if item == testWin.item: return false while item != self.root: item = self.GetItemParent(item) if item == testWin.item: return true return false # Highlight selected item def HighLight(self, item): self.pendingHighLight = None if not testWin or self.GetPyData(testWin.item).className \ not in ['wxDialog', 'wxPanel', 'wxFrame']: return # Top-level does not have highlight if item == testWin.item or item == tree.root: if testWin.highLight: testWin.highLight.Remove() return # If a control from another window is selected, remove highlight if not self.IsHighlatable(item): if testWin.highLight: testWin.highLight.Remove() return # Get window/sizer object obj, pos = self.FindNodeObject(item), self.FindNodePos(item) size = obj.GetSize() # Highlight # Nagative positions are not working wuite well if testWin.highLight: testWin.highLight.Replace(pos, size) else: testWin.highLight = HightLightBox(pos, size) testWin.highLight.item = item def ShowTestWindow(self, item): global testWin xxx = self.GetPyData(item) if panel.IsModified(): self.Apply(xxx, item) # apply changes treeObj = xxx.treeObject() if treeObj.className not in ['wxFrame', 'wxPanel', 'wxDialog', 'wxMenuBar', 'wxToolBar']: wxLogMessage('No view for this element (yet)') return if not treeObj.name: wxLogError("Can't display a noname element!") return # Show item in bold if testWin: self.SetItemBold(testWin.item, false) self.SetItemBold(item) self.CreateTestWin(item) # Double-click on Linux def OnItemActivated(self, evt): if evt.GetItem() != self.root: self.ShowTestWindow(evt.GetItem()) # Double-click on Windows def OnDClick(self, evt): item, flags = self.HitTest(evt.GetPosition()) if flags in [wxTREE_HITTEST_ONITEMBUTTON, wxTREE_HITTEST_ONITEMLABEL]: if item != self.root: self.ShowTestWindow(item) else: evt.Skip() # (re)create test window def CreateTestWin(self, item): global testWin wxBeginBusyCursor() # Create a window with this resource xxx = self.GetPyData(item).treeObject() # Close old window, remember where it was highLight = None if testWin: pos = testWin.GetPosition() if item == testWin.item: # Remember highlight if same top-level window if testWin.highLight: highLight = testWin.highLight.item # !!! if 0 is removed, refresh is broken (notebook not deleted?) if xxx.className == 'wxPanel': if testWin.highLight: testWin.pendingHighLight = highLight testWin.highLight.Remove() testWin.panel.Destroy() testWin.panel = None else: testWin.Destroy() testWin = None else: testWin.Destroy() testWin = None else: pos = testWinPos # Save in memory FS memFile = MemoryFile('xxx.xrc') # Create partial XML file - faster for big files dom = minidom.Document() mainNode = dom.createElement('resource') dom.appendChild(mainNode) # Remove temporarily from old parent elem = xxx.element parent = elem.parentNode next = elem.nextSibling parent.replaceChild(self.dummyNode, elem) # Append to new DOM, write it mainNode.appendChild(elem) dom.writexml(memFile) # Put back in place mainNode.removeChild(elem) dom.unlink() parent.replaceChild(elem, self.dummyNode) memFile.close() # write to wxMemoryFS res = wxXmlResource('') res.Load('memory:xxx.xrc') if xxx.className == 'wxFrame': # Create new frame testWin = wxPreFrame() res.LoadFrame(testWin, frame, xxx.name) # Create status bar testWin.CreateStatusBar() testWin.panel = testWin testWin.SetPosition(pos) testWin.Show(true) elif xxx.className == 'wxPanel': # Create new frame if not testWin: testWin = wxFrame(frame, -1, 'Panel: ' + xxx.name, pos=pos) testWin.panel = res.LoadPanel(testWin, xxx.name) testWin.SetClientSize(testWin.panel.GetSize()) testWin.Show(true) elif xxx.className == 'wxDialog': # Create new frame testWin = res.LoadDialog(None, xxx.name) testWin.panel = testWin testWin.Layout() testWin.SetPosition(pos) testWin.Show(true) elif xxx.className == 'wxMenuBar': testWin = wxFrame(frame, -1, 'MenuBar: ' + xxx.name, pos=pos) testWin.panel = None # Set status bar to display help testWin.CreateStatusBar() testWin.menuBar = res.LoadMenuBar(xxx.name) testWin.SetMenuBar(testWin.menuBar) testWin.Show(true) elif xxx.className == 'wxToolBar': testWin = wxFrame(frame, -1, 'ToolBar: ' + xxx.name, pos=pos) testWin.panel = None # Set status bar to display help testWin.CreateStatusBar() testWin.toolBar = res.LoadToolBar(testWin, xxx.name) testWin.SetToolBar(testWin.toolBar) testWin.Show(true) wxMemoryFSHandler_RemoveFile('xxx.xrc') testWin.item = item EVT_CLOSE(testWin, self.OnCloseTestWin) EVT_BUTTON(testWin, wxID_OK, self.OnCloseTestWin) EVT_BUTTON(testWin, wxID_CANCEL, self.OnCloseTestWin) testWin.highLight = None if highLight and not tree.pendingHighLight: self.HighLight(highLight) wxEndBusyCursor() def OnCloseTestWin(self, evt): global testWin, testWinPos self.SetItemBold(testWin.item, false) testWinPos = testWin.GetPosition() testWin.Destroy() testWin = None # Return item index in parent def ItemIndex(self, parent, item): i = 0 it, cookie = self.GetFirstChild(parent, 0) while it != item: i += 1 it, cookie = self.GetNextChild(parent, cookie) return i # True if next item should be inserted after current (vs. appended to it) def NeedInsert(self, item): xxx = self.GetPyData(item) if item == self.root: return false # root item if xxx.hasChildren and not self.GetChildrenCount(item, false): return false return not (self.IsExpanded(item) and self.GetChildrenCount(item, false)) # Pull-down def OnRightDown(self, evt): # select this item pt = evt.GetPosition(); item, flags = self.HitTest(pt) if item.Ok() and flags & wxTREE_HITTEST_ONITEM: self.SelectItem(item) # Setup menu menu = wxMenu() item = self.selection if not item: menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand tree') menu.Append(pullDownMenu.ID_COLLAPSE, 'Collapse', 'Collapse tree') else: self.ctrl = evt.ControlDown() # save Ctrl state self.shift = evt.ShiftDown() # and Shift too m = wxMenu() # create menu if self.ctrl: needInsert = true else: needInsert = self.NeedInsert(item) if item == self.root or needInsert and self.GetItemParent(item) == self.root: m.Append(pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel') m.Append(pullDownMenu.ID_NEW_DIALOG, 'Dialog', 'Create dialog') m.Append(pullDownMenu.ID_NEW_FRAME, 'Frame', 'Create frame') m.AppendSeparator() m.Append(pullDownMenu.ID_NEW_TOOL_BAR, 'ToolBar', 'Create toolbar') m.Append(pullDownMenu.ID_NEW_MENU_BAR, 'MenuBar', 'Create menubar') m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu') else: xxx = self.GetPyData(item).treeObject() # Check parent for possible child nodes if inserting sibling if needInsert: xxx = xxx.parent if xxx.__class__ == xxxMenuBar: m.Append(pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu') elif xxx.__class__ in [xxxToolBar, xxxTool] or \ xxx.__class__ == xxxSeparator and xxx.parent.__class__ == xxxToolBar: SetMenu(m, pullDownMenu.toolBarControls) elif xxx.__class__ in [xxxMenu, xxxMenuItem]: SetMenu(m, pullDownMenu.menuControls) else: SetMenu(m, pullDownMenu.controls) if xxx.__class__ == xxxNotebook: m.Enable(m.FindItem('sizer'), false) elif not (xxx.isSizer or xxx.parent and xxx.parent.isSizer): m.Enable(pullDownMenu.ID_NEW_SPACER, false) # Select correct label for create menu if not needInsert: if self.shift: menu.AppendMenu(wxNewId(), 'Insert Child', m, 'Create child object as the first child') else: menu.AppendMenu(wxNewId(), 'Append Child', m, 'Create child object as the last child') else: if self.shift: menu.AppendMenu(wxNewId(), 'Create Sibling', m, 'Create sibling before selected object') else: menu.AppendMenu(wxNewId(), 'Create Sibling', m, 'Create sibling after selected object') menu.AppendSeparator() # Not using standart IDs because we don't want to show shortcuts menu.Append(wxID_CUT, 'Cut', 'Cut to the clipboard') menu.Append(wxID_COPY, 'Copy', 'Copy to the clipboard') if self.ctrl and item != tree.root: menu.Append(pullDownMenu.ID_PASTE_SIBLING, 'Paste Sibling', 'Paste from the clipboard as a sibling') else: menu.Append(wxID_PASTE, 'Paste', 'Paste from the clipboard') menu.Append(pullDownMenu.ID_DELETE, 'Delete', 'Delete object') if self.ItemHasChildren(item): menu.AppendSeparator() menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand subtree') menu.Append(pullDownMenu.ID_COLLAPSE, 'Collapse', 'Collapse subtree') self.PopupMenu(menu, evt.GetPosition()) menu.Destroy() # Apply changes def Apply(self, xxx, item): panel.Apply() # Update tree view xxx = xxx.treeObject() if xxx.hasName and self.GetItemText(item) != xxx.name: self.SetItemText(item, xxx.treeName()) # Change tree icon for sizers if isinstance(xxx, xxxBoxSizer): self.SetItemImage(item, xxx.treeImage()) # Set global modified state frame.modified = true class PullDownMenu: ID_NEW_PANEL = wxNewId() ID_NEW_DIALOG = wxNewId() ID_NEW_FRAME = wxNewId() ID_NEW_TOOL_BAR = wxNewId() ID_NEW_TOOL = wxNewId() ID_NEW_MENU_BAR = wxNewId() ID_NEW_MENU = wxNewId() ID_NEW_STATIC_TEXT = wxNewId() ID_NEW_TEXT_CTRL = wxNewId() ID_NEW_BUTTON = wxNewId() ID_NEW_BITMAP_BUTTON = wxNewId() ID_NEW_RADIO_BUTTON = wxNewId() ID_NEW_SPIN_BUTTON = wxNewId() ID_NEW_STATIC_BOX = wxNewId() ID_NEW_CHECK_BOX = wxNewId() ID_NEW_RADIO_BOX = wxNewId() ID_NEW_COMBO_BOX = wxNewId() ID_NEW_LIST_BOX = wxNewId() ID_NEW_STATIC_LINE = wxNewId() ID_NEW_STATIC_BITMAP = wxNewId() ID_NEW_CHOICE = wxNewId() ID_NEW_SLIDER = wxNewId() ID_NEW_GAUGE = wxNewId() ID_NEW_SCROLL_BAR = wxNewId() ID_NEW_TREE_CTRL = wxNewId() ID_NEW_LIST_CTRL = wxNewId() ID_NEW_CHECK_LIST = wxNewId() ID_NEW_NOTEBOOK = wxNewId() ID_NEW_HTML_WINDOW = wxNewId() ID_NEW_CALENDAR = wxNewId() ID_NEW_BOX_SIZER = wxNewId() ID_NEW_STATIC_BOX_SIZER = wxNewId() ID_NEW_GRID_SIZER = wxNewId() ID_NEW_FLEX_GRID_SIZER = wxNewId() ID_NEW_SPACER = wxNewId() ID_NEW_TOOL_BAR = wxNewId() ID_NEW_TOOL = wxNewId() ID_NEW_MENU = wxNewId() ID_NEW_MENU_ITEM = wxNewId() ID_NEW_SEPARATOR = wxNewId() ID_NEW_LAST = wxNewId() ID_EXPAND = wxNewId() ID_COLLAPSE = wxNewId() ID_PASTE_SIBLING = wxNewId() def __init__(self, parent): self.ID_DELETE = parent.ID_DELETE EVT_MENU_RANGE(parent, self.ID_NEW_PANEL, self.ID_NEW_LAST, parent.OnCreate) EVT_MENU(parent, self.ID_COLLAPSE, parent.OnCollapse) EVT_MENU(parent, self.ID_EXPAND, parent.OnExpand) EVT_MENU(parent, self.ID_PASTE_SIBLING, parent.OnPaste) # We connect to tree, but process in frame EVT_MENU_HIGHLIGHT_ALL(tree, parent.OnPullDownHighlight) ################################################################################ # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font class ScrolledMessageDialog(wxDialog): def __init__(self, parent, msg, caption, pos = wxDefaultPosition, size = (500,300)): from wxPython.lib.layoutf import Layoutf wxDialog.__init__(self, parent, -1, caption, pos, size) text = wxTextCtrl(self, -1, msg, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY) text.SetFont(modernFont) dc = wxWindowDC(text) w, h = dc.GetTextExtent(' ') ok = wxButton(self, wxID_OK, "OK") text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok))) text.SetSize((w * 80 + 30, h * 40)) ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,))) self.SetAutoLayout(TRUE) self.Fit() self.CenterOnScreen(wxBOTH) ################################################################################ class Frame(wxFrame): def __init__(self, pos, size): global frame frame = self wxFrame.__init__(self, None, -1, '', pos, size) self.CreateStatusBar() icon = wxIcon(os.path.join(sys.path[0], 'xrced.ico'), wxBITMAP_TYPE_ICO) self.SetIcon(icon) # Idle flag self.inIdle = false # Make menus menuBar = wxMenuBar() menu = wxMenu() menu.Append(wxID_NEW, '&New\tCtrl-N', 'New file') menu.Append(wxID_OPEN, '&Open...\tCtrl-O', 'Open XRC file') menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file') menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name') menu.AppendSeparator() menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application') menuBar.Append(menu, '&File') menu = wxMenu() menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo') menu.Append(wxID_REDO, '&Redo\tCtrl-Y', 'Redo') menu.AppendSeparator() menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard') menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard') menu.Append(wxID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard') self.ID_DELETE = wxNewId() menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object') menuBar.Append(menu, '&Edit') menu = wxMenu() self.ID_EMBED_PANEL = wxNewId() menu.Append(self.ID_EMBED_PANEL, '&Embed Panel', 'Toggle embedding properties panel in the main window', true) menu.Check(self.ID_EMBED_PANEL, conf.embedPanel) menu.AppendSeparator() self.ID_TEST = wxNewId() menu.Append(self.ID_TEST, '&Test\tF5', 'Test window') self.ID_REFRESH = wxNewId() menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window') self.ID_AUTO_REFRESH = wxNewId() menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A', 'Toggle auto-refresh mode', true) menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) menuBar.Append(menu, '&View') menu = wxMenu() menu.Append(wxID_ABOUT, '&About...', 'About XCRed') self.ID_README = wxNewId() menu.Append(self.ID_README, '&Readme...', 'View the README file') if debug: self.ID_DEBUG_CMD = wxNewId() menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line') EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD) menuBar.Append(menu, '&Help') self.menuBar = menuBar self.SetMenuBar(menuBar) # Create toolbar tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT) tb.SetToolBitmapSize((24, 23)) tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file') tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file') tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file') tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut') tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy') tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste') tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) tb.AddSimpleTool(self.ID_TEST, images.getTestBitmap(), 'Test', 'Test window') tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(), 'Refresh', 'Refresh view') tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(), 'Auto-refresh', 'Toggle auto-refresh mode', true) if wxGetOsVersion()[0] == wxGTK: tb.AddSeparator() # otherwise auto-refresh sticks in status line tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) tb.Realize() self.tb = tb self.minWidth = tb.GetSize()[0] # minimal width is the size of toolbar # File EVT_MENU(self, wxID_NEW, self.OnNew) EVT_MENU(self, wxID_OPEN, self.OnOpen) EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs) EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs) EVT_MENU(self, wxID_EXIT, self.OnExit) # Edit EVT_MENU(self, wxID_UNDO, self.OnUndo) EVT_MENU(self, wxID_REDO, self.OnRedo) EVT_MENU(self, wxID_CUT, self.OnCut) EVT_MENU(self, wxID_COPY, self.OnCopy) EVT_MENU(self, wxID_PASTE, self.OnPaste) EVT_MENU(self, self.ID_DELETE, self.OnDelete) # View EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel) EVT_MENU(self, self.ID_TEST, self.OnTest) EVT_MENU(self, self.ID_REFRESH, self.OnRefresh) EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh) # Help EVT_MENU(self, wxID_ABOUT, self.OnAbout) EVT_MENU(self, self.ID_README, self.OnReadme) # Update events EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI) EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI) EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI) EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI) EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI) EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI) # Build interface sizer = wxBoxSizer(wxVERTICAL) sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND) splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH) self.splitter = splitter splitter.SetMinimumPaneSize(100) # Create tree global tree tree = XML_Tree(splitter, -1) sys.modules['xxx'].tree = tree # !!! frame styles are broken # Miniframe for not embedded mode miniFrame = wxFrame(self, -1, 'Properties Panel', (conf.panelX, conf.panelY), (conf.panelWidth, conf.panelHeight)) self.miniFrame = miniFrame sizer2 = wxBoxSizer() miniFrame.SetAutoLayout(true) miniFrame.SetSizer(sizer2) EVT_CLOSE(self.miniFrame, self.OnCloseMiniFrame) # Create panel for parameters global panel if conf.embedPanel: panel = Panel(splitter) # Set plitter windows splitter.SplitVertically(tree, panel, conf.sashPos) else: panel = Panel(miniFrame) sizer2.Add(panel, 1, wxEXPAND) miniFrame.Show(true) splitter.Initialize(tree) sizer.Add(splitter, 1, wxEXPAND) self.SetAutoLayout(true) self.SetSizer(sizer) # Init pull-down menu data global pullDownMenu pullDownMenu = PullDownMenu(self) # Mapping from IDs to element names self.createMap = { pullDownMenu.ID_NEW_PANEL: 'wxPanel', pullDownMenu.ID_NEW_DIALOG: 'wxDialog', pullDownMenu.ID_NEW_FRAME: 'wxFrame', pullDownMenu.ID_NEW_TOOL_BAR: 'wxToolBar', pullDownMenu.ID_NEW_TOOL: 'tool', pullDownMenu.ID_NEW_MENU_BAR: 'wxMenuBar', pullDownMenu.ID_NEW_MENU: 'wxMenu', pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem', pullDownMenu.ID_NEW_SEPARATOR: 'separator', pullDownMenu.ID_NEW_STATIC_TEXT: 'wxStaticText', pullDownMenu.ID_NEW_TEXT_CTRL: 'wxTextCtrl', pullDownMenu.ID_NEW_BUTTON: 'wxButton', pullDownMenu.ID_NEW_BITMAP_BUTTON: 'wxBitmapButton', pullDownMenu.ID_NEW_RADIO_BUTTON: 'wxRadioButton', pullDownMenu.ID_NEW_SPIN_BUTTON: 'wxSpinButton', pullDownMenu.ID_NEW_STATIC_BOX: 'wxStaticBox', pullDownMenu.ID_NEW_CHECK_BOX: 'wxCheckBox', pullDownMenu.ID_NEW_RADIO_BOX: 'wxRadioBox', pullDownMenu.ID_NEW_COMBO_BOX: 'wxComboBox', pullDownMenu.ID_NEW_LIST_BOX: 'wxListBox', pullDownMenu.ID_NEW_STATIC_LINE: 'wxStaticLine', pullDownMenu.ID_NEW_STATIC_BITMAP: 'wxStaticBitmap', pullDownMenu.ID_NEW_CHOICE: 'wxChoice', pullDownMenu.ID_NEW_SLIDER: 'wxSlider', pullDownMenu.ID_NEW_GAUGE: 'wxGauge', pullDownMenu.ID_NEW_SCROLL_BAR: 'wxScrollBar', pullDownMenu.ID_NEW_TREE_CTRL: 'wxTreeCtrl', pullDownMenu.ID_NEW_LIST_CTRL: 'wxListCtrl', pullDownMenu.ID_NEW_CHECK_LIST: 'wxCheckList', pullDownMenu.ID_NEW_NOTEBOOK: 'wxNotebook', pullDownMenu.ID_NEW_HTML_WINDOW: 'wxHtmlWindow', pullDownMenu.ID_NEW_CALENDAR: 'wxCalendar', pullDownMenu.ID_NEW_BOX_SIZER: 'wxBoxSizer', pullDownMenu.ID_NEW_STATIC_BOX_SIZER: 'wxStaticBoxSizer', pullDownMenu.ID_NEW_GRID_SIZER: 'wxGridSizer', pullDownMenu.ID_NEW_FLEX_GRID_SIZER: 'wxFlexGridSizer', pullDownMenu.ID_NEW_SPACER: 'spacer', } pullDownMenu.controls = [ ['control', 'Various controls', (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'), (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'), (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'), (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'), (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'), (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'), (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'), (pullDownMenu.ID_NEW_TREE_CTRL, 'TreeCtrl', 'Create tree control'), (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'), (pullDownMenu.ID_NEW_HTML_WINDOW, 'HtmlWindow', 'Create HTML window'), (pullDownMenu.ID_NEW_CALENDAR, 'Calendar', 'Create calendar control'), (pullDownMenu.ID_NEW_PANEL, 'Panel', 'Create panel'), (pullDownMenu.ID_NEW_NOTEBOOK, 'Notebook', 'Create notebook control'), ], ['button', 'Buttons', (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'), (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'), (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'), (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'), ], ['box', 'Boxes', (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'), (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'), (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'), (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'), (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'), (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox', 'Create check list control'), ], ['sizer', 'Sizers', (pullDownMenu.ID_NEW_BOX_SIZER, 'BoxSizer', 'Create box sizer'), (pullDownMenu.ID_NEW_STATIC_BOX_SIZER, 'StaticBoxSizer', 'Create static box sizer'), (pullDownMenu.ID_NEW_GRID_SIZER, 'GridSizer', 'Create grid sizer'), (pullDownMenu.ID_NEW_FLEX_GRID_SIZER, 'FlexGridSizer', 'Create flexgrid sizer'), (pullDownMenu.ID_NEW_SPACER, 'Spacer', 'Create spacer'), ] ] pullDownMenu.menuControls = [ (pullDownMenu.ID_NEW_MENU, 'Menu', 'Create menu'), (pullDownMenu.ID_NEW_MENU_ITEM, 'MenuItem', 'Create menu item'), (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'), ] pullDownMenu.toolBarControls = [ (pullDownMenu.ID_NEW_TOOL, 'Tool', 'Create tool'), (pullDownMenu.ID_NEW_SEPARATOR, 'Separator', 'Create separator'), ['control', 'Various controls', (pullDownMenu.ID_NEW_STATIC_TEXT, 'Label', 'Create static label'), (pullDownMenu.ID_NEW_STATIC_LINE, 'Line', 'Create static line'), (pullDownMenu.ID_NEW_TEXT_CTRL, 'TextBox', 'Create text box control'), (pullDownMenu.ID_NEW_CHOICE, 'Choice', 'Create choice control'), (pullDownMenu.ID_NEW_SLIDER, 'Slider', 'Create slider control'), (pullDownMenu.ID_NEW_GAUGE, 'Gauge', 'Create gauge control'), (pullDownMenu.ID_NEW_SCROLL_BAR, 'ScrollBar', 'Create scroll bar'), (pullDownMenu.ID_NEW_LIST_CTRL, 'ListCtrl', 'Create list control'), ], ['button', 'Buttons', (pullDownMenu.ID_NEW_BUTTON, 'Button', 'Create button'), (pullDownMenu.ID_NEW_BITMAP_BUTTON, 'BitmapButton', 'Create bitmap button'), (pullDownMenu.ID_NEW_RADIO_BUTTON, 'RadioButton', 'Create radio button'), (pullDownMenu.ID_NEW_SPIN_BUTTON, 'SpinButton', 'Create spin button'), ], ['box', 'Boxes', (pullDownMenu.ID_NEW_STATIC_BOX, 'StaticBox', 'Create static box'), (pullDownMenu.ID_NEW_CHECK_BOX, 'CheckBox', 'Create check box'), (pullDownMenu.ID_NEW_RADIO_BOX, 'RadioBox', 'Create radio box'), (pullDownMenu.ID_NEW_COMBO_BOX, 'ComboBox', 'Create combo box'), (pullDownMenu.ID_NEW_LIST_BOX, 'ListBox', 'Create list box'), (pullDownMenu.ID_NEW_CHECK_LIST, 'CheckListBox', 'Create check list control'), ], ] # Initialize self.Clear() # Other events EVT_IDLE(self, self.OnIdle) EVT_CLOSE(self, self.OnCloseWindow) def OnNew(self, evt): self.Clear() def OnOpen(self, evt): if not self.AskSave(): return dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile), '', '*.xrc', wxOPEN | wxCHANGE_DIR) if dlg.ShowModal() == wxID_OK: path = dlg.GetPath() self.SetStatusText('Loading...') wxYield() wxBeginBusyCursor() try: self.Open(path) self.SetStatusText('Data loaded') except: self.SetStatusText('Failed') raise wxEndBusyCursor() dlg.Destroy() def OnSaveOrSaveAs(self, evt): if evt.GetId() == wxID_SAVEAS or not self.dataFile: if self.dataFile: defaultName = '' else: defaultName = 'UNTITLED.xrc' dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile), defaultName, '*.xrc', wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR) if dlg.ShowModal() == wxID_OK: path = dlg.GetPath() dlg.Destroy() else: dlg.Destroy() return else: path = self.dataFile self.SetStatusText('Saving...') wxYield() wxBeginBusyCursor() try: self.Save(path) self.dataFile = path self.SetStatusText('Data saved') except IOError: self.SetStatusText('Failed') wxEndBusyCursor() def OnExit(self, evt): self.Close() def OnUndo(self, evt): print '*** being implemented' return print self.lastOp, self.undo if self.lastOp == 'DELETE': parent, prev, elem = self.undo if prev.IsOk(): xxx = MakeXXXFromDOM(tree.GetPyData(parent).treeObject(), elem) item = tree.InsertItem( parent, prev, xxx.treeObject().className, data=wxTreeItemData(xxx) ) def OnRedo(self, evt): print '*** being implemented' def OnCut(self, evt): selected = tree.selection if not selected: return # key pressed event # Undo info self.lastOp = 'CUT' self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] # Delete testWin? global testWin if testWin: # If deleting top-level item, delete testWin if selected == testWin.item: testWin.Destroy() testWin = None else: # Remove highlight, update testWin if not tree.IsHighlatable(selected): if testWin.highLight: testWin.highLight.Remove() tree.needUpdate = true self.clipboard = tree.RemoveLeaf(selected) tree.pendingHighLight = None tree.Unselect() panel.Clear() self.modified = true self.SetStatusText('Removed to clipboard') def OnCopy(self, evt): selected = tree.selection if not selected: return # key pressed event xxx = tree.GetPyData(selected) self.clipboard = xxx.element.cloneNode(true) self.SetStatusText('Copied') def OnPaste(self, evt): selected = tree.selection if not selected: return # key pressed event # For pasting with Ctrl pressed if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = false else: appendChild = not tree.NeedInsert(selected) xxx = tree.GetPyData(selected) if not appendChild: # If has next item, insert, else append to parent nextItem = tree.GetNextSibling(selected) if nextItem.IsOk(): # Insert before nextItem parentLeaf = tree.GetItemParent(selected) else: # last child: change selected to parent appendChild = true selected = tree.GetItemParent(selected) # Expanded container (must have children) elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, false): appendChild = false nextItem = tree.GetFirstChild(selected, 0)[0] parentLeaf = selected # Parent should be tree element or None if appendChild: parent = tree.GetPyData(selected) else: parent = tree.GetPyData(parentLeaf) if parent.hasChild: parent = parent.child # Create a copy of clipboard element elem = self.clipboard.cloneNode(true) # Tempopary xxx object to test things xxx = MakeXXXFromDOM(parent, elem) # Check compatibility error = false # Top-level x = xxx.treeObject() if x.__class__ in [xxxDialog, xxxFrame, xxxMenuBar, xxxToolBar]: if parent.__class__ != xxxMainNode: error = true elif x.__class__ == xxxPanel and parent.__class__ == xxxMainNode: pass elif x.__class__ == xxxSpacer: if not parent.isSizer: error = true elif x.__class__ == xxxSeparator: if not parent.__class__ in [xxxMenu, xxxToolBar]: error = true elif x.__class__ == xxxTool: if parent.__class__ != xxxToolBar: error = true elif x.__class__ == xxxMenuItem: if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = true elif x.isSizer and parent.__class__ == xxxNotebook: error = true else: # normal controls can be almost anywhere if parent.__class__ == xxxMainNode or \ parent.__class__ in [xxxMenuBar, xxxMenu]: error = true if error: if parent.__class__ == xxxMainNode: parentClass = 'root' else: parentClass = parent.className wxLogError('Incompatible parent/child: parent is %s, child is %s!' % (parentClass, x.className)) return # Check parent and child relationships. # If parent is sizer or notebook, child is of wrong class or # parent is normal window, child is child container then detach child. isChildContainer = isinstance(xxx, xxxChildContainer) if isChildContainer and \ ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \ not (parent.isSizer or isinstance(parent, xxxNotebook))): elem.removeChild(xxx.child.element) # detach child elem.unlink() # delete child container elem = xxx.child.element # replace # This may help garbage collection xxx.child.parent = None isChildContainer = false # Parent is sizer or notebook, child is not child container if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer): # Create sizer item element sizerItemElem = MakeEmptyDOM('sizeritem') sizerItemElem.appendChild(elem) elem = sizerItemElem elif isinstance(parent, xxxNotebook) and not isChildContainer: pageElem = MakeEmptyDOM('notebookpage') pageElem.appendChild(elem) elem = pageElem xxx = MakeXXXFromDOM(parent, elem) # Figure out if we must append a new child or sibling if appendChild: parent.element.appendChild(elem) newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), data=wxTreeItemData(xxx)) else: node = tree.GetPyData(nextItem).element parent.element.insertBefore(elem, node) # Inserting before is difficult, se we insert after or first child index = tree.ItemIndex(parentLeaf, nextItem) newItem = tree.InsertItemBefore(parentLeaf, index, xxx.treeName(), image=xxx.treeImage()) tree.SetPyData(newItem, xxx) # newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(), # image=xxx.treeImage(), data=wxTreeItemData(xxx)) # Add children items if xxx.hasChildren: treeObj = xxx.treeObject() for n in treeObj.element.childNodes: if IsObject(n): tree.AddNode(newItem, treeObj, n) # Scroll to show new item tree.EnsureVisible(newItem) tree.SelectItem(newItem) if not tree.IsVisible(newItem): tree.ScrollTo(newItem) tree.Refresh() # Update view? if testWin and tree.IsHighlatable(newItem): if conf.autoRefresh: tree.needUpdate = true tree.pendingHighLight = newItem else: tree.pendingHighLight = None self.modified = true self.SetStatusText('Pasted') def OnDelete(self, evt): selected = tree.selection if not selected: return # key pressed event # Undo info self.lastOp = 'DELETE' self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] # Delete testWin? global testWin if testWin: # If deleting top-level item, delete testWin if selected == testWin.item: testWin.Destroy() testWin = None else: # Remove highlight, update testWin if not tree.IsHighlatable(selected): if testWin.highLight: testWin.highLight.Remove() tree.needUpdate = true xnode = tree.RemoveLeaf(selected) # !!! cloneNode is broken, or something is wrong # self.undo.append(xnode.cloneNode(true)) xnode.unlink() tree.pendingHighLight = None tree.Unselect() panel.Clear() self.modified = true self.SetStatusText('Deleted') def OnEmbedPanel(self, evt): conf.embedPanel = evt.IsChecked() if conf.embedPanel: # Remember last dimentions conf.panelX, conf.panelY = self.miniFrame.GetPosition() conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() size = self.GetSize() pos = self.GetPosition() sizePanel = panel.GetSize() panel.Reparent(self.splitter) self.miniFrame.GetSizer().RemoveWindow(panel) wxYield() # Widen self.SetDimensions(pos.x, pos.y, size.x + sizePanel.x, size.y) self.splitter.SplitVertically(tree, panel, conf.sashPos) self.miniFrame.Show(false) else: conf.sashPos = self.splitter.GetSashPosition() pos = self.GetPosition() size = self.GetSize() sizePanel = panel.GetSize() self.splitter.Unsplit(panel) sizer = self.miniFrame.GetSizer() panel.Reparent(self.miniFrame) panel.Show(true) sizer.Add(panel, 1, wxEXPAND) self.miniFrame.Show(true) self.miniFrame.SetDimensions(conf.panelX, conf.panelY, conf.panelWidth, conf.panelHeight) wxYield() # Reduce width self.SetDimensions(pos.x, pos.y, max(size.x - sizePanel.x, self.minWidth), size.y) def OnTest(self, evt): if not tree.selection: return # key pressed event tree.ShowTestWindow(tree.selection) def OnRefresh(self, evt): # If modified, apply first selection = tree.selection if selection: xxx = tree.GetPyData(selection) if xxx and panel.IsModified(): tree.Apply(xxx, selection) if testWin: # (re)create tree.CreateTestWin(testWin.item) tree.needUpdate = false def OnAutoRefresh(self, evt): conf.autoRefresh = evt.IsChecked() self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) def OnAbout(self, evt): str = '%s %s\n\nRoman Rolinsky ' % \ (progname, version) dlg = wxMessageDialog(self, str, 'About ' + progname, wxOK | wxCENTRE) dlg.ShowModal() dlg.Destroy() def OnReadme(self, evt): text = open(os.path.join(sys.path[0], 'README'), 'r').read() dlg = ScrolledMessageDialog(self, text, "XRCed README") dlg.ShowModal() dlg.Destroy() # Simple emulation of python command line def OnDebugCMD(self, evt): import traceback while 1: try: exec raw_input('C:\> ') except EOFError: print '^D' break except: (etype, value, tb) =sys.exc_info() tblist =traceback.extract_tb(tb)[1:] msg =string.join(traceback.format_exception_only(etype, value) +traceback.format_list(tblist)) print msg def OnCreate(self, evt): selected = tree.selection if tree.ctrl: appendChild = false else: appendChild = not tree.NeedInsert(selected) xxx = tree.GetPyData(selected) if not appendChild: # If insert before if tree.shift: # If has previous item, insert after it, else append to parent nextItem = selected parentLeaf = tree.GetItemParent(selected) else: # If has next item, insert, else append to parent nextItem = tree.GetNextSibling(selected) if nextItem.IsOk(): # Insert before nextItem parentLeaf = tree.GetItemParent(selected) else: # last child: change selected to parent appendChild = true selected = tree.GetItemParent(selected) # Expanded container (must have children) elif tree.shift and tree.IsExpanded(selected) \ and tree.GetChildrenCount(selected, false): appendChild = false nextItem = tree.GetFirstChild(selected, 0)[0] parentLeaf = selected # Parent should be tree element or None if appendChild: parent = tree.GetPyData(selected) else: parent = tree.GetPyData(parentLeaf) if parent.hasChild: parent = parent.child # Create element className = self.createMap[evt.GetId()] xxx = MakeEmptyXXX(parent, className) # Set default name for top-level windows if parent.__class__ == xxxMainNode: cl = xxx.treeObject().__class__ frame.maxIDs[cl] += 1 xxx.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl]) xxx.treeObject().element.setAttribute('name', xxx.treeObject().name) # Figure out if we must append a new child or sibling elem = xxx.element if appendChild: # Insert newline for debug purposes parent.element.appendChild(elem) newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), data=wxTreeItemData(xxx)) else: node = tree.GetPyData(nextItem).element parent.element.insertBefore(elem, node) # !!! There is a different behavious on Win and GTK # !!! On Win InsertItem(parent, parent, ...) inserts at the end. index = tree.ItemIndex(parentLeaf, nextItem) newItem = tree.InsertItemBefore(parentLeaf, index, xxx.treeName(), image=xxx.treeImage()) # data=wxTreeItemData(xxx)) # does not work tree.SetPyData(newItem, xxx) # newItem = tree.InsertItem(parentLeaf, selected, # xxx.treeName(), image=xxx.treeImage(), # data=wxTreeItemData(xxx)) tree.EnsureVisible(newItem) tree.SelectItem(newItem) if not tree.IsVisible(newItem): tree.ScrollTo(newItem) tree.Refresh() # Update view? if testWin and tree.IsHighlatable(newItem): if conf.autoRefresh: tree.needUpdate = true tree.pendingHighLight = newItem else: tree.pendingHighLight = None # Expand/collapse subtree def OnExpand(self, evt): if tree.selection: tree.ExpandAll(tree.selection) else: tree.ExpandAll(tree.root) def OnCollapse(self, evt): if tree.selection: tree.CollapseAll(tree.selection) else: tree.CollapseAll(tree.root) def OnPullDownHighlight(self, evt): menuId = evt.GetMenuId() if menuId != -1: menu = evt.GetEventObject() help = menu.GetHelpString(menuId) self.SetStatusText(help) else: self.SetStatusText('') def OnUpdateUI(self, evt): if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]: evt.Enable(tree.selection != tree.root) elif evt.GetId() == wxID_PASTE: evt.Enable((self.clipboard and tree.selection) != None) elif evt.GetId() == self.ID_TEST: evt.Enable(tree.selection != tree.root) def OnIdle(self, evt): if self.inIdle: return # Recursive call protection self.inIdle = true if tree.needUpdate: if conf.autoRefresh: if testWin: self.SetStatusText('Refreshing test window...') # (re)create tree.CreateTestWin(testWin.item) wxYield() self.SetStatusText('') tree.needUpdate = false elif tree.pendingHighLight: tree.HighLight(tree.pendingHighLight) else: evt.Skip() self.inIdle = false # We don't let close panel window def OnCloseMiniFrame(self, evt): return def OnCloseWindow(self, evt): if not self.AskSave(): return if testWin: testWin.Destroy() # Destroy cached windows panel.cacheParent.Destroy() if not panel.GetPageCount() == 2: panel.page2.Destroy() conf.x, conf.y = self.GetPosition() conf.width, conf.height = self.GetSize() if conf.embedPanel: conf.sashPos = self.splitter.GetSashPosition() else: conf.panelX, conf.panelY = self.miniFrame.GetPosition() conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() evt.Skip() def Clear(self): self.dataFile = '' self.clipboard = None self.modified = false panel.SetModified(false) tree.Clear() panel.Clear() global testWin if testWin: testWin.Destroy() testWin = None self.SetTitle(progname) # Numbers for new controls self.maxIDs = {} self.maxIDs[xxxPanel] = self.maxIDs[xxxDialog] = self.maxIDs[xxxFrame] = \ self.maxIDs[xxxMenuBar] = self.maxIDs[xxxMenu] = self.maxIDs[xxxToolBar] = 0 def Open(self, path): # Try to read the file try: open(path) self.Clear() # Build wx tree dom = minidom.parse(path) tree.SetData(dom) self.dataFile = path self.SetTitle(progname + ': ' + os.path.basename(path)) except: wxLogError('Error reading file: %s' % path) raise def Indent(self, node, indent = 0): # Copy child list because it will change soon children = node.childNodes[:] # Main node doesn't need to be indented if indent: text = self.domCopy.createTextNode('\n' + ' ' * indent) node.parentNode.insertBefore(text, node) if children: # Append newline after last child, except for text nodes if children[-1].nodeType == minidom.Node.ELEMENT_NODE: text = self.domCopy.createTextNode('\n' + ' ' * indent) node.appendChild(text) # Indent children which are elements for n in children: if n.nodeType == minidom.Node.ELEMENT_NODE: self.Indent(n, indent + 2) def Save(self, path): try: # Apply changes self.OnRefresh(wxCommandEvent()) f = open(path, 'w') # Make temporary copy # !!! We can't clone dom node, it works only once #self.domCopy = tree.dom.cloneNode(true) self.domCopy = minidom.Document() mainNode = self.domCopy.appendChild(tree.mainNode.cloneNode(true)) self.Indent(mainNode) self.domCopy.writexml(f) f.close() self.domCopy.unlink() self.domCopy = None self.modified = false panel.SetModified(false) except: wxLogError('Error writing file: %s' % path) raise def AskSave(self): if not (self.modified or panel.IsModified()): return true flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE dlg = wxMessageDialog( self, 'File is modified. Save before exit?', 'Save before too late?', flags ) say = dlg.ShowModal() dlg.Destroy() if say == wxID_YES: self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE)) # If save was successful, modified flag is unset if not self.modified: return true elif say == wxID_NO: self.modified = false panel.SetModified(false) return true return false ################################################################################ def usage(): print >> sys.stderr, 'usage: xrced [-dvh] [file]' class App(wxApp): def OnInit(self): global debug, verbose # Process comand-line try: opts, args = getopt.getopt(sys.argv[1:], 'dvh') except getopt.GetoptError: print >> sys.stderr, 'Unknown option' usage() sys.exit(1) for o,a in opts: if o == '-h': usage() sys.exit(0) elif o == '-d': debug = true elif o == '-v': print 'XRCed version', version sys.exit(0) self.SetAppName('xrced') # Settings global conf conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE) conf.autoRefresh = conf.ReadInt('autorefresh', true) pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1) size = conf.ReadInt('width', 800), conf.ReadInt('height', 600) conf.embedPanel = conf.ReadInt('embedPanel', true) conf.sashPos = conf.ReadInt('sashPos', 200) if not conf.embedPanel: conf.panelX = conf.ReadInt('panelX', -1) conf.panelY = conf.ReadInt('panelY', -1) else: conf.panelX = conf.panelY = -1 conf.panelWidth = conf.ReadInt('panelWidth', 200) conf.panelHeight = conf.ReadInt('panelHeight', 200) conf.panic = not conf.HasEntry('nopanic') # Add handlers wxFileSystem_AddHandler(wxMemoryFSHandler()) wxInitAllImageHandlers() # Create main frame frame = Frame(pos, size) frame.Show(true) # Load resources from XRC file (!!! should be transformed to .py later?) sys.modules['params'].frame = frame frame.res = wxXmlResource('') frame.res.Load(os.path.join(sys.path[0], 'xrced.xrc')) # Load file after showing if args: conf.panic = false frame.open = frame.Open(args[0]) return true def OnExit(self): # Write config wc = wxConfigBase_Get() wc.WriteInt('autorefresh', conf.autoRefresh) wc.WriteInt('x', conf.x) wc.WriteInt('y', conf.y) wc.WriteInt('width', conf.width) wc.WriteInt('height', conf.height) wc.WriteInt('embedPanel', conf.embedPanel) if not conf.embedPanel: wc.WriteInt('panelX', conf.panelX) wc.WriteInt('panelY', conf.panelY) wc.WriteInt('sashPos', conf.sashPos) wc.WriteInt('panelWidth', conf.panelWidth) wc.WriteInt('panelHeight', conf.panelHeight) wc.WriteInt('nopanic', 1) wc.Flush() def main(): app = App() app.MainLoop() app.OnExit() if __name__ == '__main__': main()