UndoRedo on Nodes
- by Geertjan
When a change is made to the property in the Properties Window, below, the undo/redo functionality becomes enabled:
When undo/redo are invoked, e.g., via the buttons in the toolbar, the display name of the node changes accordingly. The only problem I have is that the buttons only become enabled when the Person Window is selected, not when the Properties Window is selected, which would be desirable.
Here's the Person object:
public class Person implements PropertyChangeListener {
private String name;
public static final String PROP_NAME = "name";
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
String oldName = this.name;
this.name = name;
propertyChangeSupport.firePropertyChange(PROP_NAME, oldName, name);
}
private transient final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
propertyChangeSupport.firePropertyChange(evt);
}
}
And here's the Node with UndoRedo enablement:
public class PersonNode extends AbstractNode implements UndoRedo.Provider, PropertyChangeListener {
private UndoRedo.Manager manager = new UndoRedo.Manager();
private boolean undoRedoEvent;
public PersonNode(Person person) {
super(Children.LEAF, Lookups.singleton(person));
person.addPropertyChangeListener(this);
setDisplayName(person.getName());
}
@Override
protected Sheet createSheet() {
Sheet sheet = Sheet.createDefault();
Sheet.Set set = Sheet.createPropertiesSet();
set.put(new NameProperty(getLookup().lookup(Person.class)));
sheet.put(set);
return sheet;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Person.PROP_NAME)) {
firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
}
public void fireUndoableEvent(String property, Person source, Object oldValue, Object newValue) {
manager.addEdit(new MyAbstractUndoableEdit(source, oldValue, newValue));
}
@Override
public UndoRedo getUndoRedo() {
return manager;
}
@Override
public String getDisplayName() {
Person p = getLookup().lookup(Person.class);
if (p != null) {
return p.getName();
}
return super.getDisplayName();
}
private class NameProperty extends PropertySupport.ReadWrite<String> {
private Person p;
public NameProperty(Person p) {
super("name", String.class, "Name", "Name of Person");
this.p = p;
}
@Override
public String getValue() throws IllegalAccessException, InvocationTargetException {
return p.getName();
}
@Override
public void setValue(String newValue) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String oldValue = p.getName();
p.setName(newValue);
if (!undoRedoEvent) {
fireUndoableEvent("name", p, oldValue, newValue);
fireDisplayNameChange(oldValue, newValue);
}
}
}
class MyAbstractUndoableEdit extends AbstractUndoableEdit {
private final String oldValue;
private final String newValue;
private final Person source;
private MyAbstractUndoableEdit(Person source, Object oldValue, Object newValue) {
this.oldValue = oldValue.toString();
this.newValue = newValue.toString();
this.source = source;
}
@Override
public boolean canRedo() {
return true;
}
@Override
public boolean canUndo() {
return true;
}
@Override
public void undo() throws CannotUndoException {
undoRedoEvent = true;
source.setName(oldValue.toString());
fireDisplayNameChange(oldValue, newValue);
undoRedoEvent = false;
}
@Override
public void redo() throws CannotUndoException {
undoRedoEvent = true;
source.setName(newValue.toString());
fireDisplayNameChange(oldValue, newValue);
undoRedoEvent = false;
}
}
}
Does anyone out there know how to have the Undo/Redo functionality enabled when the Properties Window is selected?