UndoRedo V1.2
by Matthew Ford
2005/06/27

au.com.forward.undoRedo
Class UndoRedoManager

java.lang.Object
  extended by java.awt.Component
      extended by java.awt.Container
          extended by javax.swing.JComponent
              extended by javax.swing.JSplitPane
                  extended by au.com.forward.undoRedo.UndoRedoManager
All Implemented Interfaces:
java.awt.image.ImageObserver, java.awt.MenuContainer, java.io.Serializable, java.util.EventListener, javax.accessibility.Accessible, javax.swing.event.UndoableEditListener

public class UndoRedoManager
extends javax.swing.JSplitPane
implements javax.swing.event.UndoableEditListener

This is the main class, it handles a tree of undo/redos. The calls that modify the data structure, undo(), redo(), processUndoRedoCommand() and undoableEditHappened(UndoableEditEvent) are not synchronized but are protected by a busy flag. If the manager is already busy processing one of these calls, another call to any of these will just quietly return doing nothing.
If the manager is busy, calls to canUndo() and canRedo() will return false.
This makes the UndoRedoManager data structure threadsafe and deadlock safe, but it is upto you to prevent concurrent undoableEdits, undos and redos in order to keep the undo/redo data structure complete.

See Also:
Serialized Form

Nested Class Summary
protected  class UndoRedoManager.DisplayTextArea
           
protected  class UndoRedoManager.Frame
          This class hold the information needed to reset the UndoRedo tree to some prevous state
protected  class UndoRedoManager.TextAreaWrapper
           
protected  class UndoRedoManager.UndoRedoTreeSelectionListener
           
protected  class UndoRedoManager.WidthEnabledCellRenderer
          A tree cell renderer which respects the set width.
 
Nested classes/interfaces inherited from class javax.swing.JSplitPane
javax.swing.JSplitPane.AccessibleJSplitPane
 
Nested classes/interfaces inherited from class javax.swing.JComponent
javax.swing.JComponent.AccessibleJComponent
 
Nested classes/interfaces inherited from class java.awt.Container
java.awt.Container.AccessibleAWTContainer
 
Nested classes/interfaces inherited from class java.awt.Component
java.awt.Component.AccessibleAWTComponent, java.awt.Component.BltBufferStrategy, java.awt.Component.FlipBufferStrategy
 
Field Summary
protected  java.util.WeakHashMap<java.lang.Long,UndoRedoManager.Frame> frameMarkerMap
          A map holding weak keys to the tree frames.
protected  int preferredWidth
          The preferred width of the tree node text displays.
protected  UndoRedoTreeNode rootNode
          The rootNode of the tree model.
protected  javax.swing.JScrollPane textScrollPane
          The scrollPane holding the extended description text component.
protected  javax.swing.JTree tree
          The undoRedo JTree
protected  javax.swing.tree.DefaultTreeModel treeModel
          The undo/redo tree model
protected  javax.swing.JScrollPane treeScrollPane
          The scrollPane holding the undo/redo Tree.
static java.lang.String VERSION
          UndoRedoManager Version
 
Fields inherited from class javax.swing.JSplitPane
BOTTOM, CONTINUOUS_LAYOUT_PROPERTY, continuousLayout, DIVIDER, DIVIDER_LOCATION_PROPERTY, DIVIDER_SIZE_PROPERTY, dividerSize, HORIZONTAL_SPLIT, LAST_DIVIDER_LOCATION_PROPERTY, lastDividerLocation, LEFT, leftComponent, ONE_TOUCH_EXPANDABLE_PROPERTY, oneTouchExpandable, orientation, ORIENTATION_PROPERTY, RESIZE_WEIGHT_PROPERTY, RIGHT, rightComponent, TOP, VERTICAL_SPLIT
 
Fields inherited from class javax.swing.JComponent
accessibleContext, listenerList, TOOL_TIP_TEXT_KEY, ui, UNDEFINED_CONDITION, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, WHEN_FOCUSED, WHEN_IN_FOCUSED_WINDOW
 
Fields inherited from class java.awt.Component
BOTTOM_ALIGNMENT, CENTER_ALIGNMENT, LEFT_ALIGNMENT, RIGHT_ALIGNMENT, TOP_ALIGNMENT
 
Fields inherited from interface java.awt.image.ImageObserver
ABORT, ALLBITS, ERROR, FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, WIDTH
 
Constructor Summary
UndoRedoManager()
          Creates a new instance of UndoRedoManager.
