001/*
002
003Copyright (c) 2011-2016 The Regents of the University of California.
004All rights reserved.
005
006Permission is hereby granted, without written agreement and without
007license or royalty fees, to use, copy, modify, and distribute this
008software and its documentation for any purpose, provided that the above
009copyright notice and the following two paragraphs appear in all copies
010of this software.
011
012IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA LIABLE TO ANY PARTY
013FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016SUCH DAMAGE.
017
018THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023ENHANCEMENTS, OR MODIFICATIONS.
024
025PT_COPYRIGHT_VERSION_2
026COPYRIGHTENDKEY
027 */
028package ptolemy.vergil.basic.layout.kieler;
029
030import java.util.HashSet;
031import java.util.LinkedList;
032import java.util.List;
033import java.util.Set;
034
035import ptolemy.data.DoubleToken;
036import ptolemy.data.expr.Parameter;
037import ptolemy.kernel.undo.UndoAction;
038import ptolemy.kernel.undo.UndoStackAttribute;
039import ptolemy.kernel.util.NamedObj;
040import ptolemy.vergil.actor.LayoutHint;
041import ptolemy.vergil.basic.layout.kieler.ApplyLayoutRequest.CurveEntry;
042import ptolemy.vergil.basic.layout.kieler.ApplyLayoutRequest.LocationEntry;
043
044/**
045 * An undo action that is able to revert the changes made by automatic layout, or to
046 * repeat them in the case of redo.
047 *
048 * @author Miro Spoenemann (<a href="mailto:msp@informatik.uni-kiel.de">msp</a>)
049 * @version $Id$
050 * @since Ptolemy II 10.0
051 * @Pt.ProposedRating Red (msp)
052 * @Pt.AcceptedRating Red (msp)
053 */
054public class UndoLayoutAction implements UndoAction {
055
056    /**
057     * Create an undo action for automatic layout.
058     *
059     * @param source The source object, which is typically the parent composite entity.
060     */
061    public UndoLayoutAction(NamedObj source) {
062        this._source = source;
063    }
064
065    ///////////////////////////////////////////////////////////////////
066    ////                         public methods                    ////
067
068    /**
069     * Add a connection.
070     * @param container The container in which to add the connection
071     * @param layoutHint to be added.
072     */
073    public void addConnection(NamedObj container, LayoutHint layoutHint) {
074        _connAddEntries.add(new ConnectionHintEntry(container, layoutHint));
075    }
076
077    /**
078     * Add a curve to the undo action. The action will set the exit angle to the
079     * value stored in the given curve entry.
080     *
081     * @param entry A curve entry with stored exit value
082     */
083    public void addCurve(CurveEntry entry) {
084        _curveEntries.add(entry);
085    }
086
087    /**
088     * Add a location to the undo action. The action will set the location to the
089     * coordinates stored in the given location entry.
090     *
091     * @param entry A location entry with all required data
092     */
093    public void addLocation(LocationEntry entry) {
094        _locationEntries.add(entry);
095    }
096
097    /**
098     * Execute the undo or redo action. This sets all previously configured locations,
099     * removes connection routing hints that are marked for removal, and adds
100     * connection routing hints that are marked for adding.
101     */
102    @Override
103    public void execute() throws Exception {
104        UndoLayoutAction undoLayoutAction = new UndoLayoutAction(_source);
105
106        // Process layout hints that shall be removed.
107        for (LayoutHint layoutHint : this._connRemoveEntries) {
108            NamedObj container = layoutHint.getContainer();
109            if (container != null) {
110                layoutHint.setContainer(null);
111                undoLayoutAction._connAddEntries
112                        .add(new ConnectionHintEntry(container, layoutHint));
113            }
114        }
115
116        // Process locations.
117        for (LocationEntry entry : this._locationEntries) {
118            double[] oldLoc = entry._locatable.getLocation();
119            undoLayoutAction.addLocation(
120                    new LocationEntry(entry._locatable, oldLoc[0], oldLoc[1]));
121            entry._locatable.setLocation(new double[] { entry._x, entry._y });
122        }
123
124        // Process layout hints that shall be added.
125        for (ConnectionHintEntry entry : this._connAddEntries) {
126            entry._layoutHint.setContainer(entry._container);
127            undoLayoutAction.removeConnection(entry._layoutHint);
128        }
129
130        // Process transition curves.
131        for (CurveEntry entry : this._curveEntries) {
132            Parameter exitAngleParam = entry._transition.exitAngle;
133            DoubleToken token = DoubleToken.convert(exitAngleParam.getToken());
134            undoLayoutAction.addCurve(
135                    new CurveEntry(entry._transition, token.doubleValue()));
136            exitAngleParam.setExpression(Double.toString(entry._exitAngle));
137        }
138
139        UndoStackAttribute undoInfo = UndoStackAttribute.getUndoInfo(_source);
140        undoInfo.push(undoLayoutAction);
141    }
142
143    /**
144     * Mark the given connection routing hint for removal. The action will remove
145     * the layout hint from its containing relation.
146     *
147     * @param layoutHint A connection routing hint contained in a relation
148     */
149    public void removeConnection(LayoutHint layoutHint) {
150        _connRemoveEntries.add(layoutHint);
151    }
152
153    ///////////////////////////////////////////////////////////////////
154    ////                         private variables                 ////
155
156    /** The configured locations that will be changed. */
157    private List<LocationEntry> _locationEntries = new LinkedList<LocationEntry>();
158    /** The configures curves that will be changed. */
159    private List<CurveEntry> _curveEntries = new LinkedList<CurveEntry>();
160    /** Layout hints that should be removed from their containing relations. */
161    private Set<LayoutHint> _connRemoveEntries = new HashSet<LayoutHint>();
162    /** Layout hints that should be added to relations. */
163    private List<ConnectionHintEntry> _connAddEntries = new LinkedList<ConnectionHintEntry>();
164    /** The source object, which is typically the parent composite entity. */
165    private NamedObj _source;
166
167    ///////////////////////////////////////////////////////////////////
168    ////                         inner classes                     ////
169
170    /**
171     * An entry that contains data for adding a connection routing hint.
172     */
173    private static class ConnectionHintEntry {
174        NamedObj _container;
175        LayoutHint _layoutHint;
176
177        ConnectionHintEntry(NamedObj container, LayoutHint layoutHint) {
178            this._container = container;
179            this._layoutHint = layoutHint;
180        }
181    }
182
183}