X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a40b5e8474e7c8d3be805c83e54c5e016c11decc..490da436c859b499a7726acd06af02304b0d925f:/wxPython/tools/XRCed/xrced.py diff --git a/wxPython/tools/XRCed/xrced.py b/wxPython/tools/XRCed/xrced.py index 2378d1661b..503c1b1b0f 100644 --- a/wxPython/tools/XRCed/xrced.py +++ b/wxPython/tools/XRCed/xrced.py @@ -6,23 +6,28 @@ 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 +# 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) -faceColour = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_3DFACE) -# Stange wxHtmlWindow behavior: when bgcolor is exact, it turns white -bgcolor = (faceColour.Red()-1, faceColour.Green()-1, faceColour.Blue()-1) -htmlHeader = '\n' % bgcolor -htmlFooter = '\n' progname = 'XRCed' -version = '0.0.5' +version = '0.0.7-3' # Local modules from xxx import * @@ -32,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) @@ -59,75 +72,162 @@ def SetMenu(m, list): class Panel(wxNotebook): def __init__(self, parent, id = -1): wxNotebook.__init__(self, parent, id, style=wxNB_BOTTOM) - #self.SetBackgroundColour(wxColour(bgcolor)) sys.modules['params'].panel = self - self.page1 = HtmlPage(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') - self.page2 = None + # 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.page1.SetPageData(xxx) - # Replace/remove style page - if self.page2: - self.RemovePage(1) - self.page2.Destroy() + 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: - self.page2 = StylePage(self, xxx.treeObject()) - self.AddPage(self.page2, 'Style') + 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: - self.page2 = None + # Remove page if exists + if self.GetPageCount() == 2: + self.SetSelection(0) + self.page1.Refresh() + self.RemovePage(1) def Clear(self): - self.page1.Clear() - if self.page2: - self.RemovePage(1) - self.page2.Destroy() - self.page2 = None - # If some parameter on some page has changed + self.SetData(None) + # Check if some parameter on some page has changed def IsModified(self): - return self.page1.IsModified() or self.page2 and self.page2.IsModified() + for p in self.pages: + if p.IsModified(): return true + return false + # Reset changed state def SetModified(self, value): - self.page1.SetModified(value) - if self.page2: - self.page2.SetModified(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: - def __init__(self): +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) - winName = evt.GetEventObject().GetName() - sizerParam = false - if winName[0] == '_': - param = winName[7:] - sizerParam = true - else: - if xxx.hasChild: xxx = xxx.child - param = winName[6:] - # Set current object - if sizerParam: - w = self.FindWindowByName('_data_' + param) - else: - w = self.FindWindowByName('data_' + 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('') # set empty (default) value - # For font element, we have to create another object + w.SetModified() # mark as changed + elem = tree.dom.createElement(param) + # Some classes are special if param == 'font': - # Make XXX object - textElem = tree.dom.createElement('font') - font = xxxFont(xxx, textElem) - xxx.params['font'] = font + xxx.params[param] = xxxParamFont(xxx.element, elem) else: - textElem = tree.dom.createElement(param) - textNode = tree.dom.createTextNode('') - textElem.appendChild(textNode) - xxx.params[param] = textNode + xxx.params[param] = xxxParam(elem) # Find place to put new element: first present element after param found = false paramStyles = xxx.allParams + xxx.styles @@ -137,122 +237,149 @@ class ParamPage: 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) + 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 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) ################################################################################ -# Properties panel notebook page -class HtmlPage(wxHtmlWindow, ParamPage): - def __init__(self, parent, id = -1): - wxHtmlWindow.__init__(self, parent, id) - ParamPage.__init__(self) - self.SetBorders(5) - if wxGetOsVersion()[1] == 1: - self.SetFonts('', '', [8, 10, 12, 14, 16, 19, 24]) - else: - self.SetFonts("", "", [7, 8, 10, 12, 16, 22, 30]) - self.modified = false - def Clear(self): - self.SetPage(htmlHeader + 'select a tree item on the left' + htmlFooter) - def SetPageData(self, xxx): - if not xxx: - self.SetPage(htmlHeader + 'this item has no properties' + htmlFooter) - return - self.SetPage(htmlHeader + xxx.generateHtml() + htmlFooter) +# 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.hasChild: prefix = '_' - else: prefix = '' + if xxx.hasName: + self.controlName.SetValue(xxx.name) 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 - self.FindWindowByName(prefix + 'data_' + param).SetValue(value) + 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.FindWindowByName(prefix + 'check_' + param).SetValue(true) - else: - self.FindWindowByName(prefix + 'data_' + 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 - self.FindWindowByName('data_' + param).SetValue(value) - if not param in xxx.required: - self.FindWindowByName('check_' + param).SetValue(true) - else: - self.FindWindowByName('data_' + param).Enable(false) - # If some parameter has changed - def IsModified(self): - return self.modified - def SetModified(self, value): - self.modified = value + 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(wxPanel, ParamPage): - def __init__(self, parent, xxx): - wxPanel.__init__(self, parent, -1) - ParamPage.__init__(self) - if wxGetOsVersion()[1] == 1: - self.SetFont(wxFont(12, wxDEFAULT, wxNORMAL, wxNORMAL)) - else: - self.SetFont(wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL)) - topSizer = wxBoxSizer(wxVERTICAL) - sizer = wxFlexGridSizer(len(xxx.styles), 2, 0, 1) - self.controls = {} # save python objects +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 + ':', name = 'check_' + param) + param + ':', size = (100,-1), name = param) check.SetValue(present) - control = paramDict[param](self, -1, '', (-1, -1), - 'data_' + param) - if present: - control.SetValue(xxx.params[param].data) - else: - control.SetValue('') + control = paramDict[param](self, name = param) control.Enable(present) - sizer.AddMany([ (check, 0, 0), - (control, 0, 0) ]) + 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, 5) + topSizer.Add(sizer, 1, wxALL | wxEXPAND, 3) self.SetAutoLayout(true) self.SetSizer(topSizer) - - self.modified = false - - # If some parameter has changed - def IsModified(self): - return self.modified - def SetModified(self, value): - self.modified = value + 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) ################################################################################ @@ -288,25 +415,25 @@ class MemoryFile: 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, - style=wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT) + 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) @@ -316,6 +443,7 @@ class XML_Tree(wxTreeCtrl): 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())) @@ -326,13 +454,10 @@ class XML_Tree(wxTreeCtrl): 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): self.Expand(item) @@ -343,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(), @@ -376,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) @@ -385,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 @@ -430,43 +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) + 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) - # Remove highlight? - if not xxx and testWin and testWin.highLight: - testWin.highLight.Remove() - return # 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 @@ -474,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() @@ -544,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() @@ -565,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) @@ -581,163 +755,165 @@ 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 menu = wxMenu() - - item = self.GetSelection() - if not item.IsOk(): + + 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(): - 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: - menu.AppendMenu(wxNewId(), 'Create child', m, - 'Create child object') + if self.shift: + menu.AppendMenu(wxNewId(), 'Create Sibling', m, + 'Create sibling before selected object') else: menu.AppendMenu(wxNewId(), 'Create Sibling', m, - 'Create sibling of selected object') + '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') - menu.Append(wxID_PASTE, 'Paste', 'Paste from 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 item.IsOk() and self.ItemHasChildren(item): + 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() - # Clear tree - def Clear(self): - self.DeleteAllItems() - # Add minimal structure - root = self.AddRoot('XML tree', self.rootImage) - 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) - # 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 = panel.page1.FindWindowByName('data_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 = panel.FindWindowByName(prefix + 'data_' + 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] - elif param == 'font': - data.updateXML(value) - 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() @@ -754,8 +930,9 @@ class PullDownMenu: 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() @@ -766,31 +943,65 @@ class PullDownMenu: 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() - self.SetIcon(wxIconFromXPMData(images.getIconData())) + 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() @@ -803,10 +1014,10 @@ class Frame(wxFrame): 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-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') @@ -814,18 +1025,27 @@ class Frame(wxFrame): 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 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) menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) 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') @@ -841,18 +1061,22 @@ class Frame(wxFrame): 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) 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) @@ -868,31 +1092,53 @@ 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 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 = Panel(splitter) - # Set plitter windows - splitter.SplitVertically(tree, panel, 200) + 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) @@ -905,8 +1151,12 @@ class Frame(wxFrame): 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', @@ -915,14 +1165,15 @@ class Frame(wxFrame): 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', @@ -933,15 +1184,12 @@ class Frame(wxFrame): 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.ID_NEW_MENU: 'wxMenu', - pullDownMenu.ID_NEW_MENU_ITEM: 'wxMenuItem', - pullDownMenu.ID_NEW_SEPARATOR: 'separator', } pullDownMenu.controls = [ ['control', 'Various controls', @@ -954,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'), @@ -972,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'), @@ -988,7 +1237,36 @@ 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() @@ -1008,10 +1286,14 @@ 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() + dlg.Destroy() def OnSaveOrSaveAs(self, evt): if evt.GetId() == wxID_SAVEAS or not self.dataFile: @@ -1031,16 +1313,20 @@ class Frame(wxFrame): 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 @@ -1048,12 +1334,13 @@ class Frame(wxFrame): 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.GetSelection() + selected = tree.selection + if not selected: return # key pressed event # Undo info self.lastOp = 'CUT' self.undo = [tree.GetItemParent(selected), tree.GetPrevSibling(selected)] @@ -1066,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) @@ -1074,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 @@ -1094,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 @@ -1103,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() @@ -1169,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)] @@ -1191,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) @@ -1220,12 +1576,22 @@ class Frame(wxFrame): self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) def OnAbout(self, evt): - wxMessageDialog(self, '%s %s\n\nRoman Rolinsky ' % \ - (progname, version), - 'About %s' % progname, wxOK | wxCENTRE).ShowModal() + 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:\> ') @@ -1240,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 @@ -1262,51 +1636,59 @@ 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() @@ -1319,28 +1701,48 @@ class Frame(wxFrame): 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): @@ -1348,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 @@ -1367,26 +1773,54 @@ 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: + # Apply changes self.OnRefresh(wxCommandEvent()) - memFile = MemoryFile(path) - tree.dom.writexml(memFile) - memFile.close() + 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 flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE - say = wxMessageDialog( self, 'File is modified. Save before exit?', - 'Save before too late?', flags ).ShowModal() + 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 @@ -1399,34 +1833,80 @@ class Frame(wxFrame): ################################################################################ +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():