UndoRedoManager(javax.swing.Action enter, IDisplayText textComponent, boolean canRollBack)
          Creates a new instance of UndoRedoManager.
 
Method Summary
 void cancel(java.lang.Long frameMarkerKey)
          Rolls back undo/redo to the last command that failed.
 boolean canRedo()
          Is there a redo branch at the top that can be redone, does not check for dead nodes.
 boolean canUndo()
          Is there an undo node in the main trunk that can be undone, does not check for dead nodes.
protected  java.util.Vector<UndoRedoTreeNode> compressFullPath(java.util.Vector<UndoRedoTreeNode> fullPath)
          Go through the vector and cancel (remove) redo/undo adjacent pairs
Input arg is unchanged.
 void disposeOfRollBack(java.lang.Long frameMarkerKey)
          Frees the resources associated with a frameMarker.
static boolean equalsComplement(UndoRedoTreeNode obj1, UndoRedoTreeNode obj2)
          Compare two DefaultMutableTreeNodes for UndoRedo complements.
protected  UndoRedoTreeNode getRoot()
          Returns the root node of the tree
 IDisplayText getTextDisplayComponent()
          Get the Component used to display extended descriptions
 javax.swing.JTree getTree()
          Get the JTree to display
 boolean isBlocked()
          Is UndoRedoManager currently blocked from accepting undo,redo or processUndoRedoCommand commands
Note: isBlocked() == true does not prevent undoableEditHappened() from processing new undoable events.
 boolean isBusy()
          Is the manager busy doing something.
 boolean isBusyOrBlocked()
          Is UndoRedoManager busy or blocked?
 void processUndoRedoCommand()
          Undo/redo to the selected tree node.
 void redo()
          Find the top redo and redo it.
 void setBlocked(boolean flag)
          SetBlocked state in UndoRedoManager.
protected  void setKeyboardMappings(javax.swing.Action enter)
          Set the Enter keyboard mapping for the tree to the given Action.
 void setPreferredDisplayWidth(int width)
          Sets the preferred width of the node display.
 void setTextDisplayComponent(IDisplayText textDisplayComponent)
          Sets the Component used to display extended descriptions.
 boolean supportsRollBack()
          Returns the rollback setting for the UndoRedoManager.
 void syncToLastSuccessfull(java.lang.Long frameMarkerKey)
          Rolls back undo/redo to the last sucessfull command.
 void undo()
          Find the top undo and undo it.
 void undoableEditHappened(javax.swing.event.UndoableEditEvent e)
          An undoable edit happened, handle it.
 
Methods inherited from class javax.swing.JSplitPane
addImpl, getAccessibleContext, getBottomComponent, getDividerLocation, getDividerSize, getLastDividerLocation, getLeftComponent, getMaximumDividerLocation, getMinimumDividerLocation, getOrientation, getResizeWeight, getRightComponent, getTopComponent, getUI, getUIClassID, isContinuousLayout, isOneTouchExpandable, isValidateRoot, paintChildren, paramString, remove, remove, removeAll, resetToPreferredSizes, setBottomComponent, setContinuousLayout, setDividerLocation, setDividerLocation, setDividerSize, setLastDividerLocation, setLeftComponent, setOneTouchExpandable, setOrientation, setResizeWeight, setRightComponent, setTopComponent, setUI, updateUI
 
