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.awt.geom.Point2D;
031import java.util.LinkedList;
032import java.util.List;
033
034import ptolemy.data.DoubleToken;
035import ptolemy.data.expr.Parameter;
036import ptolemy.domains.modal.kernel.Transition;
037import ptolemy.kernel.Relation;
038import ptolemy.kernel.undo.UndoStackAttribute;
039import ptolemy.kernel.util.Attribute;
040import ptolemy.kernel.util.ChangeRequest;
041import ptolemy.kernel.util.Locatable;
042import ptolemy.kernel.util.NamedObj;
043import ptolemy.vergil.actor.LayoutHint;
044
045/**
046 * A change request specialized for application of automatically computed layout.
047 * This is used to set new locations for graph elements and to set layout hints
048 * for connection routing.
049 *
050 * @author Miro Spoenemann (<a href="mailto:msp@informatik.uni-kiel.de">msp</a>)
051 * @version $Id$
052 * @since Ptolemy II 10.0
053 * @Pt.ProposedRating Red (msp)
054 * @Pt.AcceptedRating Red (msp)
055 */
056public class ApplyLayoutRequest extends ChangeRequest {
057
058    /**
059     * Create a request for applying layout.
060     *
061     * @param source The source object, which is typically the parent composite actor.
062     */
063    public ApplyLayoutRequest(Object source) {
064        super(source, "KIELER Automatic Layout", true);
065    }
066
067    ///////////////////////////////////////////////////////////////////
068    ////                         public methods                    ////
069
070    /**
071     * Add a new location change to the request.
072     *
073     * @param locatable The locatable that will be changed
074     * @param x The new x coordinate
075     * @param y The new y coordinate
076     */
077    public void addLocation(Locatable locatable, double x, double y) {
078        _locationEntries.add(new LocationEntry(locatable, x, y));
079    }
080
081    /**
082     * Add a new connection routing change to the request.
083     *
084     * @param relation The relation that owns the connection.
085     * @param head The head object of the connection.
086     * @param tail The tail object of the connection.
087     * @param bendPoints The new bend points.
088     */
089    public void addConnection(Relation relation, NamedObj head, NamedObj tail,
090            double[] bendPoints) {
091        _connectionEntries
092                .add(new ConnectionEntry(relation, head, tail, bendPoints));
093    }
094
095    /**
096     * Add a new connection routing change to the request.
097     *
098     * @param relation The relation that owns the connection.
099     * @param head The head object of the connection.
100     * @param tail The tail object of the connection.
101     * @param bendPoints The new bend points.
102     * @param labelLocation The location of a label, may be null
103     */
104    public void addConnection(Relation relation, NamedObj head, NamedObj tail,
105            double[] bendPoints, Point2D.Double labelLocation) {
106        _connectionEntries.add(new ConnectionEntry(relation, head, tail,
107                bendPoints, labelLocation));
108    }
109
110    /**
111     * Add a new transition curve change to the request.
112     *
113     * @param transition The transition that is represented by the curve.
114     * @param exitAngle The new value for the exit angle.
115     */
116    public void addCurve(Transition transition, double exitAngle) {
117        _curveEntries.add(new CurveEntry(transition, exitAngle));
118    }
119
120    ///////////////////////////////////////////////////////////////////
121    ////                         protected methods                 ////
122
123    /**
124     * Execute the request. This sets the previously configured locations and connections.
125     */
126    @Override
127    protected void _execute() throws Exception {
128        NamedObj source = (NamedObj) getSource();
129        UndoLayoutAction undoLayoutAction = new UndoLayoutAction(source);
130
131        // Process locations.
132        for (LocationEntry entry : _locationEntries) {
133            double[] oldLoc = entry._locatable.getLocation();
134            undoLayoutAction.addLocation(
135                    new LocationEntry(entry._locatable, oldLoc[0], oldLoc[1]));
136            entry._locatable.setLocation(new double[] { entry._x, entry._y });
137        }
138
139        // Process connection routings.
140        for (ConnectionEntry entry : _connectionEntries) {
141            Attribute attribute = entry._relation.getAttribute("_layoutHint");
142            if (attribute == null) {
143                attribute = new LayoutHint(entry._relation, "_layoutHint");
144            }
145            if (attribute instanceof LayoutHint) {
146                LayoutHint layoutHint = (LayoutHint) attribute;
147                layoutHint.setLayoutHintItem(entry._head, entry._tail,
148                        entry._bendPoints, entry._labelLocation);
149                undoLayoutAction.removeConnection(layoutHint);
150            }
151        }
152
153        // Process transition curves.
154        for (CurveEntry entry : _curveEntries) {
155            Parameter exitAngleParam = entry._transition.exitAngle;
156            DoubleToken token = DoubleToken.convert(exitAngleParam.getToken());
157            undoLayoutAction.addCurve(
158                    new CurveEntry(entry._transition, token.doubleValue()));
159            exitAngleParam.setExpression(Double.toString(entry._exitAngle));
160
161            Attribute attribute = entry._transition.getAttribute("_layoutHint");
162            if (attribute != null) {
163                // FIXME undo from arcs to splines does not work
164                // the interpretation of bendpoints as splines is only performed
165                // when the parameter 'useSplines' is set. We would have to change
166                // the parameter in the undo action as well to make it work.
167                // LayoutHint layoutHint = (LayoutHint) attribute;
168                // undoLayoutAction.addConnection(entry._transition, layoutHint);
169                entry._transition.removeAttribute(attribute);
170            }
171        }
172
173        UndoStackAttribute undoInfo = UndoStackAttribute.getUndoInfo(source);
174        undoInfo.push(undoLayoutAction);
175    }
176
177    ///////////////////////////////////////////////////////////////////
178    ////                         private variables                 ////
179
180    /** The configured locations that will be changed. */
181    private List<LocationEntry> _locationEntries = new LinkedList<LocationEntry>();
182    /** The configured connection routings that will be changed. */
183    private List<ConnectionEntry> _connectionEntries = new LinkedList<ConnectionEntry>();
184    /** The configures curves that will be changed. */
185    private List<CurveEntry> _curveEntries = new LinkedList<CurveEntry>();
186
187    ///////////////////////////////////////////////////////////////////
188    ////                         inner classes                     ////
189
190    /**
191     * An entry that contains data for changing a location.
192     */
193    static class LocationEntry {
194        Locatable _locatable;
195        double _x;
196        double _y;
197
198        LocationEntry(Locatable locatable, double x, double y) {
199            this._locatable = locatable;
200            this._x = x;
201            this._y = y;
202        }
203    }
204
205    /**
206     * An entry that contains data for changing a connection using bend points.
207     */
208    static class ConnectionEntry {
209        Relation _relation;
210        NamedObj _head;
211        NamedObj _tail;
212        double[] _bendPoints;
213        Point2D.Double _labelLocation;
214
215        ConnectionEntry(Relation relation, NamedObj head, NamedObj tail,
216                double[] bendPoints, Point2D.Double labelLocation) {
217            this._relation = relation;
218            this._head = head;
219            this._tail = tail;
220            this._bendPoints = bendPoints;
221            this._labelLocation = labelLocation;
222        }
223
224        ConnectionEntry(Relation relation, NamedObj head, NamedObj tail,
225                double[] bendPoints) {
226            this(relation, head, tail, bendPoints, null);
227        }
228    }
229
230    /**
231     * An entry that contains data for changing a curve using angular values.
232     */
233    static class CurveEntry {
234        Transition _transition;
235        double _exitAngle;
236
237        CurveEntry(Transition transition, double exitAngle) {
238            this._transition = transition;
239            this._exitAngle = exitAngle;
240        }
241    }
242
243}