/*
* @(#)UndoRedoManager.java
*
* Copyright (c) 1996-2010 The authors and contributors of JHotDraw.
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
package org.jhotdraw.undo;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.undo.*;
import java.util.*;
import org.jhotdraw.util.*;
/**
* Same as javax.swing.UndoManager but provides actions for undo and
* redo operations.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class UndoRedoManager extends UndoManager {//javax.swing.undo.UndoManager {
private static final long serialVersionUID = 1L;
protected PropertyChangeSupport propertySupport = new PropertyChangeSupport(this);
private static final boolean DEBUG = false;
/**
* The resource bundle used for internationalisation.
*/
private static ResourceBundleUtil labels;
/**
* This flag is set to true when at
* least one significant UndoableEdit
* has been added to the manager since the
* last call to discardAllEdits.
*/
private boolean hasSignificantEdits = false;
/**
* This flag is set to true when an undo or redo
* operation is in progress. The UndoRedoManager
* ignores all incoming UndoableEdit events while
* this flag is true.
*/
private boolean undoOrRedoInProgress;
/**
* Sending this UndoableEdit event to the UndoRedoManager
* disables the Undo and Redo functions of the manager.
*/
public static final UndoableEdit DISCARD_ALL_EDITS = new AbstractUndoableEdit() {
private static final long serialVersionUID = 1L;
@Override
public boolean canUndo() {
return false;
}
@Override
public boolean canRedo() {
return false;
}
};
/**
* Undo Action for use in a menu bar.
*/
private class UndoAction
extends AbstractAction {
private static final long serialVersionUID = 1L;
public UndoAction() {
labels.configureAction(this, "edit.undo");
setEnabled(false);
}
/**
* Invoked when an action occurs.
*/
@Override
public void actionPerformed(ActionEvent evt) {
try {
undo();
} catch (CannotUndoException e) {
System.err.println("Cannot undo: "+e);
e.printStackTrace();
}
}
}
/**
* Redo Action for use in a menu bar.
*/
private class RedoAction
extends AbstractAction {
private static final long serialVersionUID = 1L;
public RedoAction() {
labels.configureAction(this, "edit.redo");
setEnabled(false);
}
/**
* Invoked when an action occurs.
*/
@Override
public void actionPerformed(ActionEvent evt) {
try {
redo();
} catch (CannotRedoException e) {
System.out.println("Cannot redo: "+e);
}
}
}
/** The undo action instance. */
private UndoAction undoAction;
/** The redo action instance. */
private RedoAction redoAction;
public static ResourceBundleUtil getLabels() {
if (labels == null) {
labels = ResourceBundleUtil.getBundle("org.jhotdraw.undo.Labels");
}
return labels;
}
/** Creates new UndoRedoManager */
public UndoRedoManager() {
getLabels();
undoAction = new UndoAction();
redoAction = new RedoAction();
}
public void setLocale(Locale l) {
labels = ResourceBundleUtil.getBundle("org.jhotdraw.undo.Labels", l);
}
/**
* Discards all edits.
*/
@Override
public void discardAllEdits() {
super.discardAllEdits();
updateActions();
setHasSignificantEdits(false);
}
public void setHasSignificantEdits(boolean newValue) {
boolean oldValue = hasSignificantEdits;
hasSignificantEdits = newValue;
firePropertyChange("hasSignificantEdits", oldValue, newValue);
}
/**
* Returns true if at least one significant UndoableEdit
* has been added since the last call to discardAllEdits.
*/
public boolean hasSignificantEdits() {
return hasSignificantEdits;
}
/**
* If inProgress, inserts anEdit at indexOfNextAdd, and removes
* any old edits that were at indexOfNextAdd or later. The die
* method is called on each edit that is removed is sent, in the
* reverse of the order the edits were added. Updates
* indexOfNextAdd.
*
* <p>If not inProgress, acts as a CompoundEdit</p>
*
* <p>Regardless of inProgress, if undoOrRedoInProgress,
* calls die on each edit that is sent.</p>
*
*
* @see CompoundEdit#end
* @see CompoundEdit#addEdit
*/
@Override
public boolean addEdit(UndoableEdit anEdit) {
if (DEBUG) System.out.println("UndoRedoManager@"+hashCode()+".add "+anEdit);
if (undoOrRedoInProgress) {
anEdit.die();
return true;
}
boolean success = super.addEdit(anEdit);
updateActions();
if (success && anEdit.isSignificant() && editToBeUndone() == anEdit) {
setHasSignificantEdits(true);
}
return success;
}
/**
* Gets the undo action for use as an Undo menu item.
*/
public Action getUndoAction() {
return undoAction;
}
/**
* Gets the redo action for use as a Redo menu item.
*/
public Action getRedoAction() {
return redoAction;
}
/**
* Updates the properties of the UndoAction
* and of the RedoAction.
*/
private void updateActions() {
String label;
if (DEBUG) System.out.println("UndoRedoManager@"+hashCode()+".updateActions "+
editToBeUndone()
+" canUndo="+canUndo()+" canRedo="+canRedo());
if (canUndo()) {
undoAction.setEnabled(true);
label = getUndoPresentationName();
} else {
undoAction.setEnabled(false);
label = labels.getString("edit.undo.text");
}
undoAction.putValue(Action.NAME, label);
undoAction.putValue(Action.SHORT_DESCRIPTION, label);
if (canRedo()) {
redoAction.setEnabled(true);
label = getRedoPresentationName();
} else {
redoAction.setEnabled(false);
label = labels.getString("edit.redo.text");
}
redoAction.putValue(Action.NAME, label);
redoAction.putValue(Action.SHORT_DESCRIPTION, label);
}
/**
* Undoes the last edit event.
* The UndoRedoManager ignores all incoming UndoableEdit events,
* while undo is in progress.
*/
@Override
public void undo()
throws CannotUndoException {
undoOrRedoInProgress = true;
try {
super.undo();
} finally {
undoOrRedoInProgress = false;
updateActions();
}
}
/**
* Redoes the last undone edit event.
* The UndoRedoManager ignores all incoming UndoableEdit events,
* while redo is in progress.
*/
@Override
public void redo()
throws CannotUndoException {
undoOrRedoInProgress = true;
try {
super.redo();
} finally {
undoOrRedoInProgress = false;
updateActions();
}
}
/**
* Undoes or redoes the last edit event.
* The UndoRedoManager ignores all incoming UndoableEdit events,
* while undo or redo is in progress.
*/
@Override
public void undoOrRedo()
throws CannotUndoException, CannotRedoException {
undoOrRedoInProgress = true;
try {
super.undoOrRedo();
} finally {
undoOrRedoInProgress = false;
updateActions();
}
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupport.addPropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertySupport.addPropertyChangeListener( propertyName, listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertySupport.removePropertyChangeListener(listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertySupport.removePropertyChangeListener(propertyName, listener);
}
protected void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
propertySupport.firePropertyChange(propertyName, oldValue, newValue);
}
protected void firePropertyChange(String propertyName, int oldValue, int newValue) {
propertySupport.firePropertyChange(propertyName, oldValue, newValue);
}
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
propertySupport.firePropertyChange(propertyName, oldValue, newValue);
}
}