Synchronized Property Changes (Part 4)

Posted by Geertjan on Oracle Blogs See other posts from Oracle Blogs or by Geertjan
Published on Sun, 19 Jun 2011 00:50:37 -0700 Indexed on 2011/06/20 16:32 UTC
Read the original article Hit count: 206

Filed under:

The next step is to activate the undo/redo functionality... for a Node. Something I've not seen done before. I.e., when the Node is renamed via F2 on the Node, the "Undo/Redo" buttons should start working.

Here is the start of the solution, via this item in the mailing list and Timon Veenstra's BeanNode class, note especially the items in bold:

public class ShipNode extends BeanNode
        implements PropertyChangeListener, UndoRedo.Provider {

    private final InstanceContent ic;
    private final ShipSaveCapability saveCookie;
    private UndoRedo.Manager manager;

    private String oldDisplayName;
    private String newDisplayName;
    private Ship ship;
    
    public ShipNode(Ship bean) throws IntrospectionException {
        this(bean, new InstanceContent());
    }

    private ShipNode(Ship bean, InstanceContent ic) throws IntrospectionException {
        super(bean, Children.LEAF, new ProxyLookup(new AbstractLookup(ic), Lookups.singleton(bean)));
        this.ic = ic;
        setDisplayName(bean.getType());
        setShortDescription(String.valueOf(bean.getYear()));
        saveCookie = new ShipSaveCapability(bean);
        bean.addPropertyChangeListener(WeakListeners.propertyChange(this, bean));
    }

    @Override
    public Action[] getActions(boolean context) {
        List<? extends Action> shipActions =
                Utilities.actionsForPath("Actions/Ship");
        return shipActions.toArray(new Action[shipActions.size()]);
    }

    protected void fire(boolean modified) {
        if (modified) {
            ic.add(saveCookie);
        } else {
            ic.remove(saveCookie);
        }
    }

    @Override
    public UndoRedo getUndoRedo() {
        manager = Lookup.getDefault().lookup(
                UndoRedo.Manager.class);
        return manager;
    }
    
    private class ShipSaveCapability implements SaveCookie {
        private final Ship bean;
        public ShipSaveCapability(Ship bean) {
            this.bean = bean;
        }
        @Override
        public void save() throws IOException {
            StatusDisplayer.getDefault().setStatusText("Saving...");
            fire(false);
        }
    }

    @Override
    public boolean canRename() {
        return true;
    }

    @Override
    public void setName(String newDisplayName) {
        Ship c = getLookup().lookup(Ship.class);
        oldDisplayName = c.getType();
        c.setType(newDisplayName);
        fireNameChange(oldDisplayName, newDisplayName);
        fire(true);
        fireUndoableEvent("type", ship, oldDisplayName, newDisplayName);
    }

    public void fireUndoableEvent(String property, Ship source, Object oldValue, Object newValue) {
        ReUndoableEdit reUndoableEdit = new ReUndoableEdit(
                property, source, oldValue, newValue);
        UndoableEditEvent undoableEditEvent = new UndoableEditEvent(
                this, reUndoableEdit);
        manager.undoableEditHappened(undoableEditEvent);
    }

    private class ReUndoableEdit extends AbstractUndoableEdit {
        private Object oldValue;
        private Object newValue;
        private Ship source;
        private String property;
        public ReUndoableEdit(String property, Ship source, Object oldValue, Object newValue) {
            super();
            this.oldValue = oldValue;
            this.newValue = newValue;
            this.source = source;
            this.property = property;
        }
        @Override
        public void undo() throws CannotUndoException {
            setName(oldValue.toString());
        }
        @Override
        public void redo() throws CannotRedoException {
            setName(newValue.toString());
        }
    }

    @Override
    public String getDisplayName() {
        Ship c = getLookup().lookup(Ship.class);
        if (null != c.getType()) {
            return c.getType();
        }
        return super.getDisplayName();
    }

    @Override
    public String getShortDescription() {
        Ship c = getLookup().lookup(Ship.class);
        if (null != String.valueOf(c.getYear())) {
            return String.valueOf(c.getYear());
        }
        return super.getShortDescription();
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals("type")) {
            String oldDisplayName = evt.getOldValue().toString();
            String newDisplayName = evt.getNewValue().toString();
            fireDisplayNameChange(oldDisplayName, newDisplayName);
        } else if (evt.getPropertyName().equals("year")) {
            String oldToolTip = evt.getOldValue().toString();
            String newToolTip = evt.getNewValue().toString();
            fireShortDescriptionChange(oldToolTip, newToolTip);
        }
        fire(true);
    }
    
}

Undo works when rename is done, but Redo never does, because Undo is constantly activated, since it is reactivated whenever there is a name change. And why must the UndoRedoManager be retrieved from the Lookup (it doesn't work otherwise)? Don't get that part of the code either. Help welcome!

© Oracle Blogs or respective owner

Related posts about /NetBeans IDE