Methods inherited from class javax.swing.JComponent
addAncestorListener, addNotify, addVetoableChangeListener, computeVisibleRect, contains, createToolTip, disable, enable, firePropertyChange, firePropertyChange, firePropertyChange, fireVetoableChange, getActionForKeyStroke, getActionMap, getAlignmentX, getAlignmentY, getAncestorListeners, getAutoscrolls, getBorder, getBounds, getClientProperty, getComponentGraphics, getComponentPopupMenu, getConditionForKeyStroke, getDebugGraphicsOptions, getDefaultLocale, getFontMetrics, getGraphics, getHeight, getInheritsPopupMenu, getInputMap, getInputMap, getInputVerifier, getInsets, getInsets, getListeners, getLocation, getMaximumSize, getMinimumSize, getNextFocusableComponent, getPopupLocation, getPreferredSize, getRegisteredKeyStrokes, getRootPane, getSize, getToolTipLocation, getToolTipText, getToolTipText, getTopLevelAncestor, getTransferHandler, getVerifyInputWhenFocusTarget, getVetoableChangeListeners, getVisibleRect, getWidth, getX, getY, grabFocus, isDoubleBuffered, isLightweightComponent, isManagingFocus, isOpaque, isOptimizedDrawingEnabled, isPaintingTile, isRequestFocusEnabled, paint, paintBorder, paintComponent, paintImmediately, paintImmediately, print, printAll, printBorder, printChildren, printComponent, processComponentKeyEvent, processKeyBinding, processKeyEvent, processMouseEvent, processMouseMotionEvent, putClientProperty, registerKeyboardAction, registerKeyboardAction, removeAncestorListener, removeNotify, removeVetoableChangeListener, repaint, repaint, requestDefaultFocus, requestFocus, requestFocus, requestFocusInWindow, requestFocusInWindow, resetKeyboardActions, reshape, revalidate, scrollRectToVisible, setActionMap, setAlignmentX, setAlignmentY, setAutoscrolls, setBackground, setBorder, setComponentPopupMenu, setDebugGraphicsOptions, setDefaultLocale, setDoubleBuffered, setEnabled, setFocusTraversalKeys, setFont, setForeground, setInheritsPopupMenu, setInputMap, setInputVerifier, setMaximumSize, setMinimumSize, setNextFocusableComponent, setOpaque, setPreferredSize, setRequestFocusEnabled, setToolTipText, setTransferHandler, setUI, setVerifyInputWhenFocusTarget, setVisible, unregisterKeyboardAction, update
 
Methods inherited from class java.awt.Container
add, add, add, add, add, addContainerListener, addPropertyChangeListener, addPropertyChangeListener, applyComponentOrientation, areFocusTraversalKeysSet, countComponents, deliverEvent, doLayout, findComponentAt, findComponentAt, getComponent, getComponentAt, getComponentAt, getComponentCount, getComponents, getComponentZOrder, getContainerListeners, getFocusTraversalKeys, getFocusTraversalPolicy, getLayout, getMousePosition, insets, invalidate, isAncestorOf, isFocusCycleRoot, isFocusCycleRoot, isFocusTraversalPolicyProvider, isFocusTraversalPolicySet, layout, list, list, locate, minimumSize, paintComponents, preferredSize, printComponents, processContainerEvent, processEvent, removeContainerListener, setComponentZOrder, setFocusCycleRoot, setFocusTraversalPolicy, setFocusTraversalPolicyProvider, setLayout, transferFocusBackward, transferFocusDownCycle, validate, validateTree
 
Methods inherited from class java.awt.Component
action, add, addComponentListener, addFocusListener, addHierarchyBoundsListener, addHierarchyListener, addInputMethodListener, addKeyListener, addMouseListener, addMouseMotionListener, addMouseWheelListener, bounds, checkImage, checkImage, coalesceEvents, contains, createImage, createImage, createVolatileImage, createVolatileImage, disableEvents, dispatchEvent, enable, enableEvents, enableInputMethods, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, getBackground, getBounds, getColorModel, getComponentListeners, getComponentOrientation, getCursor, getDropTarget, getFocusCycleRootAncestor, getFocusListeners, getFocusTraversalKeysEnabled, getFont, getForeground, getGraphicsConfiguration, getHierarchyBoundsListeners, getHierarchyListeners, getIgnoreRepaint, getInputContext, getInputMethodListeners, getInputMethodRequests, getKeyListeners, getLocale, getLocation, getLocationOnScreen, getMouseListeners, getMouseMotionListeners, getMousePosition, getMouseWheelListeners, getName, getParent, getPeer, getPropertyChangeListeners, getPropertyChangeListeners, getSize, getToolkit, getTreeLock, gotFocus, handleEvent, hasFocus, hide, imageUpdate, inside, isBackgroundSet, isCursorSet, isDisplayable, isEnabled, isFocusable, isFocusOwner, isFocusTraversable, isFontSet, isForegroundSet, isLightweight, isMaximumSizeSet, isMinimumSizeSet, isPreferredSizeSet, isShowing, isValid, isVisible, keyDown, keyUp, list, list, list, location, lostFocus, mouseDown, mouseDrag, mouseEnter, mouseExit, mouseMove, mouseUp, move, nextFocus, paintAll, postEvent, prepareImage, prepareImage, processComponentEvent, processFocusEvent, processHierarchyBoundsEvent, processHierarchyEvent, processInputMethodEvent, processMouseWheelEvent, remove, removeComponentListener, removeFocusListener, removeHierarchyBoundsListener, removeHierarchyListener, removeInputMethodListener, removeKeyListener, removeMouseListener, removeMouseMotionListener, removeMouseWheelListener, removePropertyChangeListener, removePropertyChangeListener, repaint, repaint, repaint, resize, resize, setBounds, setBounds, setComponentOrientation, setCursor, setDropTarget, setFocusable, setFocusTraversalKeysEnabled, setIgnoreRepaint, setLocale, setLocation, setLocation, setName, setSize, setSize, show, show, size, toString, transferFocus, transferFocusUpCycle
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

