+License: wxWidgets license
+Version: 0.8
+Date: 13 March 2007
+
+ExpansionState is based on code and ideas from Karsten Hilbert.
+Andrea Gavana provided help with the CustomTreeCtrl integration.
+'''
+
+
+import wx, wx.lib.customtreectrl
+
+
+class TreeAPIHarmonizer(object):
+ ''' This class attempts to hide the differences in API between the
+ different tree controls that are part of wxPython. '''
+
+ def __init__(self, *args, **kwargs):
+ # CustomTreeCtrl uses a different keyword for the window style
+ # argument ('ctstyle'). To hide this, we replace the 'style' keyword
+ # by 'ctstyle' if we're mixed in with CustomTreeCtrl.
+ if isinstance(self, wx.lib.customtreectrl.CustomTreeCtrl):
+ kwargs['ctstyle'] = kwargs.pop('style', wx.TR_DEFAULT_STYLE)
+ super(TreeAPIHarmonizer, self).__init__(*args, **kwargs)
+
+ def __callSuper(self, methodName, default, *args, **kwargs):
+ # If our super class has a method called methodName, call it,
+ # otherwise return the default value.
+ superClass = super(TreeAPIHarmonizer, self)
+ if hasattr(superClass, methodName):
+ return getattr(superClass, methodName)(*args, **kwargs)
+ else:
+ return default
+
+ def GetColumnCount(self, *args, **kwargs):
+ # Only TreeListCtrl has columns, return 0 if we are mixed in
+ # with another tree control.
+ return self.__callSuper('GetColumnCount', 0, *args, **kwargs)
+
+ def GetItemType(self, *args, **kwargs):
+ # Only CustomTreeCtrl has different item types, return the
+ # default item type if we are mixed in with another tree control.
+ return self.__callSuper('GetItemType', 0, *args, **kwargs)
+
+ def SetItemType(self, item, newType):
+ # CustomTreeCtrl doesn't support changing the item type on the fly,
+ # so we create a new item and delete the old one. We currently only
+ # keep the item text, would be nicer to also retain other attributes.
+ text = self.GetItemText(item)
+ newItem = self.InsertItem(self.GetItemParent(item), item, text,
+ ct_type=newType)
+ self.Delete(item)
+ return newItem
+
+ def IsItemChecked(self, *args, **kwargs):
+ # Only CustomTreeCtrl supports checkable items, return False if
+ # we are mixed in with another tree control.
+ return self.__callSuper('IsItemChecked', False, *args, **kwargs)
+
+ def GetItemChecked(self, *args, **kwargs):
+ # For consistency's sake, provide a 'Get' and 'Set' method for
+ # checkable items.
+ return self.IsItemChecked(*args, **kwargs)
+
+ def SetItemChecked(self, *args, **kwargs):
+ # For consistency's sake, provide a 'Get' and 'Set' method for
+ # checkable items.
+ return self.CheckItem(*args, **kwargs)
+
+ def GetMainWindow(self, *args, **kwargs):
+ # Only TreeListCtrl has a separate main window, return self if we are
+ # mixed in with another tree control.
+ return self.__callSuper('GetMainWindow', self, *args, **kwargs)
+
+ def GetItemImage(self, item, which=wx.TreeItemIcon_Normal, column=-1):
+ # CustomTreeCtrl always wants the which argument, so provide it
+ # TreeListCtr.GetItemImage has a different order of arguments than
+ # the other tree controls. Hide the differenes.
+ if self.GetColumnCount():
+ args = (item, column, which)
+ else:
+ args = (item, which)
+ return super(TreeAPIHarmonizer, self).GetItemImage(*args)
+
+ def SetItemImage(self, item, imageIndex, which=wx.TreeItemIcon_Normal,
+ column=-1):
+ # The SetItemImage signature is different for TreeListCtrl and
+ # other tree controls. This adapter method hides the differences.
+ if self.GetColumnCount():
+ args = (item, imageIndex, column, which)
+ else:
+ args = (item, imageIndex, which)
+ super(TreeAPIHarmonizer, self).SetItemImage(*args)
+
+ def UnselectAll(self):
+ # Unselect all items, regardless of whether we are in multiple
+ # selection mode or not.
+ if self.HasFlag(wx.TR_MULTIPLE):
+ super(TreeAPIHarmonizer, self).UnselectAll()
+ else:
+ # CustomTreeCtrl Unselect() doesn't seem to work in all cases,
+ # also invoke UnselectAll just to be sure.
+ self.Unselect()
+ super(TreeAPIHarmonizer, self).UnselectAll()
+
+ def GetCount(self):
+ # TreeListCtrl correctly ignores the root item when it is hidden,
+ # but doesn't count the root item when it is visible
+ itemCount = super(TreeAPIHarmonizer, self).GetCount()
+ if self.GetColumnCount() and not self.HasFlag(wx.TR_HIDE_ROOT):
+ itemCount += 1
+ return itemCount
+
+ def GetSelections(self):
+ # Always return a list of selected items, regardless of whether
+ # we are in multiple selection mode or not.
+ if self.HasFlag(wx.TR_MULTIPLE):
+ selections = super(TreeAPIHarmonizer, self).GetSelections()
+ else:
+ selection = self.GetSelection()
+ if selection:
+ selections = [selection]
+ else:
+ selections = []
+ # If the root item is hidden, it should never be selected,
+ # unfortunately, CustomTreeCtrl allows it to be selected.
+ if self.HasFlag(wx.TR_HIDE_ROOT):
+ rootItem = self.GetRootItem()
+ if rootItem and rootItem in selections:
+ selections.remove(rootItem)
+ return selections
+
+ def SelectItem(self, item, *args, **kwargs):
+ # Prevent the hidden root from being selected, otherwise TreeCtrl
+ # crashes
+ if self.HasFlag(wx.TR_HIDE_ROOT) and item == self.GetRootItem():
+ return
+ else:
+ return super(TreeAPIHarmonizer, self).SelectItem(item, *args,
+ **kwargs)
+
+ def HitTest(self, *args, **kwargs):
+ ''' HitTest returns a two-tuple (item, flags) for tree controls
+ without columns and a three-tuple (item, flags, column) for tree
+ controls with columns. Our caller can indicate this method to
+ always return a three-tuple no matter what tree control we're mixed
+ in with by specifying the optional argument 'alwaysReturnColumn'
+ to be True. '''
+ alwaysReturnColumn = kwargs.pop('alwaysReturnColumn', False)
+ hitTestResult = super(TreeAPIHarmonizer, self).HitTest(*args, **kwargs)
+ if len(hitTestResult) == 2 and alwaysReturnColumn:
+ hitTestResult += (0,)
+ return hitTestResult
+
+ def ExpandAll(self, item=None):
+ # TreeListCtrl wants an item as argument. That's an inconsistency with
+ # the TreeCtrl API.
+ try:
+ super(TreeAPIHarmonizer, self).ExpandAll()
+ except TypeError:
+ if item is None:
+ item = self.GetRootItem()
+ super(TreeAPIHarmonizer, self).ExpandAll(item)
+
+
+class TreeHelper(object):
+ ''' This class provides methods that are not part of the API of any
+ tree control, but are convenient to have available. '''
+
+ def GetItemChildren(self, item, recursively=False):
+ ''' Return the children of item as a list. '''
+ children = []
+ child, cookie = self.GetFirstChild(item)
+ while child:
+ children.append(child)
+ if recursively:
+ children.extend(self.GetItemChildren(child, True))
+ child, cookie = self.GetNextChild(item, cookie)
+ return children
+
+ def ItemIndex(self, item, tupleIndex=True):
+ ''' Return the index of item. If tupleIndex is True, return a
+ an tuple-based index else return an integer-based index for item. '''
+ if tupleIndex:
+ parent = self.GetItemParent(item)
+ if parent:
+ parentIndices = self.ItemIndex(parent)
+ ownIndex = self.GetItemChildren(parent).index(item)
+ return parentIndices + (ownIndex,)
+ else:
+ return ()
+ else:
+ rootItem = self.GetRootItem()
+ if self.HasFlag(wx.TR_HIDE_ROOT):
+ if item == rootItem:
+ return None
+ countRoot = 0
+ else:
+ if item == rootItem:
+ return 0
+ countRoot = 1
+ return self.GetItemChildren(rootItem,
+ recursively=True).index(item) + countRoot
+
+
+class VirtualTree(TreeAPIHarmonizer, TreeHelper):
+ ''' This is a mixin class that can be used to allow for virtual tree
+ controls. It can be mixed in with wx.TreeCtrl, wx.gizmos.TreeListCtrl,
+ wx.lib.customtree.CustomTreeCtrl.
+
+ To use it derive a new class from this class and one of the tree
+ controls, e.g.:
+ class MyTree(VirtualTree, wx.TreeCtrl):
+ ...
+
+ VirtualTree uses several callbacks (such as OnGetItemText) to
+ retrieve information needed to construct the tree and render the
+ items. To specify what item the callback needs information about,
+ the callback passes an item index. VirtualTree supports two types of
+ indices: a tuple-based index (the default) and an integer based index.
+ See below for a more detailed explanation of the two index types.
+
+ Note that VirtualTree forces the wx.TR_HIDE_ROOT style.
+
+ In your subclass you *must* override OnGetItemText and either
+ OnGetChildrenCount if you use the tuple-based index or
+ OnGetChildrenIndices if you use the integer-based index. These
+ methods are the minimum needed to construct the tree and render
+ the item labels. If you want to add images, change fonts our colours,
+ etc., you need to override the appropriate OnGetXXX method as well.
+
+ About the index types: VirtualTree supports two types of indices that
+ are passed to the several callbacks to retrieve information about
+ items in the tree. The default index type is tuple-based, the second
+ type is integer-based.
+
+ If you use tuple-based indices, your callbacks are passed a tuple of
+ integers that identifies the item the VirtualTree wants information
+ about. An empty tuple, i.e. (), represents the hidden root item.
+ A tuple with one integer, e.g. (3,), represents a visible root item,
+ in this case the fourth one. A tuple with two integers, e.g. (3,0),
+ represents a child of a visible root item, in this case the first
+ child of the fourth root item. The tuple-based index is best suited
+ if your underlying data structure is organized as a tree and allows
+ for easy navigation to the right object in your tree data structure.
+
+ If you use integer-based indices, your callbacks are passed a simple
+ integer (or None) that identifies the item the VirtualTree wants
+ information about. None represents the hidden root item. Zero (0)
+ represents the first visible root item. If the first visible root
+ item has children 1 represents the first child of the first visible
+ root item. If that child has children 2 represents the first grand
+ child of the first visible root item. If the first child has no
+ children, 2 would represent the second child of the first visible
+ root item, etc. Basically, the integer is the row number of the item
+ (when the tree is completely expanded). Integer-based indices are
+ more convenient when your underlying data structure is organized as
+ a list that is in the same order as the tree items in the tree need
+ to be.
+ '''
+
+ def __init__(self, *args, **kwargs):
+ self._tupleIndex = kwargs.pop('tupleIndex', True)
+ kwargs['style'] = kwargs.get('style', wx.TR_DEFAULT_STYLE) | \
+ wx.TR_HIDE_ROOT
+ super(VirtualTree, self).__init__(*args, **kwargs)
+ self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding)
+ self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed)
+
+ def OnGetChildrenCount(self, tupleIndex):
+ ''' This function must be overloaded in the derived class, if
+ you use the tuple-based indices. It should return the number
+ of child items of the item with the provided tupleIndex. If
+ tupleIndex == () it should return the number of root items. '''
+ raise NotImplementedError
+
+ def OnGetChildrenIndices(self, integerIndex=None):
+ ''' This function must be overloaded in the derived class, if
+ you want to use the integer-based index. The overridden method
+ should return a list containing the (integer) indices of the
+ children of the specified item. '''
+ raise NotImplementedError
+
+ def OnGetItemText(self, index, column=0):
+ ''' This function must be overloaded in the derived class. It
+ should return the string containing the text of the specified
+ item. '''
+ raise NotImplementedError
+
+ def OnGetItemFont(self, index):
+ ''' This function may be overloaded in the derived class. It
+ should return the wx.Font to be used for the specified item. '''
+ return wx.NullFont
+
+ def OnGetItemTextColour(self, index):
+ ''' This function may be overloaded in the derived class. It
+ should return the wx.Colour to be used as text colour for the
+ specified item. '''
+ return wx.NullColour
+
+ def OnGetItemBackgroundColour(self, index):
+ ''' This function may be overloaded in the derived class. It
+ should return the wx.Colour to be used as background colour for
+ the specified item. '''
+ return wx.NullColour
+
+ def OnGetItemImage(self, index, which=wx.TreeItemIcon_Normal, column=0):
+ ''' This function may be overloaded in the derived class. It
+ should return the index of the image to be used. Don't forget
+ to associate an ImageList with the tree control. '''
+ return -1
+
+ def OnGetItemType(self, index):
+ ''' This function may be overloaded in the derived class, but
+ that only makes sense when this class is mixed in with a tree
+ control that supports checkable items, i.e. CustomTreeCtrl.
+ This method should return whether the item is to be normal (0,
+ the default), a checkbox (1) or a radiobutton (2).
+ Note that OnGetItemChecked needs to be implemented as well; it
+ should return whether the item is actually checked. '''
+ return 0
+
+ def OnGetItemChecked(self, index):
+ ''' This function may be overloaded in the derived class, but
+ that only makes sense when this class is mixed in with a tree
+ control that supports checkable items, i.e. CustomTreeCtrl.
+ This method should return whether the item is to be checked.
+ Note that OnGetItemType should return 1 (checkbox) or 2
+ (radiobutton) for this item. '''
+ return False
+
+ def RefreshItems(self):
+ ''' Redraws all visible items. '''
+ rootItem = self.GetRootItem()
+ if not rootItem:
+ rootItem = self.AddRoot('Hidden root')
+ self.RefreshChildrenRecursively(rootItem)
+
+ def RefreshChildrenRecursively(self, item, itemIndex=None):
+ ''' Refresh the children of item, reusing as much of the
+ existing items in the tree as possible. '''
+ if itemIndex is None:
+ itemIndex = self.ItemIndex(item, tupleIndex=self._tupleIndex)
+ reusableChildren = self.GetItemChildren(item)
+ for childIndex in self.ChildIndices(itemIndex):
+ if reusableChildren:
+ child = reusableChildren.pop(0)
+ else:
+ child = self.AppendItem(item, '')
+ self.RefreshItemRecursively(child, childIndex)
+ for child in reusableChildren:
+ self.Delete(child)
+
+ def RefreshItemRecursively(self, item, itemIndex):
+ ''' Refresh the item and its children recursively. '''
+ hasChildren = bool(self.ChildIndices(itemIndex))
+ self.RefreshItem(item, itemIndex, hasChildren)
+ # We need to refresh the children when the item is expanded and
+ # when the item has no children, because in the latter case we
+ # might have to delete old children from the tree:
+ if self.IsExpanded(item) or not hasChildren:
+ self.RefreshChildrenRecursively(item, itemIndex)
+ self.SetItemHasChildren(item, hasChildren)
+
+ def RefreshItem(self, item, index, hasChildren):
+ ''' Refresh one item. '''
+ item = self.RefreshItemType(item, index)
+ self.RefreshItemText(item, index)
+ self.RefreshColumns(item, index)
+ self.RefreshItemFont(item, index)
+ self.RefreshTextColour(item, index)
+ self.RefreshBackgroundColour(item, index)
+ self.RefreshItemImage(item, index, hasChildren)
+ self.RefreshCheckedState(item, index)
+
+ def RefreshItemText(self, item, index):
+ self.__refreshAttribute(item, index, 'ItemText')
+
+ def RefreshColumns(self, item, index):
+ for columnIndex in range(1, self.GetColumnCount()):
+ self.__refreshAttribute(item, index, 'ItemText', columnIndex)
+
+ def RefreshItemFont(self, item, index):
+ self.__refreshAttribute(item, index, 'ItemFont')
+
+ def RefreshTextColour(self, item, index):
+ self.__refreshAttribute(item, index, 'ItemTextColour')
+
+ def RefreshBackgroundColour(self, item, index):
+ self.__refreshAttribute(item, index, 'ItemBackgroundColour')
+
+ def RefreshItemImage(self, item, index, hasChildren):
+ regularIcons = [wx.TreeItemIcon_Normal, wx.TreeItemIcon_Selected]
+ expandedIcons = [wx.TreeItemIcon_Expanded,
+ wx.TreeItemIcon_SelectedExpanded]
+ # Refresh images in first column:
+ for icon in regularIcons:
+ self.__refreshAttribute(item, index, 'ItemImage', icon)
+ for icon in expandedIcons:
+ if hasChildren:
+ imageIndex = self.OnGetItemImage(index, icon)
+ else:
+ imageIndex = -1
+ if self.GetItemImage(item, icon) != imageIndex or imageIndex == -1:
+ self.SetItemImage(item, imageIndex, icon)
+ # Refresh images in remaining columns, if any:
+ for columnIndex in range(1, self.GetColumnCount()):
+ for icon in regularIcons:
+ self.__refreshAttribute(item, index, 'ItemImage', icon,
+ columnIndex)
+
+ def RefreshItemType(self, item, index):
+ return self.__refreshAttribute(item, index, 'ItemType')
+
+ def RefreshCheckedState(self, item, index):
+ self.__refreshAttribute(item, index, 'ItemChecked')
+
+ def ChildIndices(self, itemIndex):
+ if self._tupleIndex:
+ childrenCount = self.OnGetChildrenCount(itemIndex)
+ return [itemIndex + (childNumber,) for childNumber \
+ in range(childrenCount)]
+ else:
+ return self.OnGetChildrenIndices(itemIndex)
+
+ def OnItemExpanding(self, event):
+ self.RefreshChildrenRecursively(event.GetItem())
+ event.Skip()
+
+ def OnItemCollapsed(self, event):
+ parent = self.GetItemParent(event.GetItem())
+ if not parent:
+ parent = self.GetRootItem()
+ self.RefreshChildrenRecursively(parent)
+ event.Skip()
+
+ def __refreshAttribute(self, item, index, attribute, *args):
+ ''' Refresh the specified attribute if necessary. '''
+ value = getattr(self, 'OnGet%s'%attribute)(index, *args)
+ if getattr(self, 'Get%s'%attribute)(item, *args) != value:
+ return getattr(self, 'Set%s'%attribute)(item, value, *args)
+ else:
+ return item
+
+
+class DragAndDrop(TreeAPIHarmonizer, TreeHelper):
+ ''' This is a mixin class that can be used to easily implement
+ dragging and dropping of tree items. It can be mixed in with
+ wx.TreeCtrl, wx.gizmos.TreeListCtrl, or wx.lib.customtree.CustomTreeCtrl.
+
+ To use it derive a new class from this class and one of the tree
+ controls, e.g.:
+ class MyTree(DragAndDrop, wx.TreeCtrl):
+ ...
+
+ You *must* implement OnDrop. OnDrop is called when the user has
+ dropped an item on top of another item. It's up to you to decide how
+ to handle the drop. If you are using this mixin together with the
+ VirtualTree mixin, it makes sense to rearrange your underlying data
+ and then call RefreshItems to let the virtual tree refresh itself. '''
+
+ def __init__(self, *args, **kwargs):
+ kwargs['style'] = kwargs.get('style', wx.TR_DEFAULT_STYLE) | \
+ wx.TR_HIDE_ROOT
+ super(DragAndDrop, self).__init__(*args, **kwargs)
+ self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag)
+
+ def OnDrop(self, dropItem, dragItem):
+ ''' This function must be overloaded in the derived class.
+ dragItem is the item being dragged by the user. dropItem is the
+ item dragItem is dropped upon. If the user doesn't drop dragItem
+ on another item, dropItem equals the (hidden) root item of the
+ tree control. '''
+ raise NotImplementedError
+
+ def OnBeginDrag(self, event):
+ # We allow only one item to be dragged at a time, to keep it simple
+ self._dragItem = event.GetItem()
+ if self._dragItem and self._dragItem != self.GetRootItem():
+ self.StartDragging()
+ event.Allow()
+ else:
+ event.Veto()
+
+ def OnEndDrag(self, event):
+ self.StopDragging()
+ dropTarget = event.GetItem()
+ if not dropTarget:
+ dropTarget = self.GetRootItem()
+ if self.IsValidDropTarget(dropTarget):
+ self.UnselectAll()
+ if dropTarget != self.GetRootItem():
+ self.SelectItem(dropTarget)
+ self.OnDrop(dropTarget, self._dragItem)
+
+ def OnDragging(self, event):
+ if not event.Dragging():
+ self.StopDragging()
+ return
+ item, flags, column = self.HitTest(wx.Point(event.GetX(), event.GetY()),
+ alwaysReturnColumn=True)
+ if not item:
+ item = self.GetRootItem()
+ if self.IsValidDropTarget(item):
+ self.SetCursorToDragging()
+ else:
+ self.SetCursorToDroppingImpossible()
+ if flags & wx.TREE_HITTEST_ONITEMBUTTON:
+ self.Expand(item)
+ if self.GetSelections() != [item]:
+ self.UnselectAll()
+ if item != self.GetRootItem():
+ self.SelectItem(item)
+ event.Skip()
+
+ def StartDragging(self):
+ self.GetMainWindow().Bind(wx.EVT_MOTION, self.OnDragging)
+ self.Bind(wx.EVT_TREE_END_DRAG, self.OnEndDrag)
+ self.SetCursorToDragging()
+
+ def StopDragging(self):
+ self.GetMainWindow().Unbind(wx.EVT_MOTION)
+ self.Unbind(wx.EVT_TREE_END_DRAG)
+ self.ResetCursor()
+ self.UnselectAll()
+ self.SelectItem(self._dragItem)
+
+ def SetCursorToDragging(self):
+ self.GetMainWindow().SetCursor(wx.StockCursor(wx.CURSOR_HAND))
+
+ def SetCursorToDroppingImpossible(self):
+ self.GetMainWindow().SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY))
+
+ def ResetCursor(self):
+ self.GetMainWindow().SetCursor(wx.NullCursor)
+
+ def IsValidDropTarget(self, dropTarget):
+ if dropTarget:
+ allChildren = self.GetItemChildren(self._dragItem, recursively=True)
+ parent = self.GetItemParent(self._dragItem)
+ return dropTarget not in [self._dragItem, parent] + allChildren
+ else:
+ return True
+
+
+class ExpansionState(TreeAPIHarmonizer, TreeHelper):
+ ''' This is a mixin class that can be used to save and restore
+ the expansion state (i.e. which items are expanded and which items
+ are collapsed) of a tree. It can be mixed in with wx.TreeCtrl,
+ wx.gizmos.TreeListCtrl, or wx.lib.customtree.CustomTreeCtrl.
+
+ To use it derive a new class from this class and one of the tree
+ controls, e.g.:
+ class MyTree(ExpansionState, wx.TreeCtrl):
+ ...
+
+ By default, ExpansionState uses the position of tree items in the tree
+ to keep track of which items are expanded. This should be sufficient
+ for the simple scenario where you save the expansion state of the tree
+ when the user closes the application or file so that you can restore
+ the expansion state when the user start the application or loads that
+ file for the next session.
+
+ If you need to add or remove items between the moments of saving and
+ restoring the expansion state (e.g. in case of a multi-user application)
+ you must override GetItemIdentity so that saving and loading of the
+ expansion doesn't depend on the position of items in the tree, but
+ rather on some more stable characteristic of the underlying domain
+ object, e.g. a social security number in case of persons or an isbn
+ number in case of books. '''
+
+ def GetItemIdentity(self, item):
+ ''' Return a hashable object that represents the identity of the
+ item. By default this returns the position of the item in the
+ tree. You may want to override this to return the item label
+ (if you know that labels are unique and don't change), or return
+ something that represents the underlying domain object, e.g.
+ a database key. '''
+ return self.ItemIndex(item)
+
+ def GetExpansionState(self):
+ ''' GetExpansionState() -> list of expanded items. Expanded items
+ are coded as determined by the result of GetItemIdentity(item). '''
+ root = self.GetRootItem()
+ if not root:
+ return []
+ if self.HasFlag(wx.TR_HIDE_ROOT):
+ return self.GetExpansionStateOfChildren(root)
+ else:
+ return self.GetExpansionStateOfItem(root)
+
+ def SetExpansionState(self, listOfExpandedItems):
+ ''' SetExpansionState(listOfExpandedItems). Expands all tree items
+ whose identity, as determined by GetItemIdentity(item), is present
+ in the list and collapses all other tree items. '''
+ root = self.GetRootItem()
+ if not root:
+ return
+ if self.HasFlag(wx.TR_HIDE_ROOT):
+ self.SetExpansionStateOfChildren(listOfExpandedItems, root)
+ else:
+ self.SetExpansionStateOfItem(listOfExpandedItems, root)
+
+ ExpansionState = property(GetExpansionState, SetExpansionState)
+
+ def GetExpansionStateOfItem(self, item):
+ listOfExpandedItems = []
+ if self.IsExpanded(item):
+ listOfExpandedItems.append(self.GetItemIdentity(item))
+ listOfExpandedItems.extend(self.GetExpansionStateOfChildren(item))
+ return listOfExpandedItems
+
+ def GetExpansionStateOfChildren(self, item):
+ listOfExpandedItems = []
+ for child in self.GetItemChildren(item):
+ listOfExpandedItems.extend(self.GetExpansionStateOfItem(child))
+ return listOfExpandedItems
+
+ def SetExpansionStateOfItem(self, listOfExpandedItems, item):
+ if self.GetItemIdentity(item) in listOfExpandedItems:
+ self.Expand(item)
+ self.SetExpansionStateOfChildren(listOfExpandedItems, item)
+ else:
+ self.Collapse(item)
+
+ def SetExpansionStateOfChildren(self, listOfExpandedItems, item):
+ for child in self.GetItemChildren(item):
+ self.SetExpansionStateOfItem(listOfExpandedItems, child)
diff --git a/wxPython/wx/lib/ogl/_basic.py b/wxPython/wx/lib/ogl/_basic.py
index db095dd165..5d5f461fce 100644
--- a/wxPython/wx/lib/ogl/_basic.py
+++ b/wxPython/wx/lib/ogl/_basic.py
@@ -67,9 +67,6 @@ class ShapeEvtHandler(object):
self._previousHandler = prev
self._handlerShape = shape
- def __del__(self):
- pass
-
def SetShape(self, sh):
self._handlerShape = sh
@@ -311,9 +308,6 @@ class Shape(ShapeEvtHandler):
self.GetEventHandler().OnDelete()
self._eventHandler = None
- def __del__(self):
- ShapeEvtHandler.__del__(self)
-
def Draggable(self):
"""TRUE if the shape may be dragged by the user."""
return True
diff --git a/wxPython/wx/lib/ogl/_lines.py b/wxPython/wx/lib/ogl/_lines.py
index d4da07dbd2..77ce392cac 100644
--- a/wxPython/wx/lib/ogl/_lines.py
+++ b/wxPython/wx/lib/ogl/_lines.py
@@ -218,16 +218,6 @@ class LineShape(Shape):
self._lineControlPoints = []
self._arcArrows = []
- def __del__(self):
- if self._lineControlPoints:
- self._lineControlPoints = []
- for i in range(3):
- if self._labelObjects[i]:
- self._labelObjects[i].Select(False)
- self._labelObjects[i].RemoveFromCanvas(self._canvas)
- self._labelObjects = []
- self.ClearArrowsAtPosition(-1)
-
def GetFrom(self):
"""Return the 'from' object."""
return self._from
@@ -492,6 +482,11 @@ class LineShape(Shape):
self._from.GetLines().remove(self)
self._to = None
self._from = None
+ for i in range(3):
+ if self._labelObjects[i]:
+ self._labelObjects[i].Select(False)
+ self._labelObjects[i].RemoveFromCanvas(self._canvas)
+ self.ClearArrowsAtPosition(-1)
def SetEnds(self, x1, y1, x2, y2):
"""Set the end positions of the line."""
diff --git a/wxPython/wx/lib/plot.py b/wxPython/wx/lib/plot.py
index 0e5d01f1ec..ee1b9cf914 100644
--- a/wxPython/wx/lib/plot.py
+++ b/wxPython/wx/lib/plot.py
@@ -567,6 +567,8 @@ class PlotCanvas(wx.Panel):
self._pointLabelFunc= None
self.canvas.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
+ self._useScientificNotation = False
+
self.canvas.Bind(wx.EVT_PAINT, self.OnPaint)
self.canvas.Bind(wx.EVT_SIZE, self.OnSize)
# OnSize called to make sure the buffer is initialized.
@@ -746,6 +748,12 @@ class PlotCanvas(wx.Panel):
"""Set True to show scrollbars"""
return self.sb_vert.IsShown()
+ def SetUseScientificNotation(self, useScientificNotation):
+ self._useScientificNotation = useScientificNotation
+
+ def GetUseScientificNotation(self):
+ return self._useScientificNotation
+
def SetEnableDrag(self, value):
"""Set True to enable drag."""
if value not in [True,False]:
@@ -1131,7 +1139,7 @@ class PlotCanvas(wx.Panel):
l.append(cn)
return l
- def GetClosetPoint(self, pntXY, pointScaled= True):
+ def GetClosestPoint(self, pntXY, pointScaled= True):
"""Returns list with
[curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
list for only the closest curve.
@@ -1151,6 +1159,8 @@ class PlotCanvas(wx.Panel):
mdist = min(dists) #Min dist
i = dists.index(mdist) #index for min dist
return closestPts[i] #this is the closest point on closest curve
+
+ GetClosetPoint = GetClosestPoint
def UpdatePointLabel(self, mDataDict):
"""Updates the pointLabel point on screen with data contained in
@@ -1565,7 +1575,7 @@ class PlotCanvas(wx.Panel):
error = e
factor = f
grid = factor * 10.**power
- if power > 4 or power < -4:
+ if self._useScientificNotation and (power > 4 or power < -4):
format = '%+7.1e'
elif power >= 0:
digits = max(1, int(power))
@@ -2021,7 +2031,7 @@ class TestFrame(wx.Frame):
if self.client.GetEnablePointLabel() == True:
#make up dict with info for the pointLabel
#I've decided to mark the closest point on the closest curve
- dlst= self.client.GetClosetPoint( self.client._getXY(event), pointScaled= True)
+ dlst= self.client.GetClosestPoint( self.client._getXY(event), pointScaled= True)
if dlst != []: #returns [] if none
curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
#make up dictionary to pass to my user function (see DrawPointLabel)
diff --git a/wxPython/wx/lib/wxpTag.py b/wxPython/wx/lib/wxpTag.py
index d362fb365f..13d2a172f9 100644
--- a/wxPython/wx/lib/wxpTag.py
+++ b/wxPython/wx/lib/wxpTag.py
@@ -163,16 +163,13 @@ class wxpTagHandler(wx.html.HtmlWinTagHandler):
# create the object
parent = self.GetParser().GetWindowInterface().GetHTMLWindow()
if parent:
- obj = apply(self.ctx.classObj,
- (parent,),
- self.ctx.kwargs)
+ obj = self.ctx.classObj(parent, **self.ctx.kwargs)
obj.Show(True)
# add it to the HtmlWindow
self.GetParser().GetContainer().InsertCell(
wx.html.HtmlWidgetCell(obj, self.ctx.floatWidth))
self.ctx = None
-
return True
diff --git a/wxPython/wx/py/frame.py b/wxPython/wx/py/frame.py
index 2ca5ec9061..904c1b01fb 100644
--- a/wxPython/wx/py/frame.py
+++ b/wxPython/wx/py/frame.py
@@ -646,7 +646,7 @@ class Frame(wx.Frame):
def LoadSettings(self, config):
- """Called be derived classes to load settings specific to the Frame"""
+ """Called by derived classes to load settings specific to the Frame"""
pos = wx.Point(config.ReadInt('Window/PosX', -1),
config.ReadInt('Window/PosY', -1))
diff --git a/wxPython/wx/tools/XRCed/CHANGES.txt b/wxPython/wx/tools/XRCed/CHANGES.txt
index 91b4dbd163..212c61507f 100644
--- a/wxPython/wx/tools/XRCed/CHANGES.txt
+++ b/wxPython/wx/tools/XRCed/CHANGES.txt
@@ -1,3 +1,66 @@
+0.1.8-5 (under construction)
+-------
+
+Preferences for default "sizeritem" parameters for new panels and
+controls can be configured ("File">"Preferences...").
+
+Implemented comment object for including simple one-line comments and
+comment directives as tree nodes. No validation is performed for a
+valid XML string so comments must not contain "-->". Comment directive
+is a special comment starting with '%' character, followed by a line
+of python code. It is executed using 'exec' when the resource file is
+opened. This is useful to import plugin modules containing custom
+handlers which are specific to the resource file, hovewer this is of
+course a security hole if you use foreign XRC files. A warning is
+displayed if the preference option 'ask' is selected (by default).
+
+Added support for custom controls and plugin modules. Refer to this
+wxPythonWiki for the details:
+ http://wiki.wxpython.org/index.cgi/XRCed#custom
+
+Tool panel sections can be collapsed/expanded by clicking on the
+label of a tool group.
+
+Some undo/redo and other fixes.
+
+0.1.8-4
+-------
+
+Fixes for wxMSW (notebook highlighting, control sizes, tree Unselect).
+
+0.1.8-3
+-------
+
+Notebook page highlighting fix. Highlight resizes when the window
+is resized. ParamUnit spin button detects event handler re-entry
+(wxGTK probably has a bug in wxSpinButton with repeated events).
+
+0.1.8-2
+-------
+
+Fix for dealing with empty 'growable' property, using MiniFrame
+for properties panel, the panel is restored together with the
+main window.
+
+0.1.8-1
+-------
+
+Fixed SetItemBold and editing 'growable' properties of
+wxFlexGridSizer.
+
+0.1.8-0
+-------
+
+New feature added from a patch submitted on sourceforge by botg:
+moving of tree items.
+
+Bugs fixed:
+- ChecklistBox content editing;
+- Window styles more in sync with the docs;
+- Replacing items;
+- Reference property page created correctly (every property is
+ optional).
+
0.1.7-5
-------
diff --git a/wxPython/wx/tools/XRCed/TODO.txt b/wxPython/wx/tools/XRCed/TODO.txt
index fddee3ba32..8246420546 100644
--- a/wxPython/wx/tools/XRCed/TODO.txt
+++ b/wxPython/wx/tools/XRCed/TODO.txt
@@ -1,6 +1,8 @@
TODO for XRCed
==============
+- undo for Replace op
+
- better help
+ undo/redo
@@ -28,4 +30,4 @@ TODO for XRCed
+ selecting object by clicking in test window
-- hidden items should not be highlighted
++ hidden items should not be highlighted
diff --git a/wxPython/wx/tools/XRCed/globals.py b/wxPython/wx/tools/XRCed/globals.py
index 845e3231ff..ca71046967 100644
--- a/wxPython/wx/tools/XRCed/globals.py
+++ b/wxPython/wx/tools/XRCed/globals.py
@@ -15,7 +15,7 @@ import sys
# Global constants
progname = 'XRCed'
-version = '0.1.7-5'
+version = '0.1.8-5'
# Minimal wxWidgets version
MinWxVersion = (2,6,0)
if wx.VERSION[:3] < MinWxVersion:
@@ -48,6 +48,7 @@ class Globals:
testWinPos = wx.DefaultPosition
currentXXX = None
currentEncoding = defaultEncoding
+ conf = None
def _makeFonts(self):
self._sysFont = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)
diff --git a/wxPython/wx/tools/XRCed/images.py b/wxPython/wx/tools/XRCed/images.py
index 6d42855e83..6d133911ce 100644
--- a/wxPython/wx/tools/XRCed/images.py
+++ b/wxPython/wx/tools/XRCed/images.py
@@ -646,6 +646,118 @@ def getToolMenuItemIcon():
icon.CopyFromBitmap(getToolMenuItemBitmap())
return icon
+#----------------------------------------------------------------------
+def getToolMoveDownData():
+ return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\x00\x00\x00\x17\x08\x06\
+\x00\x00\x00\x11!\x8f-\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
+\x00\xf8IDATH\x89\xb5UA\x12\x830\x08\\H\xbf\xd5\x96\x9f\xd9\xfc\xcc\xb6\xefR\
+zI\xa6)\xa2I\xac2\x93q$\x86ea\x83D\x1cp\xa6\xb1u\xe8<\xe9\xa9\x00\x00p\xbf]\
+\x0f\x03\xb9\xd4>\xf0\x18\x11\x07j\x05p\x19x\x01\xf3\x02\xfa\x18\xba\x0c\xc6\
+q\x04\x80\xcd sb\xc6\x156.\x80\x88X\xb0\xdd@\xab=x\xbe\xde\xf9\x80z}\x88\xe9\
+9\x14@\x1eH\xb5\xc9\xc4\x81\xbc\x9a\xc7\x1c,\x81\x0fk \xc4\xe1g\x01P\x11Q\
+\xeb\xaf\xad\x07\xa0\n,\xce\x1d.S\xcb\xe20\x99\xc6\x9e&\xb7\xc8\xb4\xd5\xfe\
+\x96\xe9.\x00\xa0.\xd3V\xab\xf6\x808\x90\x88@D\xa0\xa8\xd7-ZG\xabL5\xc9\xd0\
+\x93b\xb9\xdf-S\xe0;\x0e\x80\xe5H(\xf7\x16\xd9\xa3\xe1&\xcf\xa6\xfe\xf6\xbd\
+\x0c\xeeI\xb5\x89\xc1\x96\xe5\xac\xbb\xeeAi\xcc\x81l\x89\x86\xb2,\x95qM\xf6\
+\xa7\xaf\xf3\xa4\xde(\xd8\xea\xc3f\x82\x0b\xc4\x95\xc3=AK\xfb\x005\x00\x82C\
+\xb1\xf0h\xde\x00\x00\x00\x00IEND\xaeB`\x82'
+
+def getToolMoveDownBitmap():
+ return BitmapFromImage(getToolMoveDownImage())
+
+def getToolMoveDownImage():
+ stream = cStringIO.StringIO(getToolMoveDownData())
+ return ImageFromStream(stream)
+
+def getToolMoveDownIcon():
+ icon = EmptyIcon()
+ icon.CopyFromBitmap(getToolMoveDownBitmap())
+ return icon
+
+#----------------------------------------------------------------------
+def getToolMoveLeftData():
+ return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\x00\x00\x00\x17\x08\x06\
+\x00\x00\x00\x11!\x8f-\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
+\x00\xcaIDATH\x89\xb5\x95\xe1\x0e\x83 \x0c\x84\xafe\xaf\xa5\xb27g\xdbs\r\xfc\
+#\x0b#\xad\x14\xc1KL\x0c\x9a~\xbd\xf6\x8cD\xecp\xa7\xb8>H\xf1\x9bn\x05\x00\
+\xc0\xb6.\xd3 \x8f\xd6\x0b\x92#bGV\x80\xe8@*\x98/\xa0\xcf\xa1\xe8 \x84\x00\
+\x00S\xc6$\x02\xbc\xf75l.\x00\x00^\xefO\x9es\x1aIVs\xc9\xc4\x8e\xb6uI!\x84?g\
+\xd3\x00\xc0\xcfM*\xee\xe7\x02$Y\xe3k\x8a\xa9&K|\xbb\x1c\\\x89\xaf\x19\x90\
+\x97]\xc1\xc6\x01\xf1\x985\xb3\xa3j\xc1\xa6\xf8\x9e\x02\xca\xe2\xd2\xf9\xd3\
+\x10[\x15\x10\x8b\xee\xa2\xd2\xa9%\xb2\x97ST\xbb\xd2\xa4:`v\xa4\x8d\xa8\xab\
+\x91\xd3\x87GamD\x165S4\xd2=0\xf8%_\x02\xf4\xfc\x0e-\xda\x01\xe9\x13V\xf6\
+\xde\n\xe5\xa2\x00\x00\x00\x00IEND\xaeB`\x82'
+
+def getToolMoveLeftBitmap():
+ return BitmapFromImage(getToolMoveLeftImage())
+
+def getToolMoveLeftImage():
+ stream = cStringIO.StringIO(getToolMoveLeftData())
+ return ImageFromStream(stream)
+
+def getToolMoveLeftIcon():
+ icon = EmptyIcon()
+ icon.CopyFromBitmap(getToolMoveLeftBitmap())
+ return icon
+
+#----------------------------------------------------------------------
+def getToolMoveRightData():
+ return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\x00\x00\x00\x17\x08\x06\
+\x00\x00\x00\x11!\x8f-\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
+\x00\xcdIDATH\x89\xed\x94]\x0e\x02!\x0c\x84\xa7\xc5k\xed\x8f7sOf\xdd=\x97\
+\xd4\x170,\xc1Pv\xd1\'\'!\x01\x12\xfa1mS"vH\xa5\xfe\xa9\xc4\x8e\xd0I\\\xba\
+\x9c\xc6AS`\xbeN\x03r\x11;\x8a+\xff@M\x97\xd2\xa5\x88\x00@\xd3O\x9b\x00\xf3<\
+\xe7\xb0\xbe\x00\x00x\xac[,ts\xdeM\x80(bG\xd38\xa8\x88\xec\x9cu\x03\x00o7\
+\x9a\xecQrUjoS\x17}\x92\xa5\xbbL\x0e\xa2\x8et\x97\xd9\x01\xb1#\r\xd1[jav\xe0\
+\x93\x9c\xdfE@(\xd7\xe1\x10\xc0\x17\x02)\x80\xab\xc1\x89\t\xc0\xa1\x88\x11\
+\x14\xcfX\xb7\xfa[\x0b\xe0\x8c\xbe\x0e\xa8\xa6(-\xe4r\x00\xd04\xae\x17vt\xc3\
+\x7f\\\x07\xfd|\\w\x03\x84\xc9\xb8\x0b\x98\xb8i\xd6\x0b\'hn\x9c\n\x87\xccd\
+\x00\x00\x00\x00IEND\xaeB`\x82'
+
+def getToolMoveRightBitmap():
+ return BitmapFromImage(getToolMoveRightImage())
+
+def getToolMoveRightImage():
+ stream = cStringIO.StringIO(getToolMoveRightData())
+ return ImageFromStream(stream)
+
+def getToolMoveRightIcon():
+ icon = EmptyIcon()
+ icon.CopyFromBitmap(getToolMoveRightBitmap())
+ return icon
+
+#----------------------------------------------------------------------
+def getToolMoveUpData():
+ return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\x00\x00\x00\x17\x08\x06\
+\x00\x00\x00\x11!\x8f-\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
+\x00\xefIDATH\x89\xb5\x96Q\x12\xc2 \x0cD7\xc1k\xa9\xdc\xac\xc3\xcd\xaa\x9e\
+\x0b\xf0C\x19\x91\x92\x10:mf\xf2\xd3\xd2}IX\x98\x12\xb1\xc3\x99\xc1\xa7\xaa\
+\xb7\x80\x9cb\x96\x16\xa6\x14sR\xde\x9b\x00\x00p\xbf]7"E\x98\xd9\xd1,\xe02ZP\
+W\xddv`\x01\xaa{0\x1a\x89el*\xa0\xad\x90\xd9Q\x9d\x96B\x86.\xd2\x84\x98\x1d\
+\x85\x01\xc4dSm\xd6\xa1\x82,\x1d\x88\n\xc8)\xe6\x92\x04@\xa2\x84o\x01K\xaf8\
+\r\x00\x00\xc4\x8eJ\x02}\x1bk\xb1\xb1\xe9\xba\xae\x000}\xa0\xcc\x00\xef}\x0b\
+;\x16\x00\x00\x8f\xe7\xab\x8c;k\xd7\xc7n@\tbG33\x0f\x9dg\xc3\xab\xa2\xeaF\
+\x0c\xed\xa0\xa9\x80\xdexH8x\x01?\xbb\xd6\xb1\xcb\xa6\xed\x1d$\x89w;\x98\xb1\
+i\x99\xb9$\xbe\x01\xb4\x9b*\xd9\xd4"\\\x8b\x8a\x89O\'\x7f\xe9\xbd\xcf\xda7m\
+\x1ej\xd3\xae\xc6\xd9\x7f\x15o\xbc]\x81\xb1$]\x16\xd6\x00\x00\x00\x00IEND\
+\xaeB`\x82'
+
+def getToolMoveUpBitmap():
+ return BitmapFromImage(getToolMoveUpImage())
+
+def getToolMoveUpImage():
+ stream = cStringIO.StringIO(getToolMoveUpData())
+ return ImageFromStream(stream)
+
+def getToolMoveUpIcon():
+ icon = EmptyIcon()
+ icon.CopyFromBitmap(getToolMoveUpBitmap())
+ return icon
+
#----------------------------------------------------------------------
def getToolNotebookData():
return \
@@ -1177,6 +1289,27 @@ def getToolUnknownIcon():
icon.CopyFromBitmap(getToolUnknownBitmap())
return icon
+#----------------------------------------------------------------------
+def getTreeCommentData():
+ return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x0e\x08\x06\
+\x00\x00\x00&/\x9c\x8a\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
+\x00AIDAT(\x91cddbf\xa0\x040Q\xa4\x9bf\x06\xfc\xff\xf7\xf7\xff\xff\x7f\x7f\
+\xff\xa3\xb3\xe9\xe7\x02R\x00#z,\xe0s.#\x133#A\x17 +\xc2\xc5\xc6k\x00\xa9`\
+\x18x\x81\xfaa@*\x00\x00\xb0\xac\x18?Q\xffAR\x00\x00\x00\x00IEND\xaeB`\x82'
+
+def getTreeCommentBitmap():
+ return BitmapFromImage(getTreeCommentImage())
+
+def getTreeCommentImage():
+ stream = cStringIO.StringIO(getTreeCommentData())
+ return ImageFromStream(stream)
+
+def getTreeCommentIcon():
+ icon = EmptyIcon()
+ icon.CopyFromBitmap(getTreeCommentBitmap())
+ return icon
+
#----------------------------------------------------------------------
def getTreeDefaultData():
return \
diff --git a/wxPython/wx/tools/XRCed/panel.py b/wxPython/wx/tools/XRCed/panel.py
index 42722aa53e..d05dc3793c 100644
--- a/wxPython/wx/tools/XRCed/panel.py
+++ b/wxPython/wx/tools/XRCed/panel.py
@@ -22,7 +22,7 @@ class Panel(wx.Notebook):
# Set common button size for parameter buttons
bTmp = wx.Button(self, -1, '')
import params
- params.buttonSize = (self.DLG_SZE(buttonSize)[0], bTmp.GetSize()[1])
+ params.buttonSize = (self.DLG_SZE(buttonSizeD)[0], bTmp.GetSize()[1])
bTmp.Destroy()
del bTmp
@@ -89,7 +89,7 @@ class Panel(wx.Notebook):
g.currentXXX = xxx.treeObject()
# Normal or SizerItem page
isGBSizerItem = isinstance(xxx.parent, xxxGridBagSizer)
- cacheID = (xxx.__class__, isGBSizerItem)
+ cacheID = (xxx.panelName(), isGBSizerItem)
try:
page = self.pageCache[cacheID]
page.box.SetLabel(xxx.panelName())
@@ -102,7 +102,7 @@ class Panel(wx.Notebook):
sizer.Add(page, 1, wx.EXPAND)
if xxx.hasChild:
# Special label for child objects - they may have different GUI
- cacheID = (xxx.child.__class__, xxx.__class__)
+ cacheID = (xxx.child.panelName(), xxx.__class__)
try:
page = self.pageCache[cacheID]
page.box.SetLabel(xxx.child.panelName())
@@ -160,6 +160,7 @@ class Panel(wx.Notebook):
# Register undo object when modifying first time
if not self.modified and value:
g.undoMan.RegisterUndo(UndoEdit())
+ g.frame.SetModified()
self.modified = value
def Apply(self):
@@ -184,7 +185,7 @@ class ParamPage(wx.Panel):
param = evt.GetEventObject().GetName()
w = self.controls[param]
w.Enable(True)
- objElem = xxx.element
+ objElem = xxx.node
if evt.IsChecked():
# Ad new text node in order of allParams
w.SetValue('') # set empty (default) value
@@ -192,7 +193,7 @@ class ParamPage(wx.Panel):
elem = g.tree.dom.createElement(param)
# Some classes are special
if param == 'font':
- xxx.params[param] = xxxParamFont(xxx.element, elem)
+ xxx.params[param] = xxxParamFont(xxx.node, elem)
elif param in xxxObject.bitmapTags:
xxx.params[param] = xxxParamBitmap(elem)
else:
@@ -218,7 +219,7 @@ class ParamPage(wx.Panel):
xxx.params[param].remove()
del xxx.params[param]
w.SetValue('')
- w.modified = False # mark as not changed
+ w.SetModified(False) # mark as not changed
w.Enable(False)
# Set modified flag (provokes undo storing is necessary)
panel.SetModified(True)
@@ -228,7 +229,7 @@ class ParamPage(wx.Panel):
name = self.controlName.GetValue()
if xxx.name != name:
xxx.name = name
- xxx.element.setAttribute('name', name)
+ xxx.node.setAttribute('name', name)
for param, w in self.controls.items():
if w.modified:
paramObj = xxx.params[param]
@@ -237,10 +238,11 @@ class ParamPage(wx.Panel):
xxx.setSpecial(param, value)
else:
paramObj.update(value)
+
# Save current state
def SaveState(self):
self.origChecks = map(lambda i: (i[0], i[1].GetValue()), self.checks.items())
- self.origControls = map(lambda i: (i[0], i[1].GetValue(), i[1].IsEnabled()),
+ self.origControls = map(lambda i: (i[0], i[1].GetValue(), i[1].enabled),
self.controls.items())
if self.controlName:
self.origName = self.controlName.GetValue()
@@ -257,7 +259,8 @@ class ParamPage(wx.Panel):
for k,v,e in state[1]:
self.controls[k].SetValue(v)
self.controls[k].Enable(e)
- if e: self.controls[k].modified = True
+ # Set all states to modified
+ if e and k in self.xxx.params: self.controls[k].modified = True
if self.controlName:
self.controlName.SetValue(state[2])
@@ -283,11 +286,15 @@ class PropPage(ParamPage):
for param in xxx.allParams:
present = xxx.params.has_key(param)
if param in xxx.required:
- label = wx.StaticText(self, paramIDs[param], param + ':',
- size = (LABEL_WIDTH,-1), name = param)
+ if isinstance(xxx, xxxComment):
+ label = None
+ else:
+ label = wx.StaticText(self, paramIDs[param], param + ':',
+ size = (LABEL_WIDTH,-1), name = param)
else:
- # Notebook has one very loooooong parameter
+ # Rename some parameters
if param == 'usenotebooksizer': sParam = 'usesizer:'
+ elif param == 'option': sParam = 'proportion'
else: sParam = param + ':'
label = wx.CheckBox(self, paramIDs[param], sParam,
size = (LABEL_WIDTH,-1), name = param)
@@ -303,13 +310,19 @@ class PropPage(ParamPage):
typeClass = ParamText
control = typeClass(self, param)
control.Enable(present)
- sizer.AddMany([ (label, 0, wx.ALIGN_CENTER_VERTICAL),
- (control, 0, wx.ALIGN_CENTER_VERTICAL | wx.GROW) ])
+ # Comment has only one parameter
+ if isinstance(xxx, xxxComment):
+ # Bind char event to check Enter key
+ control.text.Bind(wx.EVT_CHAR, self.OnEnter)
+ sizer.Add(control, 0, wx.ALIGN_CENTER_VERTICAL | wx.GROW)
+ else:
+ sizer.AddMany([ (label, 0, wx.ALIGN_CENTER_VERTICAL),
+ (control, 0, wx.ALIGN_CENTER_VERTICAL | wx.GROW) ])
self.controls[param] = control
topSizer.Add(sizer, 1, wx.ALL | wx.EXPAND, 3)
- self.SetAutoLayout(True)
self.SetSizer(topSizer)
topSizer.Fit(self)
+
def SetValues(self, xxx):
self.xxx = xxx
self.origChecks = []
@@ -336,6 +349,13 @@ class PropPage(ParamPage):
self.origChecks.append((param, False))
self.origControls.append((param, '', False))
+ # This is called only for comment now
+ def OnEnter(self, evt):
+ if evt.GetKeyCode() == 13:
+ g.tree.Apply(self.xxx, g.tree.selection)
+ else:
+ evt.Skip()
+
################################################################################
# Style notebook page
@@ -362,6 +382,7 @@ class StylePage(ParamPage):
self.SetAutoLayout(True)
self.SetSizer(topSizer)
topSizer.Fit(self)
+
# Set data for a cahced page
def SetValues(self, xxx):
self.xxx = xxx
diff --git a/wxPython/wx/tools/XRCed/params.py b/wxPython/wx/tools/XRCed/params.py
index 71a7414989..7142449f84 100644
--- a/wxPython/wx/tools/XRCed/params.py
+++ b/wxPython/wx/tools/XRCed/params.py
@@ -25,7 +25,10 @@ genericExStyles = [
'wxWS_EX_PROCESS_UI_UPDATES'
]
-buttonSize = (35,-1) # in dialog units, transformed to pixels in panel ctor
+# Global var initialized in Panel.__init__ for button size in screen pixels
+buttonSize = None
+# Button size in dialog units
+buttonSizeD = (35,-1)
# Class that can properly disable children
class PPanel(wx.Panel):
@@ -33,13 +36,14 @@ class PPanel(wx.Panel):
wx.Panel.__init__(self, parent, -1, name=name)
self.modified = self.freeze = False
def Enable(self, value):
+ self.enabled = value
# Something strange is going on with enable so we make sure...
for w in self.GetChildren():
w.Enable(value)
#wx.Panel.Enable(self, value)
- def SetModified(self):
- self.modified = True
- g.panel.SetModified(True)
+ def SetModified(self, state=True):
+ self.modified = state
+ if state: g.panel.SetModified(True)
# Common method to set modified state
def OnChange(self, evt):
if self.freeze: return
@@ -56,9 +60,7 @@ class ParamBinaryOr(PPanel):
sizer.Add(self.text, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.button = wx.Button(self, self.ID_BUTTON_CHOICES, 'Edit...', size=buttonSize)
sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL)
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
wx.EVT_BUTTON(self, self.ID_BUTTON_CHOICES, self.OnButtonChoices)
wx.EVT_TEXT(self, self.ID_TEXT_CTRL, self.OnChange)
def GetValue(self):
@@ -94,10 +96,7 @@ class ParamBinaryOr(PPanel):
value.append(self.values[i])
# Add ignored flags
value.extend(ignored)
- if value:
- self.SetValue(reduce(lambda a,b: a+'|'+b, value))
- else:
- self.SetValue('')
+ self.SetValue('|'.join(value))
self.SetModified()
dlg.Destroy()
@@ -172,10 +171,7 @@ class ParamStyle(ParamBinaryOr):
[self.valuesGeneric[i]
for i in range(listBoxGeneric.GetCount())
if listBoxGeneric.IsChecked(i)] + ignored
- if value:
- self.SetValue(reduce(lambda a,b: a+'|'+b, value))
- else:
- self.SetValue('')
+ self.SetValue('|'.join(value))
self.SetModified()
dlg.Destroy()
@@ -197,9 +193,7 @@ class ParamColour(PPanel):
sizer.Add(self.text, 0, wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.BOTTOM, 2)
self.button = wx.Panel(self, self.ID_BUTTON, wx.DefaultPosition, wx.Size(20, 20))
sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
self.textModified = False
wx.EVT_PAINT(self.button, self.OnPaintButton)
wx.EVT_TEXT(self, self.ID_TEXT_CTRL, self.OnChange)
@@ -259,9 +253,7 @@ class ParamFont(PPanel):
sizer.Add(self.text, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.button = wx.Button(self, self.ID_BUTTON_SELECT, 'Select...', size=buttonSize)
sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL)
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
self.textModified = False
wx.EVT_BUTTON(self, self.ID_BUTTON_SELECT, self.OnButtonSelect)
wx.EVT_TEXT(self, self.ID_TEXT_CTRL, self.OnChange)
@@ -326,7 +318,6 @@ class ParamFont(PPanel):
dlg = wx.FontDialog(self, data)
if dlg.ShowModal() == wx.ID_OK:
font = dlg.GetFontData().GetChosenFont()
- print font.GetEncoding()
if font.GetEncoding() == wx.FONTENCODING_SYSTEM:
encName = ''
else:
@@ -354,9 +345,7 @@ class ParamInt(PPanel):
self.spin = wx.SpinCtrl(self, self.ID_SPIN_CTRL, size=(60,-1))
self.spin.SetRange(-2147483648, 2147483647) # min/max integers
sizer.Add(self.spin)
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
wx.EVT_SPINCTRL(self, self.ID_SPIN_CTRL, self.OnChange)
def GetValue(self):
return str(self.spin.GetValue())
@@ -375,9 +364,7 @@ class ParamIntNN(PPanel):
self.spin = wx.SpinCtrl(self, self.ID_SPIN_CTRL, size=(60,-1))
self.spin.SetRange(0, 10000) # min/max integers
sizer.Add(self.spin)
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
wx.EVT_SPINCTRL(self, self.ID_SPIN_CTRL, self.OnChange)
def GetValue(self):
return str(self.spin.GetValue())
@@ -394,26 +381,23 @@ class ParamUnit(PPanel):
self.ID_TEXT_CTRL = wx.NewId()
self.ID_SPIN_BUTTON = wx.NewId()
sizer = wx.BoxSizer(wx.HORIZONTAL)
- self.spin = wx.SpinButton(self, self.ID_SPIN_BUTTON, style = wx.SP_VERTICAL, size=(-1,1))
+ self.spin = wx.SpinButton(self, self.ID_SPIN_BUTTON, style = wx.SP_VERTICAL, size=(-1,0))
textW = 60 - self.spin.GetSize()[0]
self.text = wx.TextCtrl(self, self.ID_TEXT_CTRL, size=(textW,-1))
self.spin.SetRange(-10000, 10000)
sizer.Add(self.text, 0, wx.EXPAND)
sizer.Add(self.spin, 0, wx.EXPAND)
- #sizer.SetMinSize((50,-1))
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
- wx.EVT_SPIN_UP(self, self.ID_SPIN_BUTTON, self.OnSpinUp)
- wx.EVT_SPIN_DOWN(self, self.ID_SPIN_BUTTON, self.OnSpinDown)
+ self.spin.Bind(wx.EVT_SPIN_UP, self.OnSpinUp)
+ self.spin.Bind(wx.EVT_SPIN_DOWN, self.OnSpinDown)
def GetValue(self):
return self.text.GetValue()
def SetValue(self, value):
- self.freeze = True
- if not value: value = '0'
+ if not value: value = '0'
self.text.SetValue(value)
- self.freeze = False
+ self.Change(0)
def Change(self, x):
+ self.freeze = True
# Check if we are working with dialog units
value = self.text.GetValue()
units = ''
@@ -423,14 +407,19 @@ class ParamUnit(PPanel):
try:
intValue = int(value) + x
self.spin.SetValue(intValue)
- self.text.SetValue(str(intValue) + units)
- self.SetModified()
+ if x: # 0 can be passed to update spin value only
+ self.text.SetValue(str(intValue) + units)
+ self.SetModified()
except:
# !!! Strange, if I use wx.LogWarning, event is re-generated
- print 'incorrect unit format'
+ print 'ERROR: incorrect unit format'
+ self.freeze = False
def OnSpinUp(self, evt):
+ self.freeze = True
self.Change(1)
def OnSpinDown(self, evt):
+ if self.freeze: return
+ self.freeze = True
self.Change(-1)
class ParamMultilineText(PPanel):
@@ -443,9 +432,7 @@ class ParamMultilineText(PPanel):
sizer.Add(self.text, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.button = wx.Button(self, self.ID_BUTTON_EDIT, 'Edit...', size=buttonSize)
sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL)
- self.SetAutoLayout(True)
- self.SetSizer(sizer)
- sizer.Fit(self)
+ self.SetSizerAndFit(sizer)
wx.EVT_BUTTON(self, self.ID_BUTTON_EDIT, self.OnButtonEdit)
wx.EVT_TEXT(self, self.ID_TEXT_CTRL, self.OnChange)
def GetValue(self):
@@ -464,18 +451,16 @@ class ParamMultilineText(PPanel):
dlg.Destroy()
class ParamText(PPanel):
- def __init__(self, parent, name, textWidth=-1):
+ def __init__(self, parent, name, textWidth=-1, style=0):
PPanel.__init__(self, parent, name)
self.ID_TEXT_CTRL = wx.NewId()
# We use sizer even here to have the same size of text control
sizer = wx.BoxSizer()
- self.text = wx.TextCtrl(self, self.ID_TEXT_CTRL, size=wx.Size(textWidth,-1))
+ self.text = wx.TextCtrl(self, self.ID_TEXT_CTRL, size=wx.Size(textWidth,-1), style=style)
if textWidth == -1: option = 1
else: option = 0
sizer.Add(self.text, option, wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.BOTTOM, 2)
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
wx.EVT_TEXT(self, self.ID_TEXT_CTRL, self.OnChange)
def GetValue(self):
return self.text.GetValue()
@@ -500,6 +485,11 @@ class ParamEncoding(ParamText):
def __init__(self, parent, name):
ParamText.__init__(self, parent, name, 100)
+class ParamComment(ParamText):
+ def __init__(self, parent, name):
+ ParamText.__init__(self, parent, name, 330 + buttonSize[0],
+ style=wx.TE_PROCESS_ENTER)
+
class ContentDialog(wx.Dialog):
def __init__(self, parent, value):
# Load from resource
@@ -555,7 +545,7 @@ class ContentCheckListDialog(wx.Dialog):
pre = wx.PreDialog()
g.frame.res.LoadOnDialog(pre, parent, 'DIALOG_CONTENT_CHECKLIST')
self.PostCreate(pre)
- self.list = xrc.XRCCTRL(self, 'CHECKLIST')
+ self.list = xrc.XRCCTRL(self, 'CHECK_LIST')
# Set list items
i = 0
for v,ch in value:
@@ -618,9 +608,7 @@ class ParamContent(PPanel):
sizer.Add(self.text, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.button = wx.Button(self, self.ID_BUTTON_EDIT, 'Edit...', size=buttonSize)
sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL)
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
self.textModified = False
wx.EVT_BUTTON(self, self.ID_BUTTON_EDIT, self.OnButtonEdit)
wx.EVT_TEXT(self, self.ID_TEXT_CTRL, self.OnChange)
@@ -631,18 +619,14 @@ class ParamContent(PPanel):
if self.textModified: # text has newer value
try:
return self.text.GetValue().split('|')
- except SyntaxError:
- wx.LogError('Syntax error in parameter value: ' + self.GetName())
+ except ValueError:
return []
return self.value
def SetValue(self, value):
self.freeze = True
if not value: value = []
self.value = value
- if value:
- repr_ = reduce(lambda a,b: '%s|%s' % (a,b), value)
- else:
- repr_ = ''
+ repr_ = '|'.join(map(str, value))
self.text.SetValue(repr_) # update text ctrl
self.freeze = False
def OnButtonEdit(self, evt):
@@ -657,6 +641,9 @@ class ParamContent(PPanel):
self.SetModified()
self.textModified = False
dlg.Destroy()
+ def SetModified(self, state=True):
+ PPanel.SetModified(self, state)
+ self.textModified = False
# CheckList content
class ParamContentCheckList(ParamContent):
@@ -674,6 +661,13 @@ class ParamContentCheckList(ParamContent):
self.SetModified()
self.textModified = False
dlg.Destroy()
+ def SetValue(self, value):
+ self.freeze = True
+ if not value: value = []
+ self.value = value
+ repr_ = '|'.join(map(str,value))
+ self.text.SetValue(repr_) # update text ctrl
+ self.freeze = False
class IntListDialog(wx.Dialog):
def __init__(self, parent, value):
@@ -729,9 +723,8 @@ class ParamIntList(ParamContent):
def OnButtonEdit(self, evt):
if self.textModified: # text has newer value
try:
- self.value = eval(self.text.GetValue())
- except SyntaxError:
- wx.LogError('Syntax error in parameter value: ' + self.GetName())
+ self.value = map(int, self.text.GetValue().split('|'))
+ except ValueError:
self.value = []
dlg = IntListDialog(self, self.value)
if dlg.ShowModal() == wx.ID_OK:
@@ -754,9 +747,7 @@ class RadioBox(PPanel):
button = wx.RadioButton(self, -1, i, size=(-1,buttonSize[1]), name=i)
topSizer.Add(button, 0, wx.RIGHT, 5)
wx.EVT_RADIOBUTTON(self, button.GetId(), self.OnRadioChoice)
- self.SetAutoLayout(True)
self.SetSizer(topSizer)
- topSizer.Fit(self)
def SetStringSelection(self, value):
self.freeze = True
for i in self.choices:
@@ -814,9 +805,7 @@ class ParamFile(PPanel):
sizer.Add(self.text, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.button = wx.Button(self, self.ID_BUTTON_BROWSE, 'Browse...',size=buttonSize)
sizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL)
- self.SetAutoLayout(True)
self.SetSizer(sizer)
- sizer.Fit(self)
self.textModified = False
wx.EVT_BUTTON(self, self.ID_BUTTON_BROWSE, self.OnButtonBrowse)
wx.EVT_TEXT(self, self.ID_TEXT_CTRL, self.OnChange)
@@ -943,5 +932,6 @@ paramDict = {
'fg': ParamColour, 'bg': ParamColour, 'font': ParamFont,
'enabled': ParamBool, 'focused': ParamBool, 'hidden': ParamBool,
'tooltip': ParamText, 'bitmap': ParamBitmap, 'icon': ParamBitmap,
- 'encoding': ParamEncoding, 'borders': ParamUnit
+ 'encoding': ParamEncoding, 'borders': ParamUnit,
+ 'comment': ParamComment
}
diff --git a/wxPython/wx/tools/XRCed/src-images/ToolMoveDown.png b/wxPython/wx/tools/XRCed/src-images/ToolMoveDown.png
new file mode 100644
index 0000000000..f0dc4bc303
Binary files /dev/null and b/wxPython/wx/tools/XRCed/src-images/ToolMoveDown.png differ
diff --git a/wxPython/wx/tools/XRCed/src-images/ToolMoveLeft.png b/wxPython/wx/tools/XRCed/src-images/ToolMoveLeft.png
new file mode 100644
index 0000000000..94b32fd672
Binary files /dev/null and b/wxPython/wx/tools/XRCed/src-images/ToolMoveLeft.png differ
diff --git a/wxPython/wx/tools/XRCed/src-images/ToolMoveRight.png b/wxPython/wx/tools/XRCed/src-images/ToolMoveRight.png
new file mode 100644
index 0000000000..05f4dd4bc0
Binary files /dev/null and b/wxPython/wx/tools/XRCed/src-images/ToolMoveRight.png differ
diff --git a/wxPython/wx/tools/XRCed/src-images/ToolMoveUp.png b/wxPython/wx/tools/XRCed/src-images/ToolMoveUp.png
new file mode 100644
index 0000000000..01d2883606
Binary files /dev/null and b/wxPython/wx/tools/XRCed/src-images/ToolMoveUp.png differ
diff --git a/wxPython/wx/tools/XRCed/src-images/TreeComment.png b/wxPython/wx/tools/XRCed/src-images/TreeComment.png
new file mode 100644
index 0000000000..c5aa5751fa
Binary files /dev/null and b/wxPython/wx/tools/XRCed/src-images/TreeComment.png differ
diff --git a/wxPython/wx/tools/XRCed/tools.py b/wxPython/wx/tools/XRCed/tools.py
index e0e3ac86d2..45687f850a 100644
--- a/wxPython/wx/tools/XRCed/tools.py
+++ b/wxPython/wx/tools/XRCed/tools.py
@@ -87,11 +87,11 @@ class Tools(wx.Panel):
(ID_NEW.UNKNOWN, images.getToolUnknownBitmap())]
]
+ self.boxes = {}
for grp in groups:
self.AddGroup(grp[0])
for b in grp[1:]:
self.AddButton(b[0], b[1], g.pullDownMenu.createMap[b[0]])
- self.SetAutoLayout(True)
self.SetSizerAndFit(self.sizer)
# Allow to be resized in vertical direction only
self.SetSizeHints(self.GetSize()[0], -1)
@@ -114,13 +114,19 @@ class Tools(wx.Panel):
def AddGroup(self, name):
# Each group is inside box
- box = wx.StaticBox(self, -1, name, style=wx.WANTS_CHARS)
+ id = wx.NewId()
+ box = wx.StaticBox(self, id, '[+] '+name, style=wx.WANTS_CHARS)
+ box.show = True
+ box.name = name
+ box.gnum = len(self.groups)
box.SetFont(g.smallerFont())
+ box.Bind(wx.EVT_LEFT_DOWN, self.OnClickBox)
boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
boxSizer.Add((0, 4))
+ self.boxes[id] = box
self.curSizer = wx.GridSizer(0, 3)
boxSizer.Add(self.curSizer)
- self.sizer.Add(boxSizer, 0, wx.TOP | wx.LEFT | wx.RIGHT, 4)
+ self.sizer.Add(boxSizer, 0, wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, 4)
self.groups.append((box,{}))
# Enable/disable group
@@ -129,6 +135,12 @@ class Tools(wx.Panel):
grp[0].Enable(enable)
for b in grp[1].values(): b.Enable(enable)
+ # Show/hide group
+ def ShowGroup(self, gnum, show = True):
+ grp = self.groups[gnum]
+ grp[0].show = show
+ for b in grp[1].values(): b.Show(show)
+
# Enable/disable group item
def EnableGroupItem(self, gnum, id, enable = True):
grp = self.groups[gnum]
@@ -140,6 +152,14 @@ class Tools(wx.Panel):
for id in ids:
grp[1][id].Enable(enable)
+ def OnClickBox(self, evt):
+ box = self.boxes[evt.GetId()]
+ # Collapse/restore static box, change label
+ self.ShowGroup(box.gnum, not box.show)
+ if box.show: box.SetLabel('[+] ' + box.name)
+ else: box.SetLabel('[-] ' + box.name)
+ self.Layout()
+
# Process key events
def OnKeyDown(self, evt):
if evt.GetKeyCode() == wx.WXK_CONTROL:
diff --git a/wxPython/wx/tools/XRCed/tree.py b/wxPython/wx/tools/XRCed/tree.py
index 26e8b91a8a..f384b15da5 100644
--- a/wxPython/wx/tools/XRCed/tree.py
+++ b/wxPython/wx/tools/XRCed/tree.py
@@ -86,10 +86,12 @@ class ID_NEW:
CHOICEBOOK = wx.NewId()
LISTBOOK = wx.NewId()
SPLITTER_WINDOW = wx.NewId()
+ GRID = wx.NewId()
SCROLLED_WINDOW = wx.NewId()
HTML_WINDOW = wx.NewId()
CALENDAR_CTRL = wx.NewId()
DATE_CTRL = wx.NewId()
+ FILE_PICKER_CTRL = wx.NewId()
GENERIC_DIR_CTRL = wx.NewId()
SPIN_CTRL = wx.NewId()
UNKNOWN = wx.NewId()
@@ -124,6 +126,10 @@ class ID_NEW:
CONTEXT_HELP_BUTTON = wx.NewId()
REF = wx.NewId()
+ COMMENT = wx.NewId()
+
+ CUSTOM = wx.NewId()
+ for i in range(99): wx.NewId() # reserve IDs for custom controls
LAST = wx.NewId()
@@ -193,10 +199,12 @@ class PullDownMenu:
ID_NEW.CHOICEBOOK: 'wxChoicebook',
ID_NEW.LISTBOOK: 'wxListbook',
ID_NEW.SPLITTER_WINDOW: 'wxSplitterWindow',
+ ID_NEW.GRID: 'wxGrid',
ID_NEW.SCROLLED_WINDOW: 'wxScrolledWindow',
ID_NEW.HTML_WINDOW: 'wxHtmlWindow',
ID_NEW.CALENDAR_CTRL: 'wxCalendarCtrl',
ID_NEW.DATE_CTRL: 'wxDatePickerCtrl',
+ ID_NEW.FILE_PICKER_CTRL: 'wxFilePickerCtrl',
ID_NEW.GENERIC_DIR_CTRL: 'wxGenericDirCtrl',
ID_NEW.SPIN_CTRL: 'wxSpinCtrl',
@@ -268,10 +276,12 @@ class PullDownMenu:
(ID_NEW.SCROLL_BAR, 'ScrollBar', 'Create scroll bar'),
(ID_NEW.TREE_CTRL, 'TreeCtrl', 'Create tree'),
(ID_NEW.LIST_CTRL, 'ListCtrl', 'Create list'),
+# (ID_NEW.GRID, 'Grid', 'Create grid'),
(ID_NEW.SCROLLED_WINDOW, 'ScrolledWindow', 'Create scrolled window'),
(ID_NEW.HTML_WINDOW, 'HtmlWindow', 'Create HTML window'),
(ID_NEW.CALENDAR_CTRL, 'CalendarCtrl', 'Create calendar control'),
(ID_NEW.DATE_CTRL, 'DatePickerCtrl', 'Create date picker control'),
+# (ID_NEW.FILE_PICKER_CTRL, 'FilePickerCtrl', 'Create file picker control'),
(ID_NEW.GENERIC_DIR_CTRL, 'GenericDirCtrl', 'Create generic dir control'),
(ID_NEW.UNKNOWN, 'Unknown', 'Create custom control placeholder'),
],
@@ -370,7 +380,13 @@ class PullDownMenu:
ID_NEW.HELP_BUTTON: ('wxID_HELP', '&Help'),
ID_NEW.CONTEXT_HELP_BUTTON: ('wxID_CONTEXT_HELP', '&Help'),
}
-
+ self.custom = ['custom', 'User-defined controls']
+ self.customMap = {}
+
+ def addCustom(self, klass):
+ n = len(self.custom)-2
+ self.custom.append((ID_NEW.CUSTOM + n, klass))
+ self.customMap[ID_NEW.CUSTOM + n] = klass
################################################################################
@@ -409,6 +425,7 @@ class HighLightBox:
l4 = wx.Window(w, -1, wx.Point(pos.x, pos.y + size.height - 2), wx.Size(size.width, 2))
l4.SetBackgroundColour(wx.RED)
self.lines = [l1, l2, l3, l4]
+ self.size = size
# Move highlight to a new position
def Replace(self, pos, size):
if size.width == -1: size.width = 0
@@ -417,6 +434,7 @@ class HighLightBox:
self.lines[1].SetDimensions(pos.x, pos.y, 2, size.height)
self.lines[2].SetDimensions(pos.x + size.width - 2, pos.y, 2, size.height)
self.lines[3].SetDimensions(pos.x, pos.y + size.height - 2, size.width, 2)
+ self.size = size
# Remove it
def Remove(self):
map(wx.Window.Destroy, self.lines)
@@ -428,8 +446,12 @@ class HighLightBox:
class XML_Tree(wx.TreeCtrl):
def __init__(self, parent, id):
- wx.TreeCtrl.__init__(self, parent, id, style = wx.TR_HAS_BUTTONS | wx.TR_MULTIPLE)
+ wx.TreeCtrl.__init__(self, parent, id,
+ style = wx.TR_HAS_BUTTONS | wx.TR_MULTIPLE | wx.TR_EDIT_LABELS)
self.SetBackgroundColour(wx.Colour(224, 248, 224))
+ self.fontComment = wx.FFont(self.GetFont().GetPointSize(),
+ self.GetFont().GetFamily(),
+ wx.FONTFLAG_ITALIC)
# Register events
wx.EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
# One works on Linux, another on Windows
@@ -440,6 +462,8 @@ class XML_Tree(wx.TreeCtrl):
wx.EVT_RIGHT_DOWN(self, self.OnRightDown)
wx.EVT_TREE_ITEM_EXPANDED(self, self.GetId(), self.OnItemExpandedCollapsed)
wx.EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemExpandedCollapsed)
+ self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginLabelEdit)
+ self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndLabelEdit)
self.selection = None
self.selectionChanging = False
@@ -450,6 +474,7 @@ class XML_Tree(wx.TreeCtrl):
# Create image list
il = wx.ImageList(16, 16, True)
self.rootImage = il.Add(images.getTreeRootImage().Scale(16,16).ConvertToBitmap())
+ xxxComment.image = il.Add(images.getTreeCommentImage().Scale(16,16).ConvertToBitmap())
xxxObject.image = il.Add(images.getTreeDefaultImage().Scale(16,16).ConvertToBitmap())
xxxPanel.image = il.Add(images.getTreePanelImage().Scale(16,16).ConvertToBitmap())
xxxDialog.image = il.Add(images.getTreeDialogImage().Scale(16,16).ConvertToBitmap())
@@ -560,17 +585,22 @@ class XML_Tree(wx.TreeCtrl):
item = self.AppendItem(itemParent, treeObj.treeName(),
image=treeObj.treeImage(),
data=wx.TreeItemData(xxx))
- # Different color for references
- if treeObj.ref:
+ # Different color for comments and references
+ if xxx.className == 'comment':
+ self.SetItemTextColour(item, 'Blue')
+ self.SetItemFont(item, self.fontComment)
+ elif treeObj.ref:
self.SetItemTextColour(item, 'DarkGreen')
+ elif treeObj.hasStyle and treeObj.params.get('hidden', False):
+ self.SetItemTextColour(item, 'Grey')
# Try to find children objects
if treeObj.hasChildren:
- nodes = treeObj.element.childNodes[:]
+ nodes = treeObj.node.childNodes[:]
for n in nodes:
if IsObject(n):
self.AddNode(item, treeObj, n)
elif n.nodeType != minidom.Node.ELEMENT_NODE:
- treeObj.element.removeChild(n)
+ treeObj.node.removeChild(n)
n.unlink()
# Insert new item at specific position
@@ -579,23 +609,30 @@ class XML_Tree(wx.TreeCtrl):
xxx = MakeXXXFromDOM(parent, elem)
# If nextItem is None, we append to parent, otherwise insert before it
if nextItem.IsOk():
- node = self.GetPyData(nextItem).element
- parent.element.insertBefore(elem, node)
+ node = self.GetPyData(nextItem).node
+ parent.node.insertBefore(elem, node)
# Inserting before is difficult, se we insert after or first child
index = self.ItemIndex(nextItem)
newItem = self.InsertItemBefore(itemParent, index,
xxx.treeName(), image=xxx.treeImage())
self.SetPyData(newItem, xxx)
else:
- parent.element.appendChild(elem)
+ parent.node.appendChild(elem)
newItem = self.AppendItem(itemParent, xxx.treeName(), image=xxx.treeImage(),
data=wx.TreeItemData(xxx))
- # Different color for references
- if xxx.treeObject().ref: self.SetItemTextColour(newItem, 'DarkGreen')
+ treeObj = xxx.treeObject()
+ # Different color for references and comments
+ if xxx.className == 'comment':
+ self.SetItemTextColour(newItem, 'Blue')
+ self.SetItemFont(newItem, self.fontComment)
+ elif treeObj.ref:
+ self.SetItemTextColour(newItem, 'DarkGreen')
+ elif treeObj.hasStyle and treeObj.params.get('hidden', False):
+ self.SetItemTextColour(newItem, 'Grey')
# Add children items
if xxx.hasChildren:
treeObj = xxx.treeObject()
- for n in treeObj.element.childNodes:
+ for n in treeObj.node.childNodes:
if IsObject(n):
self.AddNode(newItem, treeObj, n)
return newItem
@@ -603,13 +640,14 @@ class XML_Tree(wx.TreeCtrl):
# Remove leaf of tree, return it's data object
def RemoveLeaf(self, leaf):
xxx = self.GetPyData(leaf)
- node = xxx.element
+ node = xxx.node
parent = node.parentNode
parent.removeChild(node)
self.Delete(leaf)
# Reset selection object
self.selection = None
return node
+
# Find position relative to the top-level window
def FindNodePos(self, item, obj=None):
# Root at (0,0)
@@ -629,17 +667,18 @@ class XML_Tree(wx.TreeCtrl):
if g.testWin.highLight:
g.testWin.highLight.Remove()
break
- # Find first ancestor which is a wxWindow (not a sizer)
+ # For sizers and notebooks we must select the first window-like parent
winParent = itemParent
while self.GetPyData(winParent).isSizer:
winParent = self.GetItemParent(winParent)
# Notebook children are layed out in a little strange way
- if self.GetPyData(itemParent).treeObject().__class__ == xxxNotebook:
- parentPos = wx.Point(0,0)
- else:
- parentPos = self.FindNodePos(winParent)
- # Position (-1,-1) is really (0,0)
+ # wxGTK places NB panels relative to the NB parent
+ if wx.Platform == '__WXGTK__':
+ if self.GetPyData(itemParent).treeObject().__class__ == xxxNotebook:
+ winParent = self.GetItemParent(winParent)
+ parentPos = self.FindNodePos(winParent)
pos = obj.GetPosition()
+ # Position (-1,-1) is really (0,0)
if pos == (-1,-1): pos = (0,0)
return parentPos + pos
@@ -698,7 +737,6 @@ class XML_Tree(wx.TreeCtrl):
# If some data was modified, apply changes
if g.panel.IsModified():
self.Apply(xxx, oldItem)
- #if conf.autoRefresh:
if g.testWin:
if g.testWin.highLight:
g.testWin.highLight.Remove()
@@ -723,7 +761,8 @@ class XML_Tree(wx.TreeCtrl):
if item == g.testWin.item: return False
while item != self.root:
item = self.GetItemParent(item)
- if item == g.testWin.item: return True
+ if item == g.testWin.item:
+ return True
return False
# Highlight selected item
@@ -739,19 +778,27 @@ class XML_Tree(wx.TreeCtrl):
return
# Get window/sizer object
obj = self.FindNodeObject(item)
- if not obj:
+ xxx = self.GetPyData(item).treeObject()
+ # Remove existing HL if item not found or is hidden
+ if not obj or xxx.hasStyle and xxx.params.get('hidden', False):
if g.testWin.highLight: g.testWin.highLight.Remove()
return
- pos = self.FindNodePos(item, obj)
+ pos = self.FindNodePos(item, obj)
size = obj.GetSize()
# Highlight
# Negative positions are not working quite well
- if g.testWin.highLight:
- g.testWin.highLight.Replace(pos, size)
+ hl = g.testWin.highLight
+ # If highlight object has the same size SetDimension does not repaint it
+ # so we must remove the old HL window
+ if hl and hl.size == size:
+ hl.Remove()
+ hl = None
+ if hl:
+ hl.Replace(pos, size)
else:
- g.testWin.highLight = HighLightBox(pos, size)
- g.testWin.highLight.Refresh()
- g.testWin.highLight.item = item
+ g.testWin.highLight = hl = HighLightBox(pos, size)
+ hl.Refresh()
+ hl.item = item
def ShowTestWindow(self, item):
xxx = self.GetPyData(item)
@@ -769,6 +816,7 @@ class XML_Tree(wx.TreeCtrl):
return
# Show item in bold
if g.testWin: # Reset old
+ self.UnselectAll()
self.SetItemBold(g.testWin.item, False)
try:
wx.BeginBusyCursor()
@@ -804,19 +852,6 @@ class XML_Tree(wx.TreeCtrl):
testWin = g.testWin
# Create a window with this resource
xxx = self.GetPyData(item).treeObject()
-
- # If frame
-# if xxx.__class__ == xxxFrame:
- # Frame can't have many children,
- # but it's first child possibly can...
-# child = self.GetFirstChild(item)[0]
-# if child.IsOk() and self.GetPyData(child).__class__ == xxxPanel:
-# # Clean-up before recursive call or error
-# wx.MemoryFSHandler.RemoveFile('xxx.xrc')
-# wx.EndBusyCursor()
-# self.CreateTestWin(child)
-# return
-
# Close old window, remember where it was
highLight = None
if testWin:
@@ -842,7 +877,7 @@ class XML_Tree(wx.TreeCtrl):
# Save in memory FS
memFile = MemoryFile('xxx.xrc')
# Create memory XML file
- elem = xxx.element.cloneNode(True)
+ elem = xxx.node.cloneNode(True)
if not xxx.name:
name = 'noname'
else:
@@ -871,6 +906,10 @@ class XML_Tree(wx.TreeCtrl):
if not g.currentEncoding:
xmlFlags != xrc.XRC_USE_LOCALE
res = xrc.XmlResource('', xmlFlags)
+ xrc.XmlResource.Set(res) # set as global
+ # Register handlers
+ addHandlers()
+ # Same module list
res.Load('memory:xxx.xrc')
try:
if xxx.__class__ == xxxFrame:
@@ -901,7 +940,7 @@ class XML_Tree(wx.TreeCtrl):
testWin.SetClientSize(testWin.GetBestSize())
testWin.Show(True)
elif xxx.__class__ == xxxDialog:
- testWin = g.testWin = res.LoadDialog(None, STD_NAME)
+ testWin = g.testWin = res.LoadDialog(g.frame, STD_NAME)
testWin.panel = testWin
testWin.Layout()
testWin.SetPosition(pos)
@@ -911,7 +950,7 @@ class XML_Tree(wx.TreeCtrl):
wx.EVT_BUTTON(testWin, wx.ID_CANCEL, self.OnCloseTestWin)
elif xxx.__class__ == xxxWizard:
wiz = wx.wizard.PreWizard()
- res.LoadOnObject(wiz, None, STD_NAME, 'wxWizard')
+ res.LoadOnObject(wiz, g.frame, STD_NAME, 'wxWizard')
# Find first page (don't know better way)
firstPage = None
for w in wiz.GetChildren():
@@ -953,9 +992,11 @@ class XML_Tree(wx.TreeCtrl):
testWin.toolBar = res.LoadToolBar(testWin, STD_NAME)
testWin.SetToolBar(testWin.toolBar)
testWin.Show(True)
+ # Catch some events, set highlight
if testWin:
testWin.item = item
wx.EVT_CLOSE(testWin, self.OnCloseTestWin)
+ wx.EVT_SIZE(testWin, self.OnSizeTestWin)
testWin.highLight = None
if highLight and not self.pendingHighLight:
self.HighLight(highLight)
@@ -968,6 +1009,9 @@ class XML_Tree(wx.TreeCtrl):
inf = sys.exc_info()
wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1])
wx.LogError('Error loading resource')
+ # Cleanup
+ res.Unload('xxx.xrc')
+ xrc.XmlResource.Set(None)
wx.MemoryFSHandler.RemoveFile('xxx.xrc')
def CloseTestWindow(self):
@@ -981,6 +1025,11 @@ class XML_Tree(wx.TreeCtrl):
def OnCloseTestWin(self, evt):
self.CloseTestWindow()
+ def OnSizeTestWin(self, evt):
+ if g.testWin.highLight:
+ self.HighLight(g.testWin.highLight.item)
+ evt.Skip()
+
# Return index in parent, for real window children
def WindowIndex(self, item):
n = 0 # index of sibling
@@ -1062,6 +1111,7 @@ class XML_Tree(wx.TreeCtrl):
SetMenu(m, pullDownMenu.topLevel)
m.AppendSeparator()
m.Append(ID_NEW.REF, 'reference...', 'Create object_ref node')
+ m.Append(ID_NEW.COMMENT, 'comment', 'Create comment node')
else:
xxx = self.GetPyData(item).treeObject()
# Check parent for possible child nodes if inserting sibling
@@ -1083,8 +1133,12 @@ class XML_Tree(wx.TreeCtrl):
m.Enable(ID_NEW.SPACER, False)
if xxx.__class__ is not xxxFrame:
m.Enable(ID_NEW.MENU_BAR, False)
+ # Add custom controls menu
+ if len(pullDownMenu.custom) > 2:
+ SetMenu(m, [pullDownMenu.custom])
m.AppendSeparator()
m.Append(ID_NEW.REF, 'reference...', 'Create object_ref node')
+ m.Append(ID_NEW.COMMENT, 'comment', 'Create comment node')
# Select correct label for create menu
if not needInsert:
if self.shift:
@@ -1116,15 +1170,16 @@ class XML_Tree(wx.TreeCtrl):
(ID_NEW.PANEL, 'Panel', 'Create panel'),
(ID_NEW.DIALOG, 'Dialog', 'Create dialog'),
(ID_NEW.FRAME, 'Frame', 'Create frame')], shift=True)
- elif xxx.isSizer:
+ elif xxx.isSizer and self.ItemHasChildren(item):
SetMenu(m, pullDownMenu.sizers, shift=True)
else:
SetMenu(m, pullDownMenu.controls, shift=True)
- id = wx.NewId()
- menu.AppendMenu(id, 'Replace With', m)
- if not m.GetMenuItemCount(): menu.Enable(id, False)
- menu.Append(pullDownMenu.ID_SUBCLASS, 'Subclass...',
- 'Set "subclass" property')
+ if xxx.isElement:
+ id = wx.NewId()
+ menu.AppendMenu(id, 'Replace With', m)
+ if not m.GetMenuItemCount(): menu.Enable(id, False)
+ menu.Append(pullDownMenu.ID_SUBCLASS, 'Subclass...',
+ 'Set "subclass" property')
menu.AppendSeparator()
# Not using standart IDs because we don't want to show shortcuts
menu.Append(wx.ID_CUT, 'Cut', 'Cut to the clipboard')
@@ -1143,6 +1198,12 @@ class XML_Tree(wx.TreeCtrl):
self.PopupMenu(menu, evt.GetPosition())
menu.Destroy()
+ # Redefine to force the update of font dimentions on wxGTK
+ if wx.Platform == '__WXGTK__':
+ def SetItemBold(self, item, state=True):
+ wx.TreeCtrl.SetItemBold(self, item, state)
+ self.SetIndent(self.GetIndent())
+
# Apply changes
def Apply(self, xxx, item):
g.panel.Apply()
@@ -1153,9 +1214,25 @@ class XML_Tree(wx.TreeCtrl):
# Item width may have changed
# !!! Tric to update tree width (wxGTK, ??)
self.SetIndent(self.GetIndent())
+ elif xxx.className == 'comment':
+ self.SetItemText(item, xxx.treeName())
# Change tree icon for sizers
if isinstance(xxx, xxxBoxSizer):
self.SetItemImage(item, xxx.treeImage())
# Set global modified state
g.frame.SetModified()
+ def OnBeginLabelEdit(self, evt):
+ xxx = self.GetPyData(evt.GetItem())
+ if xxx.isElement:
+ evt.Veto()
+ else:
+ evt.Skip()
+
+ def OnEndLabelEdit(self, evt):
+ xxx = self.GetPyData(evt.GetItem())
+ node = xxx.node
+ if not xxx.isElement:
+ node.data = evt.GetLabel()
+ g.panel.SetData(xxx)
+ evt.Skip()
diff --git a/wxPython/wx/tools/XRCed/undo.py b/wxPython/wx/tools/XRCed/undo.py
index fe0c181739..4b28a76c38 100644
--- a/wxPython/wx/tools/XRCed/undo.py
+++ b/wxPython/wx/tools/XRCed/undo.py
@@ -6,6 +6,7 @@
from globals import *
from xxx import MakeXXXFromDOM
+from tree import *
#from panel import *
# Undo/redo classes
@@ -123,20 +124,21 @@ class UndoPasteCreate:
class UndoReplace:
def __init__(self, item):
self.itemIndex = g.tree.ItemFullIndex(item)
- self.xxx = g.tree.GetPyData(item)
+ #self.xxx = g.tree.GetPyData(item)
+ self.elem = None
def destroy(self):
- if self.xxx: self.xxx.element.unlink()
+ if self.elem: self.elem.unlink()
def undo(self):
print 'Sorry, UndoReplace is not yet implemented.'
return
item = g.tree.ItemAtFullIndex(self.itemIndex)
xxx = g.tree.GetPyData(item)
# Replace with old element
- parent = xxx.parent.element
+ parent = xxx.parent.node
if xxx is self.xxx: # sizeritem or notebookpage - replace child
- parent.replaceChild(self.xxx.child.element, xxx.child.element)
+ parent.replaceChild(self.xxx.child.node, xxx.child.node)
else:
- parent.replaceChild(self.xxx.element, xxx.element)
+ parent.replaceChild(self.xxx.node, xxx.node)
self.xxx.parent = xxx.parent
xxx = self.xxx
g.tree.SetPyData(item, xxx)
@@ -163,6 +165,91 @@ class UndoReplace:
def redo(self):
return
+class UndoMove:
+ def __init__(self, oldParent, oldIndex, newParent, newIndex):
+ self.oldParent = oldParent
+ self.oldIndex = oldIndex
+ self.newParent = newParent
+ self.newIndex = newIndex
+ def destroy(self):
+ pass
+ def undo(self):
+ item = g.tree.GetFirstChild(self.newParent)[0]
+ for i in range(self.newIndex): item = g.tree.GetNextSibling(item)
+ elem = g.tree.RemoveLeaf(item)
+ nextItem = g.tree.GetFirstChild(self.oldParent)[0]
+ for i in range(self.oldIndex): nextItem = g.tree.GetNextSibling(nextItem)
+
+ parent = g.tree.GetPyData(self.oldParent).treeObject()
+
+ # 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.
+ xxx = MakeXXXFromDOM(parent, elem)
+ isChildContainer = isinstance(xxx, xxxChildContainer)
+ if isChildContainer and \
+ ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
+ (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
+ not (parent.isSizer or isinstance(parent, xxxNotebook))):
+ elem.removeChild(xxx.child.node) # detach child
+ elem.unlink() # delete child container
+ elem = xxx.child.node # 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
+
+ selected = g.tree.InsertNode(self.oldParent, parent, elem, nextItem)
+ g.tree.EnsureVisible(selected)
+ g.tree.SelectItem(selected)
+ def redo(self):
+ item = g.tree.GetFirstChild(self.oldParent)[0]
+ for i in range(self.oldIndex): item = g.tree.GetNextSibling(item)
+ elem = g.tree.RemoveLeaf(item)
+
+ parent = g.tree.GetPyData(self.newParent).treeObject()
+
+ # 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.
+ xxx = MakeXXXFromDOM(parent, elem)
+ isChildContainer = isinstance(xxx, xxxChildContainer)
+ if isChildContainer and \
+ ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \
+ (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \
+ not (parent.isSizer or isinstance(parent, xxxNotebook))):
+ elem.removeChild(xxx.child.node) # detach child
+ elem.unlink() # delete child container
+ elem = xxx.child.node # 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
+
+ nextItem = g.tree.GetFirstChild(self.newParent)[0]
+ for i in range(self.newIndex): nextItem = g.tree.GetNextSibling(nextItem)
+ selected = g.tree.InsertNode(self.newParent, parent, elem, nextItem)
+ g.tree.EnsureVisible(selected)
+ g.tree.SelectItem(selected)
+
class UndoEdit:
def __init__(self):
self.pages = map(ParamPage.GetState, g.panel.pages)
diff --git a/wxPython/wx/tools/XRCed/xrced.py b/wxPython/wx/tools/XRCed/xrced.py
index e47b62f287..f4eaee78ab 100644
--- a/wxPython/wx/tools/XRCed/xrced.py
+++ b/wxPython/wx/tools/XRCed/xrced.py
@@ -28,6 +28,7 @@ from xml.parsers import expat
from tree import * # imports xxx which imports params
from panel import *
from tools import *
+from params import genericStyles
# Cleanup recursive import sideeffects, otherwise we can't create undoMan
import undo
undo.ParamPage = ParamPage
@@ -39,9 +40,14 @@ if __name__ == '__main__':
else:
basePath = os.path.dirname(__file__)
+# Remember system path
+sys_path = sys.path
+
# 1 adds CMD command to Help menu
debug = 0
+import xxx
+
g.helpText = """\
Welcome to XRCed
DON'T PANIC :)
Read this note before clicking on anything!
@@ -50,7 +56,7 @@ select "Append Child", and then any command.
Or just press one of the buttons on the tools palette.
Enter XML ID, change properties, create children.
To test your interface select Test command (View menu).
-Consult README file for the details.
+Consult README.txt file for the details.