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
283 lines
11 KiB
Python
283 lines
11 KiB
Python
# Name: undo.py
|
|
# Purpose: XRC editor, undo/redo module
|
|
# Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be>
|
|
# Created: 01.12.2002
|
|
# RCS-ID: $Id$
|
|
|
|
from globals import *
|
|
from xxx import MakeXXXFromDOM
|
|
from tree import *
|
|
#from panel import *
|
|
|
|
# Undo/redo classes
|
|
class UndoManager:
|
|
# Undo/redo stacks
|
|
undo = []
|
|
redo = []
|
|
def RegisterUndo(self, undoObj):
|
|
self.undo.append(undoObj)
|
|
for i in self.redo: i.destroy()
|
|
self.redo = []
|
|
def Undo(self):
|
|
undoObj = self.undo.pop()
|
|
undoObj.undo()
|
|
self.redo.append(undoObj)
|
|
g.frame.SetModified()
|
|
g.frame.SetStatusText('Undone')
|
|
def Redo(self):
|
|
undoObj = self.redo.pop()
|
|
undoObj.redo()
|
|
self.undo.append(undoObj)
|
|
g.frame.SetModified()
|
|
g.frame.SetStatusText('Redone')
|
|
def Clear(self):
|
|
for i in self.undo: i.destroy()
|
|
self.undo = []
|
|
for i in self.redo: i.destroy()
|
|
self.redo = []
|
|
def CanUndo(self):
|
|
return not not self.undo
|
|
def CanRedo(self):
|
|
return not not self.redo
|
|
|
|
class UndoCutDelete:
|
|
def __init__(self, itemIndex, parent, elem):
|
|
self.itemIndex = itemIndex
|
|
self.parent = parent
|
|
self.elem = elem
|
|
def destroy(self):
|
|
if self.elem: self.elem.unlink()
|
|
def undo(self):
|
|
item = g.tree.InsertNode(g.tree.ItemAtFullIndex(self.itemIndex[:-1]),
|
|
self.parent, self.elem,
|
|
g.tree.ItemAtFullIndex(self.itemIndex))
|
|
# Scroll to show new item (!!! redundant?)
|
|
g.tree.EnsureVisible(item)
|
|
g.tree.SelectItem(item)
|
|
self.elem = None
|
|
# Update testWin if needed
|
|
if g.testWin and g.tree.IsHighlatable(item):
|
|
if g.conf.autoRefresh:
|
|
g.tree.needUpdate = True
|
|
g.tree.pendingHighLight = item
|
|
else:
|
|
g.tree.pendingHighLight = None
|
|
def redo(self):
|
|
item = g.tree.ItemAtFullIndex(self.itemIndex)
|
|
# Delete testWin?
|
|
if g.testWin:
|
|
# If deleting top-level item, delete testWin
|
|
if item == g.testWin.item:
|
|
g.testWin.Destroy()
|
|
g.testWin = None
|
|
else:
|
|
# Remove highlight, update testWin
|
|
if g.testWin.highLight:
|
|
g.testWin.highLight.Remove()
|
|
g.tree.needUpdate = True
|
|
self.elem = g.tree.RemoveLeaf(item)
|
|
g.tree.UnselectAll()
|
|
g.panel.Clear()
|
|
|
|
class UndoPasteCreate:
|
|
def __init__(self, itemParent, parent, item, selected):
|
|
self.itemParentIndex = g.tree.ItemFullIndex(itemParent)
|
|
self.parent = parent
|
|
self.itemIndex = g.tree.ItemFullIndex(item) # pasted item
|
|
self.selectedIndex = g.tree.ItemFullIndex(selected) # maybe different from item
|
|
self.elem = None
|
|
def destroy(self):
|
|
if self.elem: self.elem.unlink()
|
|
def undo(self):
|
|
self.elem = g.tree.RemoveLeaf(g.tree.ItemAtFullIndex(self.itemIndex))
|
|
# Restore old selection
|
|
selected = g.tree.ItemAtFullIndex(self.selectedIndex)
|
|
g.tree.EnsureVisible(selected)
|
|
g.tree.SelectItem(selected)
|
|
# Delete testWin?
|
|
if g.testWin:
|
|
# If deleting top-level item, delete testWin
|
|
if selected == g.testWin.item:
|
|
g.testWin.Destroy()
|
|
g.testWin = None
|
|
else:
|
|
# Remove highlight, update testWin
|
|
if g.testWin.highLight:
|
|
g.testWin.highLight.Remove()
|
|
g.tree.needUpdate = True
|
|
def redo(self):
|
|
item = g.tree.InsertNode(g.tree.ItemAtFullIndex(self.itemParentIndex),
|
|
self.parent, self.elem,
|
|
g.tree.ItemAtFullIndex(self.itemIndex))
|
|
# Scroll to show new item
|
|
g.tree.EnsureVisible(item)
|
|
g.tree.SelectItem(item)
|
|
self.elem = None
|
|
# Update testWin if needed
|
|
if g.testWin and g.tree.IsHighlatable(item):
|
|
if g.conf.autoRefresh:
|
|
g.tree.needUpdate = True
|
|
g.tree.pendingHighLight = item
|
|
else:
|
|
g.tree.pendingHighLight = None
|
|
|
|
class UndoReplace:
|
|
def __init__(self, item):
|
|
self.itemIndex = g.tree.ItemFullIndex(item)
|
|
#self.xxx = g.tree.GetPyData(item)
|
|
self.elem = None
|
|
def destroy(self):
|
|
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.node
|
|
if xxx is self.xxx: # sizeritem or notebookpage - replace child
|
|
parent.replaceChild(self.xxx.child.node, xxx.child.node)
|
|
else:
|
|
parent.replaceChild(self.xxx.node, xxx.node)
|
|
self.xxx.parent = xxx.parent
|
|
xxx = self.xxx
|
|
g.tree.SetPyData(item, xxx)
|
|
g.tree.SetItemText(item, xxx.treeName())
|
|
g.tree.SetItemImage(item, xxx.treeImage())
|
|
|
|
# Update panel
|
|
g.panel.SetData(xxx)
|
|
# Update tools
|
|
g.tools.UpdateUI()
|
|
g.tree.EnsureVisible(item)
|
|
g.tree.SelectItem(item)
|
|
# Delete testWin?
|
|
if g.testWin:
|
|
# If deleting top-level item, delete testWin
|
|
if selected == g.testWin.item:
|
|
g.testWin.Destroy()
|
|
g.testWin = None
|
|
else:
|
|
# Remove highlight, update testWin
|
|
if g.testWin.highLight:
|
|
g.testWin.highLight.Remove()
|
|
g.tree.needUpdate = True
|
|
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)
|
|
self.selectedIndex = g.tree.ItemFullIndex(g.tree.GetSelection())
|
|
def destroy(self):
|
|
pass
|
|
# Update test view
|
|
def update(self, selected):
|
|
g.tree.Apply(g.tree.GetPyData(selected), selected)
|
|
# Update view
|
|
if g.testWin:
|
|
if g.testWin.highLight:
|
|
g.testWin.highLight.Remove()
|
|
g.tree.pendingHighLight = selected
|
|
if g.testWin:
|
|
g.tree.needUpdate = True
|
|
def undo(self):
|
|
# Restore selection
|
|
selected = g.tree.ItemAtFullIndex(self.selectedIndex)
|
|
if selected != g.tree.GetSelection():
|
|
g.tree.SelectItem(selected)
|
|
# Save current state for redo
|
|
map(ParamPage.SaveState, g.panel.pages)
|
|
pages = map(ParamPage.GetState, g.panel.pages)
|
|
map(ParamPage.SetState, g.panel.pages, self.pages)
|
|
self.pages = pages
|
|
self.update(selected)
|
|
def redo(self):
|
|
self.undo()
|
|
self.update(g.tree.GetSelection())
|