VERSION

public static final java.lang.String VERSION
UndoRedoManager Version

See Also:
Constant Field Values

tree

protected javax.swing.JTree tree
The undoRedo JTree


rootNode

protected UndoRedoTreeNode rootNode
The rootNode of the tree model.


treeModel

protected javax.swing.tree.DefaultTreeModel treeModel
The undo/redo tree model


frameMarkerMap

protected java.util.WeakHashMap<java.lang.Long,UndoRedoManager.Frame> frameMarkerMap
A map holding weak keys to the tree frames.


preferredWidth

protected int preferredWidth
The preferred width of the tree node text displays.


textScrollPane

protected javax.swing.JScrollPane textScrollPane
The scrollPane holding the extended description text component.


treeScrollPane

protected javax.swing.JScrollPane treeScrollPane
The scrollPane holding the undo/redo Tree.

Constructor Detail

UndoRedoManager

public UndoRedoManager()
Creates a new instance of UndoRedoManager.
Using the default Enter key action which undoes/redoes to the selected tree node.


UndoRedoManager

public UndoRedoManager(javax.swing.Action enter,
                       IDisplayText textComponent,
                       boolean canRollBack)
Creates a new instance of UndoRedoManager.

Parameters:
textComponent - - the Component used to display the extended descriptions,
if null a default class extending JTextArea is used.
enter - - the action to attach to the Enter key for the selected treenode,
if null use a default action which undoes/redoes to the selected node
canRollBack - - true if this UndoRedoManager supports rollback. If true then all UndoableEdits passed to undoableEditHappened(javax.swing.event.UndoableEditEvent) must implement ICanBeRolledBack
Method Detail

supportsRollBack

public boolean supportsRollBack()
Returns the rollback setting for the UndoRedoManager.

Returns:
- true if rollback is supported, else false.
It true then all UndoableEdits passed this manager must implement ICanBeRolledBack.

undoableEditHappened

public void undoableEditHappened(javax.swing.event.UndoableEditEvent e)
An undoable edit happened, handle it.
If the the manager is already busy just return quietly.
Wraps the UndoableEditEvent in an UndoRedo object and add it to the tree.
Sets busy while it is processing.

Specified by:
undoableEditHappened in interface javax.swing.event.UndoableEditListener
Parameters:
e - - the UndoableEditEvent

syncToLastSuccessfull

public void syncToLastSuccessfull(java.lang.Long frameMarkerKey)
Rolls back undo/redo to the last sucessfull command.
Use this to recover the undo/redo stack to match the last command that the server actually executed.

The frameMarker points to the frame prior to the command being executed. The undo/redo is rolled back to the following frame in the stack, i.e. the frame after the command has been executed.
The frameMarker is disposed of after this call.
If the frameMarker is not valid (cannot be found) just ignore quietly. This allows this method to be called in any order of a sequence of roll backs. The oldest frameMarker will take precedence.

Parameters:
frameMarkerKey - the marker before the one to roll back to.

cancel

public void cancel(java.lang.Long frameMarkerKey)
Rolls back undo/redo to the last command that failed.
Use this to recover the undo/redo stack to match the last command that the server actually executed.

Rolls back an undo/redo if it fails or is cancelled.
This and following frameMarkers are disposed of after this call.
If the frameMarker is not valid (cannot be found) just ignore quietly. This allows this method to be called in any order of a sequence of roll backs. The oldest frameMarker will take precedence.

Parameters:
frameMarkerKey - the marker to roll back to.

disposeOfRollBack

public void disposeOfRollBack(java.lang.Long frameMarkerKey)
Frees the resources associated with a frameMarker.
Call after an UndoableEdit redo/undo completes successfully.

FrameMarkers are only weekly held by the UndoRedoManager so they can be garbage collected when there are no other references. Since the marker is set in the UndoableEdit implementing this interface, you need to null it out that reference, and any others, in order have it garbage collected, if you do not call this method.

Parameters:
frameMarkerKey - the marker to free

getTree

public javax.swing.JTree getTree()
Get the JTree to display

