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}