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?