001/* A parameter with type double with a limited range.
002
003 Copyright (c) 2004-2014 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.actor.parameters;
029
030import ptolemy.data.DoubleToken;
031import ptolemy.data.Token;
032import ptolemy.data.expr.Parameter;
033import ptolemy.data.expr.StringParameter;
034import ptolemy.data.type.BaseType;
035import ptolemy.kernel.util.Attribute;
036import ptolemy.kernel.util.IllegalActionException;
037import ptolemy.kernel.util.NameDuplicationException;
038import ptolemy.kernel.util.NamedObj;
039
040///////////////////////////////////////////////////////////////////
041//// DoubleRangeParameter
042
043/**
044 This is a parameter with type double with a limited range and
045 limited precision.
046 Its value is an double token that is constrained to lie
047 within the boundaries specified by its two parameters,
048 <i>min</i> and <i>max</i>, inclusive.  Moreover, the values
049 are quantized so that there are exactly <i>precision</i>
050 values, uniformly spaced in the range.
051 A user interface will typically use this
052 information to represent the parameter value using a slider
053 which can be decorated by labels indicating the minimum and
054 maximum values. The actual text displayed by the labels can
055 be set using the <i>minLabel</i> and <i>maxLabel</i> parameters
056 which default to showing the actual minimum and maximum double value.
057 The default values for <i>min</i> and <i>max</i> are 0.0 and 1.0,
058 with a default value of 0.5.  The default <i>precision</i> is 100.
059 <p>
060 @author Edward A. Lee, Christoph Daniel Schulze
061 @version $Id$
062 @since Ptolemy II 4.1
063 @Pt.ProposedRating Yellow (eal)
064 @Pt.AcceptedRating Red (cxh)
065 */
066public class DoubleRangeParameter extends Parameter {
067    /** Construct an attribute with the given name contained by the
068     *  specified container. The container argument must not be null, or a
069     *  NullPointerException will be thrown.  This attribute will use the
070     *  workspace of the container for synchronization and version counts.
071     *  If the name argument is null, then the name is set to the empty
072     *  string. Increment the version of the workspace.
073     *  @param container The container.
074     *  @param name The name of this attribute.
075     *  @exception IllegalActionException If the attribute is not of an
076     *   acceptable class for the container, or if the name contains a period.
077     *  @exception NameDuplicationException If the name coincides with
078     *   an attribute already in the container.
079     */
080    public DoubleRangeParameter(NamedObj container, String name)
081            throws IllegalActionException, NameDuplicationException {
082        super(container, name);
083
084        min = new Parameter(this, "min");
085        min.setExpression("0.0");
086        min.setTypeEquals(BaseType.DOUBLE);
087
088        max = new Parameter(this, "max");
089        max.setExpression("1.0");
090        max.setTypeEquals(BaseType.DOUBLE);
091
092        precision = new Parameter(this, "precision");
093        precision.setExpression("100");
094        precision.setTypeEquals(BaseType.INT);
095
096        minLabel = new StringParameter(this, "minLabel");
097        minLabel.setExpression("$min");
098
099        maxLabel = new StringParameter(this, "maxLabel");
100        maxLabel.setExpression("$max");
101
102        // We can't set a default value because then
103        // restore defaults will restore to this value,
104        // but this may be out of range for a particular
105        // application.
106        // setExpression("0.5");
107        setTypeEquals(BaseType.DOUBLE);
108
109        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-30\" y=\"-2\" "
110                + "width=\"60\" height=\"4\" " + "style=\"fill:white\"/>\n"
111                + "<rect x=\"15\" y=\"-10\" " + "width=\"4\" height=\"20\" "
112                + "style=\"fill:grey\"/>\n" + "</svg>\n");
113    }
114
115    ///////////////////////////////////////////////////////////////////
116    ////                         parameters                        ////
117
118    /** The maximum value. This is has a double value, and defaults to 1.0. */
119    public Parameter max;
120
121    /** The minimum value. This is has a double value, and defaults to 0.0. */
122    public Parameter min;
123
124    /** The precision, which is the number of possible values.
125     *  This is has an integer value, and defaults to 100.
126     */
127    public Parameter precision;
128
129    /** The label text displayed for the maximum end of the slider. This is a String,
130     *  and defaults to {@code $max}, which is expanded to the value of the {@code max}
131     *  parameter in the user interface.
132     */
133    public StringParameter maxLabel;
134
135    /** The label text displayed for the minimum end of the slider. This is a String,
136     *  and defaults to {@code $min}, which is expanded to the value of the {@code min}
137     *  parameter in the user interface.
138     */
139    public StringParameter minLabel;
140
141    ///////////////////////////////////////////////////////////////////
142    ////                         public methods                    ////
143
144    /** React to a change in an attribute by ensuring that the current
145     *  value remains within the range given by <i>min</i> and <i>max</i>.
146     *  @param attribute The attribute that changed.
147     *  @exception IllegalActionException If the change is not acceptable
148     *   to this container (should not be thrown).
149     */
150    @Override
151    public void attributeChanged(Attribute attribute)
152            throws IllegalActionException {
153        if (attribute == max && !_inCheck) {
154            try {
155                _inCheck = true;
156
157                double maxValue = ((DoubleToken) max.getToken()).doubleValue();
158                double value = ((DoubleToken) getToken()).doubleValue();
159
160                if (value > maxValue) {
161                    setToken(max.getToken());
162                }
163            } finally {
164                _inCheck = false;
165            }
166        } else if (attribute == min && !_inCheck) {
167            try {
168                _inCheck = true;
169
170                double minValue = ((DoubleToken) min.getToken()).doubleValue();
171                double value = ((DoubleToken) getToken()).doubleValue();
172
173                if (value < minValue) {
174                    setToken(min.getToken());
175                }
176            } finally {
177                _inCheck = false;
178            }
179        } else {
180            super.attributeChanged(attribute);
181        }
182    }
183
184    ///////////////////////////////////////////////////////////////////
185    ////                         protected methods                 ////
186
187    /*  Set the token value and type of the variable, and notify the
188     *  container that the value (and type, if appropriate) has changed.
189     *  Also notify value dependents that they need to be re-evaluated,
190     *  and notify any listeners that have been registered with
191     *  addValueListener().
192     *  If setTypeEquals() has been called, then attempt to convert
193     *  the specified token into one of the appropriate type, if needed,
194     *  rather than changing the type.
195     *  @param newToken The new value of the variable.
196     *  @exception IllegalActionException If the token is not an IntToken
197     *   or its value is out of range.
198     */
199    @Override
200    protected void _setTokenAndNotify(Token newToken)
201            throws IllegalActionException {
202        if (_inCheck) {
203            super._setTokenAndNotify(newToken);
204            return;
205        }
206
207        if (newToken instanceof DoubleToken) {
208            try {
209                _inCheck = true;
210
211                double minValue = ((DoubleToken) min.getToken()).doubleValue();
212                double maxValue = ((DoubleToken) max.getToken()).doubleValue();
213                double currentValue = ((DoubleToken) newToken).doubleValue();
214
215                if (minValue <= currentValue && currentValue <= maxValue) {
216                    // All is OK.
217                    super._setTokenAndNotify(newToken);
218                    return;
219                }
220
221                throw new IllegalActionException(this,
222                        "Value is required to lie between " + min + " and "
223                                + max + ".");
224            } finally {
225                _inCheck = false;
226            }
227        }
228
229        throw new IllegalActionException(this,
230                "Value is required to be a double token.");
231    }
232
233    ///////////////////////////////////////////////////////////////////
234    ////                         private variables                 ////
235
236    /** Indicator that we are in the middle of a check, so skip
237     *  circular dependency.
238     */
239    private boolean _inCheck = false;
240}