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.

Fixes for wxMSW (notebook highlighting, control sizes, tree Unselect).

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).

Fix for dealing with empty 'growable' property, using MiniFrame
for properties panel, the panel is restored together with the
main window.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_8_BRANCH@44949 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2007-03-19 17:55:26 +00:00
parent 459fbbc100
commit 30d91f9ebc
11 changed files with 989 additions and 376 deletions

View File

@@ -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,19 +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
@@ -581,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
@@ -605,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)
@@ -631,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
@@ -700,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()
@@ -747,16 +783,22 @@ class XML_Tree(wx.TreeCtrl):
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)
@@ -774,7 +816,7 @@ class XML_Tree(wx.TreeCtrl):
return
# Show item in bold
if g.testWin: # Reset old
self.Unselect()
self.UnselectAll()
self.SetItemBold(g.testWin.item, False)
try:
wx.BeginBusyCursor()
@@ -810,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:
@@ -848,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:
@@ -877,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:
@@ -907,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)
@@ -917,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():
@@ -959,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)
@@ -974,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):
@@ -987,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
@@ -1068,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
@@ -1089,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:
@@ -1126,11 +1174,12 @@ class XML_Tree(wx.TreeCtrl):
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')
@@ -1165,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()