001/** An attribute used to store a relative visual location. */ 002/* 003Below is the copyright agreement for the Ptolemy II system. 004 005Copyright (c) 1995-2018 The Regents of the University of California. 006All rights reserved. 007 008Permission is hereby granted, without written agreement and without 009license or royalty fees, to use, copy, modify, and distribute this 010software and its documentation for any purpose, provided that the above 011copyright notice and the following two paragraphs appear in all copies 012of this software. 013 014IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 015FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 016ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 017THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 018SUCH DAMAGE. 019 020THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 021INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 022MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 023PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 024CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 025ENHANCEMENTS, OR MODIFICATIONS. 026 027Ptolemy II includes the work of others, to see those copyrights, follow 028the copyright link on the splash page or see copyright.htm. 029 */ 030 031package ptolemy.vergil.basic; 032 033import java.util.List; 034 035import ptolemy.kernel.CompositeEntity; 036import ptolemy.kernel.util.IllegalActionException; 037import ptolemy.kernel.util.InternalErrorException; 038import ptolemy.kernel.util.Locatable; 039import ptolemy.kernel.util.Location; 040import ptolemy.kernel.util.NameDuplicationException; 041import ptolemy.kernel.util.NamedObj; 042import ptolemy.kernel.util.Settable; 043import ptolemy.kernel.util.StringAttribute; 044 045/** An attribute used to store a relative visual location. 046 * The location is relative to an object specified by 047 * the <i>relativeTo</i> attribute, which gives the name 048 * of an object that is expected to be contained by the 049 * container of the container of this attribute. 050 * In addition, the <i>relativeToElementName</i> specifies 051 * what kind of object this is relative to (an "entity", 052 * "property" (attribute), "port", or "relation"). 053 * 054 @author Edward A. Lee, Christian Motika, Miro Spoenemann 055 @version $Id$ 056 @since Ptolemy II 11.0 057 @Pt.ProposedRating Yellow (cxh) 058 @Pt.AcceptedRating Red (cxh) 059 */ 060public class RelativeLocation extends Location { 061 062 /** Construct an instance. 063 * @param container The container (the object to have a relative location). 064 * @param name The name of this instance. 065 * @exception IllegalActionException If the superclass throws it. 066 * @exception NameDuplicationException If the superclass throws it. 067 */ 068 public RelativeLocation(NamedObj container, String name) 069 throws IllegalActionException, NameDuplicationException { 070 super(container, name); 071 072 relativeTo = new StringAttribute(this, "relativeTo"); 073 relativeTo.setVisibility(Settable.EXPERT); 074 relativeToElementName = new StringAttribute(this, 075 "relativeToElementName"); 076 relativeToElementName.setExpression("entity"); 077 } 078 079 /////////////////////////////////////////////////////////////////// 080 //// parameters //// 081 082 /** The name of the object this location is relative to. */ 083 public StringAttribute relativeTo; 084 085 /** The element name of the object this location is relative to. 086 * This defaults to "entity". 087 */ 088 public StringAttribute relativeToElementName; 089 090 /** The initial offset for new relative locatable objects. */ 091 public static final double INITIAL_OFFSET = 40.0; 092 093 /** The maximal distance of the relative location. If this is exceeded 094 * after moving the relative locatable, the link is broken (see 095 * {@link LocatableNodeDragInteractor#mouseReleased(diva.canvas.event.LayerEvent)}). 096 */ 097 public static final double BREAK_THRESHOLD = 300.0; 098 099 /////////////////////////////////////////////////////////////////// 100 //// public methods //// 101 102 /** Get the location in some cartesian coordinate system. 103 * This method returns the absolute location of the object. 104 * If the relative location was previously attached to an object 105 * referenced in the {@link #relativeTo} property and that object 106 * is gone, then the internally stored location is updated so it 107 * contains the correct absolute location. 108 * @return The location. 109 * @see #setLocation(double[]) 110 */ 111 @Override 112 public double[] getLocation() { 113 double[] offset = super.getLocation(); 114 NamedObj relativeToObject = getRelativeToNamedObj(); 115 if (relativeToObject == null || offset == null) { 116 return offset; 117 } 118 double[] relativeToLocation = _getRelativeToLocation(relativeToObject); 119 if (relativeToLocation != null) { 120 double[] result = new double[offset.length]; 121 for (int i = 0; i < offset.length; i++) { 122 result[i] = offset[i] + relativeToLocation[i]; 123 } 124 return result; 125 } 126 // If we get to here, then the relativeTo object is gone, so 127 // update the relative location to an absolute one if possible. 128 if (_cachedReltoLoc != null) { 129 for (int i = 0; i < offset.length; i++) { 130 // The offset array is also referenced by the superclass, so 131 // changes to its content affect the actual location. 132 offset[i] += _cachedReltoLoc[i]; 133 } 134 _cachedReltoLoc = null; 135 } 136 return offset; 137 } 138 139 /** Get the relative location, relative to the <i>relativeTo</i> 140 * object, if there is one, and otherwise return the absolute 141 * location. 142 * @return The relative location. 143 * @see #setLocation(double[]) 144 */ 145 public double[] getRelativeLocation() { 146 return super.getLocation(); 147 } 148 149 /** If the <i>relativeTo</i> object exists, return it. 150 * Otherwise, return null and clear the <i>relativeTo</i> 151 * parameter value. 152 * @return The relativeTo object, or null if it 153 * does not exist. 154 */ 155 public NamedObj getRelativeToNamedObj() { 156 String relativeToName = relativeTo.getExpression(); 157 if (relativeToName.trim().equals("")) { 158 return null; 159 } 160 NamedObj result = null; 161 NamedObj container = getContainer(); 162 if (container != null) { 163 NamedObj containersContainer = container.getContainer(); 164 if (containersContainer instanceof CompositeEntity) { 165 CompositeEntity composite = (CompositeEntity) containersContainer; 166 String elementName = relativeToElementName.getExpression(); 167 // The relativeTo object is not necessarily an Entity. 168 if (elementName.equals("property")) { 169 result = composite.getAttribute(relativeToName); 170 } else if (elementName.equals("port")) { 171 result = composite.getPort(relativeToName); 172 } else if (elementName.equals("relation")) { 173 result = composite.getRelation(relativeToName); 174 } else { 175 result = composite.getEntity(relativeToName); 176 } 177 } 178 } 179 if (result == null) { 180 // The relativeTo object could not be found, so the attributes holding 181 // the reference are no longer valid. Erase their content. 182 try { 183 relativeTo.setExpression(""); 184 relativeToElementName.setExpression(""); 185 } catch (IllegalActionException exception) { 186 throw new InternalErrorException(exception); 187 } 188 } 189 return result; 190 } 191 192 /** Set the location in some cartesian coordinate system, and notify 193 * the container and any value listeners of the new location. Setting 194 * the location involves maintaining a local copy of the passed 195 * parameter. No notification is done if the location is the same 196 * as before. This method propagates the value to any derived objects. 197 * If the relative location is attached to an object referenced in the 198 * {@link #relativeTo} property, then only the relative location is 199 * stored internally. 200 * @param location The location. 201 * @exception IllegalActionException Thrown when attributeChanged() is called. 202 * @see #getLocation() 203 */ 204 @Override 205 public void setLocation(double[] location) throws IllegalActionException { 206 NamedObj relativeToObject = getRelativeToNamedObj(); 207 if (relativeToObject == null) { 208 super.setLocation(location); 209 return; 210 } 211 double[] relativeToLocation = _getRelativeToLocation(relativeToObject); 212 if (relativeToLocation != null) { 213 double[] result = new double[location.length]; 214 for (int i = 0; i < location.length; i++) { 215 result[i] = location[i] - relativeToLocation[i]; 216 } 217 super.setLocation(result); 218 return; 219 } 220 // If we get to here, then the relativeTo object is gone, so delete 221 // the cached value. 222 _cachedReltoLoc = null; 223 super.setLocation(location); 224 } 225 226 /////////////////////////////////////////////////////////////////// 227 //// private methods //// 228 229 /** If the <i>relativeTo</i> object exists, return its location. 230 * Otherwise, return null. 231 * @param relativeToObject The relativeTo object. 232 * @return The location of the relativeTo object, or null if it 233 * does not exist. 234 */ 235 private double[] _getRelativeToLocation(NamedObj relativeToObject) { 236 List<Locatable> locatables = relativeToObject 237 .attributeList(Locatable.class); 238 if (locatables.size() > 0) { 239 _cachedReltoLoc = locatables.get(0).getLocation(); 240 return _cachedReltoLoc; 241 } 242 return null; 243 } 244 245 /////////////////////////////////////////////////////////////////// 246 //// private variables //// 247 248 /** The cached relativeTo location. This is used to restore the absolute position 249 * after the relativeTo object has been deleted. 250 */ 251 private double[] _cachedReltoLoc; 252 253}