Returns:
the tree showing the undo/redo branches

isBusy

public boolean isBusy()
Is the manager busy doing something.

Returns:
true if UndoRedoManager is busy handling undoableEditHappened(UndoableEditEvent), undo() or redo() or processUndoRedoCommand(), else false

setBlocked

public void setBlocked(boolean flag)
SetBlocked state in UndoRedoManager.
Set to true to prevent undo, redo, and processUndoRedoCommand commands being processed.
Note: Setting blocked true does not prevent undoableEditHappened() from processing new undoable events. It only blocks user inputs.

Parameters:
flag - true to prevent undo, redo, and processUndoRedoCommand from responding
false to enable processing again.

isBlocked

public boolean isBlocked()
Is UndoRedoManager currently blocked from accepting undo,redo or processUndoRedoCommand commands
Note: isBlocked() == true does not prevent undoableEditHappened() from processing new undoable events. It only blocks user inputs.

Returns:
true if undo, redo, and processUndoRedoCommand are blocked

isBusyOrBlocked

public boolean isBusyOrBlocked()
Is UndoRedoManager busy or blocked?

Returns:
true if either busy processing or blocked

canUndo

public boolean canUndo()
Is there an undo node in the main trunk that can be undone, does not check for dead nodes.

Returns:
true if there is an undo node, false if busy or all main branch nodes have been undone

canRedo

public boolean canRedo()
Is there a redo branch at the top that can be redone, does not check for dead nodes.

Returns:
true if top node in a redo branch has not already been redone after cancelling out the undo nodes with their redos in the highest most redo branchs (depth first).
False if busy or top node after cancellation is an undo or all redo's nodes in the highest most redo branchs (depth first) have been redone.

undo

public void undo()
          throws javax.swing.undo.CannotUndoException
Find the top undo and undo it.
Just return if nothing to do or if the manager is busy.
Sets busy while it is processing.

Throws:
javax.swing.undo.CannotUndoException - if cannot be undone.

redo

public void redo()
          throws javax.swing.undo.CannotRedoException
Find the top redo and redo it.
Just return if nothing to do or the manager is busy.
Sets busy while it is processing.

Throws:
javax.swing.undo.CannotRedoException - if cannot be redone.

processUndoRedoCommand

public void processUndoRedoCommand()
Undo/redo to the selected tree node.
If no node selected or if already busy, just return quietly.
Sets busy while it is processing.
Note: if the undo/redo will take some time you should start another thread to run this method.


setPreferredDisplayWidth

public void setPreferredDisplayWidth(int width)
Sets the preferred width of the node display.
Use this to prevent node descriptions being truncated.

Parameters:
width - the width to set, if it is <20 the width is set at 20

equalsComplement

public static boolean equalsComplement(UndoRedoTreeNode obj1,
                                       UndoRedoTreeNode obj2)
Compare two DefaultMutableTreeNodes for UndoRedo complements.
UndoRedo complements have the same UndoableEdit but
undoRedo1.isUndo() == (! undoRedo2.isUndo())

Note: two BranchUndoRedo userObjects will compare as equal

Parameters:
obj1 - - node 1
obj2 - - node 2
Returns:
true if node 1 is an UndoRedo complement of node 2, else false

getRoot

protected UndoRedoTreeNode getRoot()
Returns the root node of the tree

Returns:
root node

setKeyboardMappings

protected void setKeyboardMappings(javax.swing.Action enter)
Set the Enter keyboard mapping for the tree to the given Action.

Parameters:
enter - the action to use.

compressFullPath

protected java.util.Vector<UndoRedoTreeNode> compressFullPath(java.util.Vector<UndoRedoTreeNode> fullPath)
Go through the vector and cancel (remove) redo/undo adjacent pairs
Input arg is unchanged.

Parameters:
fullPath - the complete list of nodes in the path from the top of the tree to the selected node
Returns:
the compressed list with paired undo/redo nodes and BranchUndoRedo nodes removed. This input list is unchanged.

getTextDisplayComponent

public IDisplayText getTextDisplayComponent()
Get the Component used to display extended descriptions

Returns:
The IDisplayText component which extends Component.

setTextDisplayComponent

public void setTextDisplayComponent(IDisplayText textDisplayComponent)
Sets the Component used to display extended descriptions.

Parameters:
textDisplayComponent - The Component that will display extended descriptions.

©2005, Forward Computing and Control Pty. Ltd
ACN 003 669 994   NSW Australia
All Rights Reserved.