001/* An abstract attribute with a reference to a shape.
002
003 Copyright (c) 2003-2016 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.vergil.kernel.attributes;
029
030import java.awt.Color;
031import java.awt.Shape;
032import java.awt.geom.Rectangle2D;
033
034import ptolemy.actor.gui.ColorAttribute;
035import ptolemy.data.ArrayToken;
036import ptolemy.data.DoubleToken;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.expr.Variable;
039import ptolemy.data.type.ArrayType;
040import ptolemy.data.type.BaseType;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.Location;
044import ptolemy.kernel.util.NameDuplicationException;
045import ptolemy.kernel.util.NamedObj;
046import ptolemy.kernel.util.Workspace;
047import ptolemy.vergil.icon.ShapeIcon;
048
049///////////////////////////////////////////////////////////////////
050//// ShapeAttribute
051
052/**
053 This is an abstract attribute that is rendered as a shape.
054 This base class provides support for a line width and a line color.
055 The line color can be "none", in which case no line is drawn.
056 Concrete derived classes provide particular shapes.
057 <p>
058 @author Edward A. Lee
059 @version $Id$
060 @since Ptolemy II 4.0
061 @Pt.ProposedRating Yellow (eal)
062 @Pt.AcceptedRating Red (cxh)
063 */
064public abstract class ShapeAttribute extends VisibleAttribute {
065    /** Construct an attribute with the given name contained by the
066     *  specified container. The container argument must not be null, or a
067     *  NullPointerException will be thrown.  This attribute will use the
068     *  workspace of the container for synchronization and version counts.
069     *  If the name argument is null, then the name is set to the empty
070     *  string. Increment the version of the workspace.
071     *  @param container The container.
072     *  @param name The name of this attribute.
073     *  @exception IllegalActionException If the attribute is not of an
074     *   acceptable class for the container, or if the name contains a period.
075     *  @exception NameDuplicationException If the name coincides with
076     *   an attribute already in the container.
077     */
078    public ShapeAttribute(NamedObj container, String name)
079            throws IllegalActionException, NameDuplicationException {
080        super(container, name);
081
082        _icon = new ShapeIcon(this, "_icon", _getDefaultShape());
083        _icon.setPersistent(false);
084
085        // Create a location because, for some mysterious reason,
086        // if there isn't one, then the icon is not shown in the
087        // icon editor.
088        new Location(this, "_location");
089
090        // This must appear before lineColor or cloning could
091        // fail if lineColor references it.
092        _none = new Variable(this, "none");
093        _none.setExpression("{1.0, 1.0, 1.0, 0.0}");
094
095        lineWidth = new Parameter(this, "lineWidth");
096        lineWidth.setTypeEquals(BaseType.DOUBLE);
097        lineWidth.setExpression("1.0");
098
099        lineColor = new ColorAttribute(this, "lineColor");
100        lineColor.setExpression("{0.0, 0.0, 0.0, 1.0}");
101
102        dashArray = new Parameter(this, "dashArray");
103        dashArray.setTypeEquals(new ArrayType(BaseType.DOUBLE));
104
105        rotation = new Parameter(this, "rotation");
106        rotation.setTypeEquals(BaseType.DOUBLE);
107        rotation.setExpression("0.0");
108    }
109
110    ///////////////////////////////////////////////////////////////////
111    ////                         parameters                        ////
112
113    /** Specification of the dash pattern for dashed or dotted lines.
114     *  An empty value indicates that the line should not be dashed.
115     *  The values specify the length of solid and transparent segments
116     *  in an alternating fashion.
117     *  This is an array of doubles that by default is empty.
118     */
119    public Parameter dashArray;
120
121    /** The line color.  This is a string representing an array with
122     *  four elements, red, green, blue, and alpha, where alpha is
123     *  transparency. The default is "{0.0, 0.0, 0.0, 1.0}", which
124     *  represents an opaque black.
125     */
126    public ColorAttribute lineColor;
127
128    /** The line width.  This is a double that defaults to 1.0.
129     */
130    public Parameter lineWidth;
131
132    /** The angle of rotation in radians. This is a double that
133     *  defaults to 0.0.
134     */
135    public Parameter rotation;
136
137    ///////////////////////////////////////////////////////////////////
138    ////                         public methods                    ////
139
140    /** React to a changes in the attributes by changing
141     *  the icon.
142     *  @param attribute The attribute that changed.
143     *  @exception IllegalActionException If the change is not acceptable
144     *   to this container (should not be thrown).
145     */
146    @Override
147    public void attributeChanged(Attribute attribute)
148            throws IllegalActionException {
149        if (attribute == lineWidth) {
150            double lineWidthValue = ((DoubleToken) lineWidth.getToken())
151                    .doubleValue();
152            _icon.setLineWidth((float) lineWidthValue);
153        } else if (attribute == dashArray) {
154            ArrayToken value = (ArrayToken) dashArray.getToken();
155
156            if (value == null || value.length() == 0) {
157                _icon.setDashArray(null);
158            } else {
159                float[] floatValue = new float[value.length()];
160
161                for (int i = 0; i < value.length(); i++) {
162                    floatValue[i] = (float) ((DoubleToken) value.getElement(i))
163                            .doubleValue();
164                }
165
166                _icon.setDashArray(floatValue);
167            }
168        } else if (attribute == lineColor) {
169            Color lineColorValue = lineColor.asColor();
170
171            if (lineColorValue.getAlpha() == 0f) {
172                // Color is fully transparent, so no line is desired.
173                _icon.setLineColor(null);
174            } else {
175                _icon.setLineColor(lineColorValue);
176            }
177        } else if (attribute == rotation) {
178            double angle = ((DoubleToken) rotation.getToken()).doubleValue();
179            _icon.setRotation(angle);
180        } else {
181            super.attributeChanged(attribute);
182        }
183    }
184
185    /** Clone the object into the specified workspace. The new object is
186     *  <i>not</i> added to the directory of that workspace (you must do this
187     *  yourself if you want it there).
188     *  The result is an object with no container.
189     *  @param workspace The workspace for the cloned object.
190     *  @exception CloneNotSupportedException Not thrown in this base class
191     *  @return The new Attribute.
192     */
193    @Override
194    public Object clone(Workspace workspace) throws CloneNotSupportedException {
195        ShapeAttribute newObject = (ShapeAttribute) super.clone(workspace);
196
197        // The base class clones the icon, but since this is a protected
198        // member, it doesn't automatically get updated by NamedObj!
199        newObject._icon = (ShapeIcon) newObject.getAttribute("_icon");
200        newObject._none = (Variable) newObject.getAttribute("_none");
201        return newObject;
202    }
203
204    ///////////////////////////////////////////////////////////////////
205    ////                         protected methods                 ////
206
207    /** Return the default shape to use for this icon.
208     *  Note that this is called in the constructor, so if you override
209     *  it in derived classed, you cannot access any methods or members
210     *  of the derived class because they will not have been constructed.
211     *  @return The default shape for this attribute.
212     */
213    protected Shape _getDefaultShape() {
214        // NOTE: In an ideal world, this would not be necessary, because
215        // setShape() would override the default shape.  Unfortunately,
216        // without this, icons are rendered before setShape() has been called,
217        // and consequently, all shape attributes are rendered the same in
218        // the utilities library.
219        return new Rectangle2D.Double(0.0, 0.0, 20.0, 20.0);
220    }
221
222    ///////////////////////////////////////////////////////////////////
223    ////                       protected members                   ////
224
225    /** The shape icon. */
226    protected ShapeIcon _icon;
227
228    /** A color parameter whose value is a fully transparent white
229     *  (alpha = 0.0), which is interpreted as no color.
230     */
231    protected Variable _none;
232}