package com.revolsys.swing.undo;
import java.awt.Component;
import java.awt.Event;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeSupport;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.text.JTextComponent;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoableEdit;
import com.revolsys.beans.PropertyChangeSupportProxy;
import com.revolsys.io.BaseCloseable;
import com.revolsys.swing.action.RunnableAction;
import com.revolsys.util.OS;
import com.revolsys.value.GlobalBooleanValue;
public class UndoManager extends javax.swing.undo.UndoManager
implements PropertyChangeSupportProxy {
private static final long serialVersionUID = 1L;
private final GlobalBooleanValue eventsEnabled = new GlobalBooleanValue(true);
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
@Override
public synchronized boolean addEdit(final UndoableEdit edit) {
if (edit == null) {
return false;
} else {
final boolean enabled = isEventsEnabled();
if (enabled) {
try (
BaseCloseable c = setEventsEnabled(false)) {
if (edit instanceof AbstractUndoableEdit) {
final AbstractUndoableEdit abstractEdit = (AbstractUndoableEdit)edit;
if (!abstractEdit.isHasBeenDone()) {
if (abstractEdit.canRedo()) {
abstractEdit.redo();
} else {
return false;
}
}
}
}
if (isEventsEnabled()) {
final boolean added = super.addEdit(edit);
fireEvents();
return added;
} else {
return false;
}
} else {
return false;
}
}
}
public void addKeyMap(final Component component) {
if (component instanceof JComponent) {
final JComponent jcomponent = (JComponent)component;
if (jcomponent instanceof JTextComponent) {
final JTextComponent textComponent = (JTextComponent)jcomponent;
textComponent.getDocument().addUndoableEditListener(this);
}
final InputMap inputMap = jcomponent
.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
final ActionMap actionMap = jcomponent.getActionMap();
int modifiers;
if (OS.isMac()) {
modifiers = Event.META_MASK;
} else {
modifiers = Event.CTRL_MASK;
}
final KeyStroke undoKey = KeyStroke.getKeyStroke(KeyEvent.VK_Z, modifiers);
final RunnableAction undoAction = new RunnableAction("Undo", this::undo);
actionMap.put("undo", undoAction);
inputMap.put(undoKey, "undo");
final KeyStroke redoKey = KeyStroke.getKeyStroke(KeyEvent.VK_Y, modifiers);
final RunnableAction redoAction = new RunnableAction("Redo", this::redo);
actionMap.put("redo", redoAction);
inputMap.put(redoKey, "redo");
}
}
@Override
public synchronized void discardAllEdits() {
super.discardAllEdits();
fireEvents();
}
protected synchronized void fireEvents() {
final boolean canUndo = isCanUndo();
final boolean canRedo = isCanRedo();
this.propertyChangeSupport.firePropertyChange("canUndo", !canUndo, canUndo);
this.propertyChangeSupport.firePropertyChange("canRedo", !canRedo, canRedo);
}
@Override
public synchronized PropertyChangeSupport getPropertyChangeSupport() {
return this.propertyChangeSupport;
}
public synchronized boolean isCanRedo() {
return canRedo();
}
public synchronized boolean isCanUndo() {
return canUndo();
}
public boolean isEventsEnabled() {
return this.eventsEnabled.isTrue();
}
@Override
public synchronized void redo() throws CannotRedoException {
try (
BaseCloseable c = setEventsEnabled(false)) {
if (canRedo()) {
super.redo();
}
} finally {
fireEvents();
}
}
public BaseCloseable setEventsEnabled(final boolean eventsEnabled) {
return this.eventsEnabled.closeable(eventsEnabled);
}
@Override
public synchronized void undo() throws CannotUndoException {
try (
BaseCloseable c = setEventsEnabled(false)) {
if (canUndo()) {
super.undo();
}
} finally {
fireEvents();
}
}
}