|
The Command Pattern for Undo/Redo
An Introduction with Examples in Java |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
Action class that
implements javax.swing.undo.UndoableEditListener
and handles actionPerformed() messages as
desiredAction
UndoableEditSupport objectAction to
the UndoableEditSupport
UndoableEditSupport to post the editimport java.awt.*;
import javax.swing.*;
/**
* A simple yard marker for a football scoreboard
*
* @version 1.0
* @author Prof. David Benrstein, James Madison University
*/
public class YardMarker extends JPanel
{
private int down, toGo, yard;
private JLabel downL, sideL, toGoL, yardL;
private String side;
/**
* Default Constructor
*/
public YardMarker()
{
super();
downL = new JLabel();
sideL = new JLabel();
toGoL = new JLabel();
yardL = new JLabel();
performLayout();
}
/**
* Returns the current down
*/
public int getDown()
{
return down;
}
/**
* Returns the current side of the field the
* ball is on
*/
public String getSide()
{
return side;
}
/**
* Returns the current yards to go for a first down
*/
public int getToGo()
{
return toGo;
}
/**
* Returns the current position of the ball
*/
public int getYard()
{
return yard;
}
/**
* Layout this component
*/
private void performLayout()
{
setLayout(new GridLayout(1,7));
add(new JLabel("Down: "));
downL.setForeground(Color.BLUE);
add(downL);
add(new JLabel("To Go: "));
toGoL.setForeground(Color.BLUE);
add(toGoL);
add(new JLabel("On: "));
sideL.setForeground(Color.BLUE);
add(sideL);
yardL.setForeground(Color.BLUE);
add(yardL);
}
/**
* Set the current values
*/
public void setValues(int down, int toGo, String side, int yard)
{
this.down = down;
this.toGo = toGo;
this.side = side;
this.yard = yard;
downL.setText(""+down);
toGoL.setText(""+toGo);
sideL.setText(side);
yardL.setText(""+yard);
}
}
import javax.swing.undo.*;
/**
* An (undoable) change to a YardMarker
*
* @version 1.0
* @author Prof. David Benrstein, James Madison University
*/
public class YardMarkerEdit extends AbstractUndoableEdit
{
private int down, toGo, yard;
private int oldDown, oldToGo, oldYard;
private String side;
private String oldSide;
private YardMarker ym;
/**
* Explicit Value Constructor
*/
public YardMarkerEdit(YardMarker ym,
int down, int toGo, String side, int yard)
{
super();
this.ym = ym;
this.down = down;
this.toGo = toGo;
this.side = side;
this.yard = yard;
oldDown = ym.getDown();
oldToGo = ym.getToGo();
oldSide = ym.getSide();
oldYard = ym.getYard();
ym.setValues(down, toGo, side, yard);
}
/**
* Get a description of this edit
*/
public String getPresentationName()
{
return ""+down+" and "+toGo+" on "+side+" "+yard;
}
/**
* Redo this edit
*/
public void redo() throws CannotUndoException
{
super.redo(); // Sets hasBeenDone to true
ym.setValues(down, toGo, side, yard);
}
/**
* Redo this edit
*/
public void undo() throws CannotUndoException
{
super.undo(); // Sets hasBeenDone to false
ym.setValues(oldDown, oldToGo, oldSide, oldYard);
}
}
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.undo.*;
/**
* A fairly generic undo/redo action that can be added to
* menus and toolbars.
*
* It changes its state when an edit occurs and when
* an undo/redo occurs.
*
* @version 1.0
* @author Prof. David Bernstein, James Madison University
*/
public class UndoRedoAction extends AbstractAction
implements UndoableEditListener
{
private UndoableEdit lastEdit;
/**
* Default Constructor
*/
public UndoRedoAction()
{
putValue(Action.NAME, "Undo/Redo");
setEnabled(false);
}
/**
* Perform the action
*/
public void actionPerformed(ActionEvent evt)
{
if (lastEdit != null)
{
if (lastEdit.canUndo())
{
lastEdit.undo();
putValue(Action.NAME,
lastEdit.getRedoPresentationName());
}
else if (lastEdit.canRedo())
{
lastEdit.redo();
putValue(Action.NAME,
lastEdit.getUndoPresentationName());
}
else
{
putValue(Action.NAME, "Undo/Redo");
setEnabled(false);
}
}
}
/**
* Handle UndoableEidtHappened events
* (Required by UndoableEditListener)
*/
public void undoableEditHappened(UndoableEditEvent evt)
{
lastEdit = evt.getEdit();
if (lastEdit.canUndo())
{
putValue(Action.NAME,
lastEdit.getUndoPresentationName());
setEnabled(true);
}
}
}
import java.awt.*;
import javax.swing.*;
import javax.swing.undo.*;
/**
* An example that demonstrates undo/redo.
*
* @version 1.0
* @author Prof. David Bernstein, James Madison University
*/
public class Driver implements Runnable
{
private UndoableEditSupport support;
private YardMarker ym;
/**
* The entry point of the application.
*
* @param args The command line arguments (ignored)
*/
public static void main(String[] args) throws Exception
{
Driver driver = new Driver();
SwingUtilities.invokeAndWait(driver);
// Make a change
YardMarkerEdit edit = new YardMarkerEdit(driver.ym, 1, 10, "Own", 35);
driver.support.postEdit(edit);
// Sleep to give time to see the scoreboard
// before the change
try
{
Thread.sleep(5000);
}
catch (InterruptedException ie)
{
}
// Make a change
edit = new YardMarkerEdit(driver.ym, 2, 8, "Own", 37);
driver.support.postEdit(edit);
}
/**
* The code to run in the event dispatch thread.
*/
public void run()
{
// Layout the frame
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(400,400);
Container contentPane = f.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(new JLabel("Bernstein Stadium"), BorderLayout.NORTH);
ym = new YardMarker();
contentPane.add(ym, BorderLayout.SOUTH);
contentPane.add(new JLabel("Your ad here!"), BorderLayout.CENTER);
JMenuBar menuBar = new JMenuBar();
JMenu editMenu = new JMenu("Edit");
UndoRedoAction urAction = new UndoRedoAction();
editMenu.add(urAction);
menuBar.add(editMenu);
f.setJMenuBar(menuBar);
// Add undo/redo support
support = new UndoableEditSupport();
support.addUndoableEditListener(urAction);
f.setVisible(true);
}
}