X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/09f3d4e679f5742c54fb062ede846b77606a9c5b..490da436c859b499a7726acd06af02304b0d925f:/wxPython/tools/XRCed/xrced.py diff --git a/wxPython/tools/XRCed/xrced.py b/wxPython/tools/XRCed/xrced.py index 6d19390d8b..503c1b1b0f 100644 --- a/wxPython/tools/XRCed/xrced.py +++ b/wxPython/tools/XRCed/xrced.py @@ -2,22 +2,32 @@ # 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 * -import wxPython.lib.wxpTag +from wxPython.html import wxHtmlWindow from xml.dom import minidom import os -import tempfile +import getopt +# Icons import images -# String constants -htmlHeader = '\n' -htmlFooter = '\n' +# 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.3' +version = '0.0.7-3' # Local modules from xxx import * @@ -27,11 +37,19 @@ testWin = None testWinPos = wxDefaultPosition # 1 adds CMD command to Help menu -debug = 1 +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. +""" -if debug: - import traceback - import time +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) @@ -48,68 +66,322 @@ def SetMenu(m, list): else: # separator m.AppendSeparator() -# Properties panel -class Panel(wxHtmlWindow): - def __init__(self, parent, id): - wxHtmlWindow.__init__(self, parent, id) - self.SetBorders(5) - self.SetFonts('', '', [8, 10, 12, 14, 16, 19, 24]) - EVT_CHECKBOX(self, xxxObject.ID_CHECK_PARAMS, self.OnCheckParams) - EVT_CHECKBOX(self, xxxChildContainer.ID_CHECK_PARAMS, self.OnCheckParams) - self.modified = false - +################################################################################ + +# 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.SetPage(htmlHeader + 'select a tree item on the left' + htmlFooter) - + 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): - selected = tree.GetSelection() - xxx = tree.GetPyData(selected) - if xxx.hasChild and evt.GetId() != xxxChildContainer.ID_CHECK_PARAMS: - xxx = xxx.child - # Set current object - param = evt.GetEventObject().GetName()[6:] - if xxx.hasChild: - w = GetRegistered('_'+param) - else: - w = GetRegistered(param) - elem = xxx.element + 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('') - textElem = tree.dom.createElement(param) - textNode = tree.dom.createTextNode('') - textElem.appendChild(textNode) + 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 - for p in xxx.allParams[xxx.allParams.index(param) + 1:]: + 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].parentNode - elem.insertBefore(textElem, nextTextElem) + nextTextElem = xxx.params[p].node + objElem.insertBefore(elem, nextTextElem) else: - elem.appendChild(textElem) - xxx.params[param] = textNode + objElem.appendChild(elem) else: - # Remove parameter element and following text node - textElem = xxx.params[param].parentNode - newline = textElem.nextSibling - if newline and newline.nodeType == minidom.Node.TEXT_NODE: - elem.removeChild(newline) - elem.removeChild(textElem) + # 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()) - # Set modified flas - self.SetModified(true) - - # If some parameter was changed + # 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): @@ -136,52 +408,55 @@ class HightLightBox: 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 = self.buffer + data.encode() + self.buffer += data.encode() def close(self): - f = open(self.name, 'w') - f.write(self.buffer) - f.close() - # !!! memory FS will work someday - #self.file = wxMemoryFSHandler_AddFile(self.name, self.buffer) + wxMemoryFSHandler_AddFile(self.name, self.buffer) class XML_Tree(wxTreeCtrl): def __init__(self, parent, id): - wxTreeCtrl.__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) - EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated) + # 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 = false + self.ctrl = self.shift = false self.dom = None # Create image list il = wxImageList(16, 16, true) - 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()) ) - 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.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) - # !!! temporary solution for GetOldItem problem def Unselect(self): - self.selection = wxTreeItemId() + self.selection = None wxTreeCtrl.Unselect(self) - def GetSelection(self): - return self.selection def ExpandAll(self, item): if self.ItemHasChildren(item): @@ -193,25 +468,64 @@ class XML_Tree(wxTreeCtrl): 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.GetRootItem(), None, 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 - xxx = MakeXXXFromDOM(xxxParent, 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(), @@ -226,8 +540,6 @@ class XML_Tree(wxTreeCtrl): 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) @@ -235,30 +547,37 @@ class XML_Tree(wxTreeCtrl): parent = node.parentNode parent.removeChild(node) self.Delete(leaf) - # Update view? - #if testWin and self.GetItemAncestor(leaf) == testWin.item: - # if testWin.highLight: - # testWin.highLight.Remove() - # self.needUpdate = true + # 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) - if itemParent == self.GetRootItem(): return wxPoint(0, 0) + # 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) - return parentPos + obj.GetPosition() - + # 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) - if itemParent == self.GetRootItem(): return testWin.panel xxx = self.GetPyData(item).treeObject() parentWin = self.FindNodeObject(itemParent) # Top-level sizer? return window's sizer @@ -280,82 +599,43 @@ class XML_Tree(wxTreeCtrl): if isinstance(child, wxNotebookSizerPtr): child = child.GetNotebook() return child - def OnSelChanged(self, evt): # Apply changes - # !!! problem with wxGTK + # !!! problem with wxGTK - GetOldItem is Ok if nothing selected #oldItem = evt.GetOldItem() + status = '' oldItem = self.selection - if oldItem.IsOk(): + if oldItem: xxx = self.GetPyData(oldItem) # If some data was modified, apply changes - if xxx: - if panel.IsModified(): - self.Apply(xxx, oldItem) - #if conf.autoRefresh: - if testWin and testWin.highLight: + 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 - # Generate HTML view - item = evt.GetItem() - self.selection = item # !!! fix - xxx = self.GetPyData(item) - html = htmlHeader - # List of parameters tuples (parameter, isDefined) - if not xxx: # root item - html += 'this item has no properties' + htmlFooter - panel.SetPage(html) - if testWin and testWin.highLight: - testWin.highLight.Remove() + status = 'Changes were applied' + frame.SetStatusText(status) + # Generate view + self.selection = evt.GetItem() + if not self.selection.IsOk(): + self.selection = None return - # Normal nodes - ClearRegister() # empty register - html += xxx.generateHtml() - html += htmlFooter - panel.SetPage(html) - # Set values, checkboxes to false, disable defaults - if xxx.hasChild: prefix = '_' - else: prefix = '' - for param in xxx.allParams: - if xxx.params.has_key(param): - if param == 'content': - value = [] - for text in xxx.params[param]: - value.append(str(text.data)) # convert from unicode - else: - value = xxx.params[param].data - GetRegistered(prefix + param).SetValue(value) - if not param in xxx.required: - panel.FindWindowByName('check_' + param).SetValue(true) - else: - GetRegistered(prefix + param).Enable(false) - # Same for the child of sizeritem - if xxx.hasChild: - xxx = xxx.child - for param in xxx.allParams: - if xxx.params.has_key(param): - if param == 'content': - value = [] - for text in xxx.params[param]: - value.append(str(text.data)) # convert from unicode - else: - value = xxx.params[param].data - GetRegistered(param).SetValue(value) - if not param in xxx.required: - panel.FindWindowByName('check_' + param).SetValue(true) - else: - GetRegistered(param).Enable(false) + xxx = self.GetPyData(self.selection) + # Update panel + panel.SetData(xxx) # Clear flag panel.SetModified(false) # Hightlighting is done in OnIdle - tree.pendingHighLight = item - - # Find top-level parent - def GetItemAncestor(self, item): - while self.GetItemParent(item) != self.GetRootItem(): + 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) - return item - + if item == testWin.item: return true + return false # Highlight selected item def HighLight(self, item): self.pendingHighLight = None @@ -363,63 +643,68 @@ class XML_Tree(wxTreeCtrl): not in ['wxDialog', 'wxPanel', 'wxFrame']: return # Top-level does not have highlight - if item == testWin.item: + 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 self.GetItemAncestor(item) != testWin.item and testWin.highLight: - testWin.highLight.Remove() + 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() - # For notebook, select item's page. - # For children of page, nothing happens (too much work) - if isinstance(self.GetPyData(item).parent, xxxNotebook): - notebook = self.FindNodeObject(self.GetItemParent(item)) - # Find position - n = 0 - prev = self.GetPrevSibling(item) - while prev.IsOk(): - n += 1 - prev = self.GetPrevSibling(prev) - notebook.SetSelection(n) # Highlight - try: # finally I use exceptions + # Nagative positions are not working wuite well + if testWin.highLight: testWin.highLight.Replace(pos, size) - except AttributeError: + else: testWin.highLight = HightLightBox(pos, size) testWin.highLight.item = item - - # Double-click - def OnItemActivated(self, evt): - item = evt.GetItem() + def ShowTestWindow(self, item): + global testWin xxx = self.GetPyData(item) - if not xxx: return # if root selected, do nothing 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, node): + def CreateTestWin(self, item): global testWin + wxBeginBusyCursor() # Create a window with this resource - xxx = self.GetPyData(node).treeObject() - if not xxx: return # if root selected, do nothing - # If noname element, display error - if not xxx.hasName or not xxx.name: - wxLogError("Can't display a noname element") - return + xxx = self.GetPyData(item).treeObject() # Close old window, remember where it was highLight = None if testWin: pos = testWin.GetPosition() - if node == testWin.item: + 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 0 and xxx.className == 'wxPanel': + if xxx.className == 'wxPanel': if testWin.highLight: testWin.pendingHighLight = highLight testWin.highLight.Remove() @@ -433,9 +718,8 @@ class XML_Tree(wxTreeCtrl): testWin = None else: pos = testWinPos - # Save in temporary file before activating - memFile = MemoryFile(tempfile.mktemp('xrc')) - #memFile = MemoryFile('core.xrc') # to write debug file + # Save in memory FS + memFile = MemoryFile('xxx.xrc') # Create partial XML file - faster for big files dom = minidom.Document() @@ -454,14 +738,15 @@ class XML_Tree(wxTreeCtrl): mainNode.removeChild(elem) dom.unlink() parent.replaceChild(elem, self.dummyNode) - memFile.close() # write to wxMemoryFS res = wxXmlResource('') - res.Load(memFile.name) + 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) @@ -470,162 +755,253 @@ class XML_Tree(wxTreeCtrl): if not testWin: testWin = wxFrame(frame, -1, 'Panel: ' + xxx.name, pos=pos) testWin.panel = res.LoadPanel(testWin, xxx.name) - testWin.SetSize(testWin.panel.GetSize()) + 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) - else: - wxLogMessage('No view for this element yet') - return - os.unlink(memFile.name) # remove tmp file - testWin.item = node - testWin.Connect(testWin.GetId(), -1, wxEVT_CLOSE_WINDOW, self.OnCloseTestWin) + 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 - evt.Skip() + + # 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 not xxx: return false # root item - if self.ctrl: return true # if Ctrl pressed, always insert - if xxx.hasChildren and not self.ItemHasChildren(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.ItemHasChildren(item)) + 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 - pullDownMenu.menu = wxMenu() - item = self.GetSelection() - if not item.IsOk(): - pullDownMenu.menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand tree') + 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 - m = wxMenu() # create menu - if item != self.GetRootItem(): needInsert = self.NeedInsert(item) - if item == self.GetRootItem() or \ - self.GetItemParent(item) == self.GetRootItem() and needInsert: + 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_MENU_BAR, 'MenuBar', 'Create menu bar') + 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) + 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 not (xxx.isSizer or \ - xxx.parent and xxx.parent.isSizer): + 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 item == self.GetRootItem(): - pullDownMenu.menu.AppendMenu(wxNewId(), 'Create', m, 'Create top-level object') + 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 not needInsert: - pullDownMenu.menu.AppendMenu(wxNewId(), 'Create child', m, - 'Create child object') + if self.shift: + menu.AppendMenu(wxNewId(), 'Create Sibling', m, + 'Create sibling before selected object') else: - pullDownMenu.menu.AppendMenu(wxNewId(), 'Create Sibling', m, - 'Create sibling of selected object') - pullDownMenu.menu.AppendSeparator() - pullDownMenu.menu.Append(wxID_CUT, 'Cut', 'Cut to the clipboard') - pullDownMenu.menu.Append(wxID_COPY, 'Copy', 'Copy to the clipboard') - pullDownMenu.menu.Append(wxID_PASTE, 'Paste', 'Paste from the clipboard') - pullDownMenu.menu.Append(pullDownMenu.ID_DELETE, - 'Delete', 'Delete object') - if item.IsOk() and self.ItemHasChildren(item): - pullDownMenu.menu.AppendSeparator() - pullDownMenu.menu.Append(pullDownMenu.ID_EXPAND, 'Expand', 'Expand subtree') - self.PopupMenu(pullDownMenu.menu, evt.GetPosition()) - pullDownMenu.menu.Destroy() - pullDownMenu.menu = None - - - # Clear tree - def Clear(self): - self.DeleteAllItems() - # Add minimal structure - root = self.AddRoot('XML tree') - self.Unselect() - 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) + 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): - if not xxx: return - # !!! Save undo info - if xxx.undo: xxx.undo.unlink() - xxx.undo = xxx.element.cloneNode(false) - if xxx.hasName: - name = GetRegistered('name').GetValue() - if xxx.name != name: - xxx.name = name - xxx.element.setAttribute('name', name) - self.SetItemText(item, xxx.treeName()) - if xxx.hasChild: prefix = '_' - else: prefix = '' - for param, data in xxx.params.items(): - value = GetRegistered(prefix + param).GetValue() - if param == 'content': - # If number if items is not the same, recreate children - if len(value) != len(data): - elem = xxx.element.getElementsByTagName('content')[0] - for n in elem.childNodes: - elem.removeChild(n) - data = [] - for str in value: - itemElem = tree.dom.createElement('item') - itemText = tree.dom.createTextNode(str) - itemElem.appendChild(itemText) - elem.appendChild(itemElem) - data.append(itemText) - xxx.params[param] = data - else: - for i in range(len(value)): - data[i].data = value[i] - else: - data.data = value - if xxx.hasChild: - self.Apply(xxx.child, item) - else: - # Change tree icon for sizers - if isinstance(xxx, xxxBoxSizer): - self.SetItemImage(item, xxx.treeImage()) - # Set global modified state - frame.modified = true + 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, size): - wxFrame.__init__(self, None, -1, '', size=size) + 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() @@ -641,7 +1017,7 @@ class Frame(wxFrame): menu = wxMenu() menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo') - menu.Append(wxID_REDO, '&Redo\tCtrl-R', 'Redo') + 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') @@ -651,8 +1027,15 @@ class Frame(wxFrame): 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 view') + 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) @@ -660,7 +1043,9 @@ class Frame(wxFrame): menuBar.Append(menu, '&View') menu = wxMenu() - menu.Append(wxID_ABOUT, 'About...', 'About XCRed') + 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') @@ -671,23 +1056,27 @@ class Frame(wxFrame): self.SetMenuBar(menuBar) # Create toolbar - tb = self.CreateToolBar()#wxTB_DOCKABLE | wxTB_FLAT) - tb.SetToolBitmapSize((24,23)) + 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.AddSeparator() + 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.AddSeparator() + 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) - self.tb = tb 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) @@ -703,104 +1092,71 @@ class Frame(wxFrame): 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 - splitter = wxSplitterWindow(self, -1) + 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 - #panel = wxPanel(self, -1) - # Sizer for static box - #sizer = wxBoxSizer() - panel = Panel(splitter, -1) - sys.modules['params'].panel = panel - #sizer.Add(panel, 1, wxEXPAND) - #box = wxStaticBox(panel, -1, 'Parameters') - #boxSizer = wxStaticBoxSizer(box) - #boxSizer.Add(wxButton(panel, -1, 'BUTT ON')) - #sizer.Add(boxSizer, 1, wxEXPAND | wxALL, 10) - #panel.SetAutoLayout(true) - #panel.SetSizer(sizer) - # Set plitter windows - splitter.SplitVertically(tree, panel, 200) - #topSizer = wxBoxSizer() - #topSizer.Add(splitter, 1, wxEXPAND) - #self.SetAutoLayout(true) - #self.SetSizer(topSizer) + 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 - class MenuData: pass global pullDownMenu - pullDownMenu = MenuData() - pullDownMenu.menu = None - pullDownMenu.ID_NEW_PANEL = wxNewId() - pullDownMenu.ID_NEW_DIALOG = wxNewId() - pullDownMenu.ID_NEW_FRAME = wxNewId() - pullDownMenu.ID_NEW_MENU_BAR = wxNewId() - pullDownMenu.ID_NEW_MENU = wxNewId() - - pullDownMenu.ID_NEW_STATIC_TEXT = wxNewId() - pullDownMenu.ID_NEW_TEXT_CTRL = wxNewId() - - pullDownMenu.ID_NEW_BUTTON = wxNewId() - pullDownMenu.ID_NEW_BITMAP_BUTTON = wxNewId() - pullDownMenu.ID_NEW_RADIO_BUTTON = wxNewId() - pullDownMenu.ID_NEW_SPIN_BUTTON = wxNewId() - - pullDownMenu.ID_NEW_STATIC_BOX = wxNewId() - pullDownMenu.ID_NEW_CHECK_BOX = wxNewId() - pullDownMenu.ID_NEW_RADIO_BOX = wxNewId() - pullDownMenu.ID_NEW_COMBO_BOX = wxNewId() - pullDownMenu.ID_NEW_LIST_BOX = wxNewId() - - pullDownMenu.ID_NEW_STATIC_LINE = wxNewId() - pullDownMenu.ID_NEW_CHOICE = wxNewId() - pullDownMenu.ID_NEW_SLIDER = wxNewId() - pullDownMenu.ID_NEW_GAUGE = wxNewId() - pullDownMenu.ID_NEW_SCROLL_BAR = wxNewId() - pullDownMenu.ID_NEW_TREE_CTRL = wxNewId() - pullDownMenu.ID_NEW_LIST_CTRL = wxNewId() - pullDownMenu.ID_NEW_CHECK_LIST = wxNewId() - pullDownMenu.ID_NEW_NOTEBOOK = wxNewId() - pullDownMenu.ID_NEW_HTML_WINDOW = wxNewId() - pullDownMenu.ID_NEW_CALENDAR = wxNewId() - - pullDownMenu.ID_NEW_BOX_SIZER = wxNewId() - pullDownMenu.ID_NEW_STATIC_BOX_SIZER = wxNewId() - pullDownMenu.ID_NEW_GRID_SIZER = wxNewId() - pullDownMenu.ID_NEW_FLEX_GRID_SIZER = wxNewId() - pullDownMenu.ID_NEW_SPACER = wxNewId() - pullDownMenu.ID_NEW_MENU = wxNewId() - pullDownMenu.ID_NEW_MENU_ITEM = wxNewId() - pullDownMenu.ID_NEW_SEPARATOR = wxNewId() - pullDownMenu.ID_NEW_LAST = wxNewId() - pullDownMenu.ID_DELETE = self.ID_DELETE - pullDownMenu.ID_EXPAND = wxNewId() - EVT_MENU_RANGE(self, pullDownMenu.ID_NEW_PANEL, - pullDownMenu.ID_NEW_LAST, self.OnCreate) - EVT_MENU(self, pullDownMenu.ID_EXPAND, self.OnExpand) - # We connect to tree, but process in frame - EVT_MENU_HIGHLIGHT_ALL(tree, self.OnPullDownHighlight) + 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', @@ -817,6 +1173,7 @@ class Frame(wxFrame): 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', @@ -833,9 +1190,6 @@ class Frame(wxFrame): pullDownMenu.ID_NEW_GRID_SIZER: 'wxGridSizer', pullDownMenu.ID_NEW_FLEX_GRID_SIZER: 'wxFlexGridSizer', pullDownMenu.ID_NEW_SPACER: 'spacer', - pullDownMenu.ID_NEW_MENU: 'wxMenu', - pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem', - pullDownMenu.ID_NEW_SEPARATOR: 'separator', } pullDownMenu.controls = [ ['control', 'Various controls', @@ -848,7 +1202,6 @@ class Frame(wxFrame): (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_CHECK_LIST, 'CheckList', 'Create check 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'), @@ -866,6 +1219,8 @@ class Frame(wxFrame): (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'), @@ -882,6 +1237,35 @@ class Frame(wxFrame): (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() @@ -902,9 +1286,13 @@ class Frame(wxFrame): self.SetStatusText('Loading...') wxYield() wxBeginBusyCursor() - self.Open(path) + try: + self.Open(path) + self.SetStatusText('Data loaded') + except: + self.SetStatusText('Failed') + raise wxEndBusyCursor() - self.SetStatusText('Ready') dlg.Destroy() def OnSaveOrSaveAs(self, evt): @@ -914,24 +1302,31 @@ class Frame(wxFrame): dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile), defaultName, '*.xrc', wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR) - if dlg.ShowModal() == wxID_CANCEL: return - path = dlg.GetPath() - dlg.Destroy() + if dlg.ShowModal() == wxID_OK: + path = dlg.GetPath() + dlg.Destroy() + else: + dlg.Destroy() + return else: path = self.dataFile self.SetStatusText('Saving...') wxYield() wxBeginBusyCursor() - self.Save(path) - self.dataFile = path + try: + self.Save(path) + self.dataFile = path + self.SetStatusText('Data saved') + except IOError: + self.SetStatusText('Failed') wxEndBusyCursor() - self.SetStatusText('Ready') 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 @@ -944,7 +1339,8 @@ class Frame(wxFrame): print '*** being implemented' def OnCut(self, evt): - selected = tree.GetSelection() + selected = tree.selection + if not selected: return # key pressed event # Undo info self.lastOp = 'CUT' self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] @@ -957,7 +1353,7 @@ class Frame(wxFrame): testWin = None else: # Remove highlight, update testWin - if tree.GetItemAncestor(selected) == testWin.item: + if not tree.IsHighlatable(selected): if testWin.highLight: testWin.highLight.Remove() tree.needUpdate = true self.clipboard = tree.RemoveLeaf(selected) @@ -965,15 +1361,21 @@ class Frame(wxFrame): tree.Unselect() panel.Clear() self.modified = true + self.SetStatusText('Removed to clipboard') def OnCopy(self, evt): - selected = tree.GetSelection() + 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.GetSelection() - appendChild = not tree.NeedInsert(selected) + 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 @@ -985,7 +1387,7 @@ class Frame(wxFrame): appendChild = true selected = tree.GetItemParent(selected) # Expanded container (must have children) - elif tree.IsExpanded(selected) and tree.ItemHasChildren(selected): + elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, false): appendChild = false nextItem = tree.GetFirstChild(selected, 0)[0] parentLeaf = selected @@ -994,59 +1396,80 @@ class Frame(wxFrame): parent = tree.GetPyData(selected) else: parent = tree.GetPyData(parentLeaf) - if parent and parent.hasChild: parent = parent.child + 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) - className = xxx.treeObject().className - # Check parent and child relationships - # Parent is sizer or notebook, child is of wrong class or - # parent is normal window, child is child container: detach child + + # 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 not parent and isChildContainer or \ - (parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ + if isChildContainer and \ + ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \ - (not parent.isSizer and not isinstance(parent, xxxNotebook) and \ - isChildContainer): - if isChildContainer: - elem.removeChild(xxx.child.element) # detach child - elem.unlink() # delete child container - elem = xxx.child.element # replace - # This should help garbage collection (!!! maybe not needed?) - xxx.child.parent = None - xxx.child = None - if parent: - # 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 + 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: - if parent: node = parent.element - else: node = tree.mainNode - node.appendChild(elem) + parent.element.appendChild(elem) newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), data=wxTreeItemData(xxx)) else: node = tree.GetPyData(nextItem).element - if parent: - elemParent = parent.element - else: - elemParent = tree.mainNode - elemParent.insertBefore(elem, node) + parent.element.insertBefore(elem, node) # Inserting before is difficult, se we insert after or first child - newItem = tree.InsertItem(parentLeaf, selected, xxx.treeName(), - image=xxx.treeImage(), data=wxTreeItemData(xxx)) + 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() @@ -1060,16 +1483,18 @@ class Frame(wxFrame): tree.ScrollTo(newItem) tree.Refresh() # Update view? - if testWin and tree.GetItemAncestor(newItem) == testWin.item: + 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.GetSelection() + selected = tree.selection + if not selected: return # key pressed event # Undo info self.lastOp = 'DELETE' self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] @@ -1082,21 +1507,61 @@ class Frame(wxFrame): testWin = None else: # Remove highlight, update testWin - if tree.GetItemAncestor(selected) == testWin.item: + if not tree.IsHighlatable(selected): if testWin.highLight: testWin.highLight.Remove() tree.needUpdate = true xnode = tree.RemoveLeaf(selected) - self.undo.append(xnode.cloneNode(true)) + # !!! 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.GetSelection() - if selection.IsOk(): + selection = tree.selection + if selection: xxx = tree.GetPyData(selection) if xxx and panel.IsModified(): tree.Apply(xxx, selection) @@ -1111,14 +1576,22 @@ class Frame(wxFrame): self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) def OnAbout(self, evt): - dlg = wxMessageDialog(self, '%s %s\n\nRoman Rolinsky ' % \ - (progname, version), - 'About %s' % progname, wxOK | wxCENTRE) + 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:\> ') @@ -1133,20 +1606,28 @@ class Frame(wxFrame): print msg def OnCreate(self, evt): - selected = tree.GetSelection() - appendChild = not tree.NeedInsert(selected) + selected = tree.selection + if tree.ctrl: 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 + # If insert before + if tree.shift: + # If has previous item, insert after it, else append to parent + nextItem = selected parentLeaf = tree.GetItemParent(selected) - else: # last child: change selected to parent - appendChild = true - selected = 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.IsExpanded(selected) and tree.ItemHasChildren(selected): + elif tree.shift and tree.IsExpanded(selected) \ + and tree.GetChildrenCount(selected, false): appendChild = false nextItem = tree.GetFirstChild(selected, 0)[0] parentLeaf = selected @@ -1155,82 +1636,113 @@ class Frame(wxFrame): parent = tree.GetPyData(selected) else: parent = tree.GetPyData(parentLeaf) - if parent and parent.hasChild: parent = parent.child + 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: - if parent: node = parent.element - else: node = tree.mainNode # Insert newline for debug purposes - node.appendChild(tree.dom.createTextNode('\n')) - node.appendChild(elem) + parent.element.appendChild(elem) newItem = tree.AppendItem(selected, xxx.treeName(), image=xxx.treeImage(), data=wxTreeItemData(xxx)) else: node = tree.GetPyData(nextItem).element - if parent: - elemParent = parent.element - else: - elemParent = tree.mainNode - elemParent.insertBefore(tree.dom.createTextNode('\n'), node) - elemParent.insertBefore(elem, node) - # Inserting before is difficult, se we insert after or first child - newItem = tree.InsertItem(parentLeaf, selected, - xxx.treeName(), image=xxx.treeImage(), - data=wxTreeItemData(xxx)) + 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.GetItemAncestor(newItem) == testWin.item: + 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.GetSelection().IsOk(): - tree.ExpandAll(tree.GetSelection()) - else: - tree.ExpandAll(tree.GetRootItem()) + 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() - help = '' - if menuId != -1: help = pullDownMenu.GetHelpString(menuId) - self.SetStatusText(help) + 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]: - enable = tree.GetSelection().IsOk() and \ - tree.GetSelection() != tree.GetRootItem() - evt.Enable(enable) + evt.Enable(tree.selection != tree.root) elif evt.GetId() == wxID_PASTE: - enable = self.clipboard != None - evt.Enable(enable) + 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) - evt.Skip() + 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): @@ -1238,13 +1750,17 @@ class Frame(wxFrame): self.clipboard = None self.modified = false panel.SetModified(false) - panel.Clear() 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 @@ -1257,17 +1773,46 @@ class Frame(wxFrame): self.dataFile = path self.SetTitle(progname + ': ' + os.path.basename(path)) except: - wxLogError('Error reading file: ' + path) + 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: - memFile = MemoryFile(path) - tree.dom.writexml(memFile) - memFile.close() + # 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: ' + path) + wxLogError('Error writing file: %s' % path) + raise def AskSave(self): if not (self.modified or panel.IsModified()): return true @@ -1286,34 +1831,82 @@ class Frame(wxFrame): return true return false +################################################################################ + +def usage(): + print >> sys.stderr, 'usage: xrced [-dvh] [file]' + class App(wxApp): def OnInit(self): - self.SetAppName("xrced") + 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 - # !!! wxConfigBase_Get doesn't seem to work - conf = wxConfig(style=wxCONFIG_USE_LOCAL_FILE) + 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 - global frame - frame = self.frame = Frame(size) - self.frame.Show(true) - # Load resources from XRC file (!!! should be transformed to .py later) + 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():