001/* An icon that renders the value of an attribute of the container.
002
003 Copyright (c) 1999-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.icon;
029
030import java.awt.Color;
031import java.awt.geom.Rectangle2D;
032import java.util.Iterator;
033
034import javax.swing.SwingConstants;
035import javax.swing.SwingUtilities;
036
037import diva.canvas.Figure;
038import diva.canvas.toolbox.BasicRectangle;
039import diva.canvas.toolbox.LabelFigure;
040import ptolemy.data.IntToken;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.InternalErrorException;
044import ptolemy.kernel.util.NameDuplicationException;
045import ptolemy.kernel.util.NamedObj;
046import ptolemy.kernel.util.Settable;
047import ptolemy.kernel.util.Workspace;
048
049///////////////////////////////////////////////////////////////////
050//// UpdatedValueIcon
051
052/**
053 An icon that displays the value of an attribute of the container,
054 updating it as the value of the value of the attribute is updated.
055 The attribute is assumed to be an instance of Settable, and its name
056 is given by the parameter <i>attributeName</i>.  The width of the
057 display is fixed, and is given by the attribute <i>displayWidth</i>,
058 which is in "n" characters.
059
060 @author Edward A. Lee
061 @version $Id$
062 @since Ptolemy II 2.0
063 @Pt.ProposedRating Yellow (eal)
064 @Pt.AcceptedRating Red (johnr)
065 */
066public class UpdatedValueIcon extends AttributeValueIcon {
067    /** Create a new icon with the given name in the given container.
068     *  The container is required to implement Settable, or an exception
069     *  will be thrown.
070     *  @param container The container for this attribute.
071     *  @param name The name of this attribute.
072     *  @exception IllegalActionException If thrown by the parent
073     *  class or while setting an attribute.
074     *  @exception NameDuplicationException If the name coincides with
075     *   an attribute already in the container.
076     */
077    public UpdatedValueIcon(NamedObj container, String name)
078            throws NameDuplicationException, IllegalActionException {
079        super(container, name);
080    }
081
082    ///////////////////////////////////////////////////////////////////
083    ////                         public methods                    ////
084
085    /** Override the base class to identify the named attribute in the
086     *  container and to attach a listener to it.
087     *  @param attribute The attribute that changed.
088     *  @exception IllegalActionException Not thrown in this class.
089     */
090    @Override
091    public void attributeChanged(Attribute attribute)
092            throws IllegalActionException {
093        if (attribute == attributeName) {
094            // If we were previously associated with an attribute,
095            // remove the listener.
096            if (_associatedAttribute != null) {
097                _associatedAttribute.removeValueListener(this);
098                _associatedAttribute = null;
099            }
100
101            NamedObj container = getContainer();
102
103            if (container != null) {
104                Attribute candidateAttribute = container
105                        .getAttribute(attributeName.getExpression());
106
107                if (candidateAttribute instanceof Settable) {
108                    _associatedAttribute = (Settable) candidateAttribute;
109                    _associatedAttribute.addValueListener(this);
110                }
111            }
112
113            _updateFigures();
114        } else {
115            super.attributeChanged(attribute);
116        }
117    }
118
119    /** Clone the object into the specified workspace. The new object is
120     *  <i>not</i> added to the directory of that workspace (you must do this
121     *  yourself if you want it there).
122     *  The result is an object with no container.
123     *  @param workspace The workspace for the cloned object.
124     *  @exception CloneNotSupportedException Not thrown in this base class
125     *  @return The new Attribute.
126     */
127    @Override
128    public Object clone(Workspace workspace) throws CloneNotSupportedException {
129        UpdatedValueIcon newObject = (UpdatedValueIcon) super.clone(workspace);
130        newObject._associatedAttribute = null;
131
132        // Find the new associated attribute for the clone.
133        try {
134            newObject.attributeChanged(newObject.attributeName);
135        } catch (IllegalActionException e) {
136            throw new InternalErrorException(e);
137        }
138
139        return newObject;
140    }
141
142    /** Create a new background figure.  This overrides the base class
143     *  to draw a fixed-width box.
144     *  @return A new figure.
145     */
146    @Override
147    public Figure createBackgroundFigure() {
148        // Measure width of a character.  Unfortunately, this
149        // requires generating a label figure that we will not use.
150        // Note that pessimistic choice of character would be "m",
151        // but this results in way too much padding normally.
152        // Thus, we use "n" and correct if necessary.
153        LabelFigure label = new LabelFigure("N", _labelFont, 1.0,
154                SwingConstants.CENTER);
155        Rectangle2D stringBounds = label.getBounds();
156
157        try {
158            int numberOfCharacters = ((IntToken) displayWidth.getToken())
159                    .intValue();
160
161            // NOTE: Padding of 10.
162            int width = (int) (stringBounds.getWidth() * numberOfCharacters
163                    + 10);
164
165            _displayString();
166            label = new LabelFigure("N", _labelFont, 1.0,
167                    SwingConstants.CENTER);
168            stringBounds = label.getBounds();
169            if (width < stringBounds.getWidth()) {
170                width = (int) stringBounds.getWidth() + 10;
171            }
172
173            return new BasicRectangle(0, 0, width, 30, Color.white, 1);
174        } catch (IllegalActionException ex) {
175            // Should not be thrown.
176            throw new InternalErrorException(ex);
177        }
178    }
179
180    /** React to the specified Settable changing by requesting a
181     *  repaint of the most recently constructed figures.
182     *  @param settable The object that has changed value.
183     */
184    @Override
185    public void valueChanged(Settable settable) {
186        if (settable == _associatedAttribute) {
187            _updateFigures();
188        } else {
189            super.valueChanged(settable);
190        }
191    }
192
193    ///////////////////////////////////////////////////////////////////
194    ////                         protected methods                 ////
195
196    /** Update the figures that were created by this icon to reflect the
197     *  new attribute value.  This method is called by this class in response
198     *  to notification that attributes have changed.
199     */
200    protected void _updateFigures() {
201        // Invoke in the swing thread.
202        // NOTE: These requests could be consolidated, so that
203        // if there is a string of them pending, only one gets
204        // executed. However, this results in jerky updates, with
205        // some values being skipped, so it seems like it's not
206        // a good idea.
207        SwingUtilities.invokeLater(new Runnable() {
208            @Override
209            public void run() {
210                String string = _displayString();
211                synchronized (_figures) {
212                    Iterator figures = _liveFigureIterator();
213
214                    while (figures.hasNext()) {
215                        LabelFigure figure = (LabelFigure) figures.next();
216                        figure.setString(string);
217                    }
218                }
219            }
220        });
221    }
222
223    ///////////////////////////////////////////////////////////////////
224    ////                         protected members                 ////
225
226    /** The attribute whose value is being represented. */
227    protected Settable _associatedAttribute;
228}