# Name: undo.py # Purpose: XRC editor, undo/redo module # Author: Roman Rolinsky # Created: 01.12.2002 # RCS-ID: $Id: undo.py 54812 2008-07-29 13:39:00Z ROL $ from globals import * import view from component import Manager from model import Model undo_depth = 10 # max number of undo remembered # Undo/redo classes class UndoManager: # Undo/redo stacks undo = [] redo = [] def RegisterUndo(self, undoObj): TRACE('RegisterUndo: %s', undoObj.label) self.undo.append(undoObj) while len(self.undo) > undo_depth: self.undo.pop(0) map(Undo.destroy, self.redo) self.redo = [] self.UpdateToolHelp() def GetUndoLabel(self): return self.undo[-1].label def GetRedoLabel(self): return self.redo[-1].label def Undo(self): undoObj = self.undo.pop() undoObj.undo() self.redo.append(undoObj) view.frame.SetStatusText('Undone') Presenter.setModified() self.UpdateToolHelp() def Redo(self): undoObj = self.redo.pop() undoObj.redo() self.undo.append(undoObj) view.frame.SetStatusText('Redone') Presenter.setModified() self.UpdateToolHelp() def Clear(self): map(Undo.destroy, self.undo) self.undo = [] map(Undo.destroy, self.redo) self.redo = [] self.UpdateToolHelp() def CanUndo(self): return bool(self.undo) def CanRedo(self): return bool(self.redo) def UpdateToolHelp(self): if g.undoMan.CanUndo(): msg = 'Undo ' + self.GetUndoLabel() view.frame.tb.SetToolShortHelp(wx.ID_UNDO, msg) view.frame.tb.SetToolLongHelp(wx.ID_UNDO, msg) if g.undoMan.CanRedo(): msg = 'Redo ' + self.GetRedoLabel() view.frame.tb.SetToolShortHelp(wx.ID_REDO, msg) view.frame.tb.SetToolLongHelp(wx.ID_REDO, msg) class Undo: '''ABC for Undo*.''' def redo(self): # usually redo is same as undo self.undo() def destroy(self): pass class UndoCutDelete(Undo): label = 'cut/delete' def __init__(self, itemIndex, state, node): self.itemIndex = itemIndex self.state = state self.node = node def destroy(self): if self.node: self.node.unlink() self.node = None def undo(self): Presenter.unselect() # Updating DOM. Find parent node first parentItem = view.tree.ItemAtFullIndex(self.itemIndex[:-1]) parentNode = view.tree.GetPyData(parentItem) parentComp = Manager.getNodeComp(parentNode) nextItem = view.tree.ItemAtFullIndex(self.itemIndex) if nextItem: nextNode = parentComp.getTreeOrImplicitNode(view.tree.GetPyData(nextItem)) else: nextNode = None # Insert before next parentNode.insertBefore(self.node, nextNode) # Remember test window item if view.testWin.item is not None: testItemIndex = view.tree.ItemFullIndex(view.testWin.item) # Update tree and presenter view.tree.FlushSubtree(parentItem, parentNode) view.tree.SetFullState(self.state) # Restore test window item if view.testWin.item is not None: view.testWin.item = view.tree.ItemAtFullIndex(testItemIndex) item = view.tree.ItemAtFullIndex(self.itemIndex) view.tree.EnsureVisible(item) # This will generate events view.tree.SelectItem(item) def redo(self): item = view.tree.ItemAtFullIndex(self.itemIndex) Presenter.setData(item) self.node = Presenter.delete(item) # Undoing paste/create is the opposite of cut/delete, so we can reuse # UndoCutDelete class swapping undo<->redo class UndoPasteCreate(UndoCutDelete): label = 'paste/create' # The ctor is different because node is not known initially def __init__(self, itemIndex, state): self.itemIndex = itemIndex # new item index self.state = state # tree state self.node = None undo = UndoCutDelete.redo redo = UndoCutDelete.undo class UndoReplace(Undo): label = 'replace' def __init__(self, itemIndex, comp, node): self.itemIndex = itemIndex self.comp = comp self.node = node def destroy(self): if self.node: self.node.unlink() self.node = None def undo(self): # Replace current node with old node Presenter.unselect() item = view.tree.ItemAtFullIndex(self.itemIndex) Presenter.setData(item) comp = self.comp node = self.node data = wx.TreeItemData(node) parentItem = view.tree.GetItemParent(item) parentNode = view.tree.GetPyData(parentItem) self.node = view.tree.GetPyData(item) self.comp = Presenter.comp Presenter.container.replaceChild(parentNode, node, self.node) # Replace tree item: insert new, remove old label = comp.getTreeText(node) imageId = comp.getTreeImageId(node) item = view.tree.InsertItem(parentItem, item, label, imageId, data=data) view.tree.Delete(view.tree.GetPrevSibling(item)) Presenter.item = item # Add children for n in filter(is_object, node.childNodes): view.tree.AddNode(item, comp.getTreeNode(n)) view.tree.EnsureVisible(item) # Update panel view.tree.SelectItem(item) Presenter.setModified() class UndoEdit(Undo): '''Undo class for using in AttributePanel.''' label = 'edit' def __init__(self, item, page): self.index = view.tree.ItemFullIndex(item) self.page = page panel = view.panel.nb.GetPage(page).panel self.values = panel.GetValues() def undo(self): # Go back to the previous item Presenter.unselect() item = view.tree.ItemAtFullIndex(self.index) Presenter.setData(item) panel = view.panel.nb.GetPage(self.page).panel values = panel.GetValues() panel.SetValues(self.values) Presenter.update(item) self.values = values view.tree.SelectItem(item) class UndoGlobal(Undo): '''Undo class storing a copy of the complete tree. Can be used for non-frequent operations to avoid programming special undo classes.''' label = 'global' def __init__(self): self.mainNode = Model.mainNode.cloneNode(True) self.state = view.tree.GetFullState() def destroy(self): self.mainNode.unlink() def undo(self): # Exchange Model.mainNode,self.mainNode = \ self.mainNode,Model.dom.replaceChild(self.mainNode, Model.mainNode) # Replace testElem Model.testElem = Model.mainNode.childNodes[0] state = view.tree.GetFullState() Presenter.flushSubtree() view.tree.SetFullState(self.state) self.state = state def redo(self): self.undo() Presenter.unselect()