001/* A parameter with type integer with a limited range.
002
003 Copyright (c) 2001-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.IntToken;
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//// IntRangeParameter
042
043/**
044 <p>This is a parameter with type integer with a limited range.
045 Its value is an integer token that is constrained to lie
046 within the boundaries specified by its two parameters,
047 <i>min</i> and <i>max</i>.  These specify the minimum and maximum values.
048 A user interface will typically use this
049 information to represent the parameter value using a slider
050 which can be decorated by labels indicating the minimum and
051 maximum values. The actual text displayed by the labels can
052 be set using the <i>minLabel</i> and <i>maxLabel</i> parameters
053 which default to showing the actual minimum and maximum int value.
054 The default values for <i>min</i> and <i>max</i> are 0 and 100,
055 respectively, and the default value for this parameter is 50.
056 </p>
057
058 @author Edward A. Lee, Christoph Daniel Schulze
059 @version $Id$
060 @since Ptolemy II 3.0
061 @Pt.ProposedRating Yellow (eal)
062 @Pt.AcceptedRating Red (cxh)
063 */
064public class IntRangeParameter extends Parameter {
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 IntRangeParameter(NamedObj container, String name)
079            throws IllegalActionException, NameDuplicationException {
080        super(container, name);
081
082        min = new Parameter(this, "min");
083        min.setExpression("0");
084        min.setTypeEquals(BaseType.INT);
085
086        max = new Parameter(this, "max");
087        max.setExpression("100");
088        max.setTypeEquals(BaseType.INT);
089
090        minLabel = new StringParameter(this, "minLabel");
091        minLabel.setExpression("$min");
092
093        maxLabel = new StringParameter(this, "maxLabel");
094        maxLabel.setExpression("$max");
095
096        setExpression("50");
097        setTypeEquals(BaseType.INT);
098
099        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-30\" y=\"-2\" "
100                + "width=\"60\" height=\"4\" " + "style=\"fill:white\"/>\n"
101                + "<rect x=\"15\" y=\"-10\" " + "width=\"4\" height=\"20\" "
102                + "style=\"fill:grey\"/>\n" + "</svg>\n");
103    }
104
105    ///////////////////////////////////////////////////////////////////
106    ////                         parameters                        ////
107
108    /** The maximum value. This is has an integer value, and defaults to 100. */
109    public Parameter max;
110
111    /** The minimum value. This is has an integer value, and defaults to 0. */
112    public Parameter min;
113
114    /** The label text displayed for the maximum end of the slider. This is a String,
115     *  and defaults to {@code $max}, which is expanded to the value of the {@code max}
116     *  parameter in the user interface.
117     */
118    public StringParameter maxLabel;
119
120    /** The label text displayed for the minimum end of the slider. This is a String,
121     *  and defaults to {@code $min}, which is expanded to the value of the {@code min}
122     *  parameter in the user interface.
123     */
124    public StringParameter minLabel;
125
126    ///////////////////////////////////////////////////////////////////
127    ////                         public methods                    ////
128
129    /** React to a change in an attribute by ensuring that the current
130     *  value remains within the range given by <i>min</i> and <i>max</i>.
131     *  @param attribute The attribute that changed.
132     *  @exception IllegalActionException If the change is not acceptable
133     *   to this container (should not be thrown).
134     */
135    @Override
136    public void attributeChanged(Attribute attribute)
137            throws IllegalActionException {
138        if (attribute == max && !_inCheck) {
139            try {
140                _inCheck = true;
141
142                int maxValue = ((IntToken) max.getToken()).intValue();
143
144                if (getCurrentValue() > maxValue) {
145                    setToken(max.getToken());
146                }
147            } finally {
148                _inCheck = false;
149            }
150        } else if (attribute == min && !_inCheck) {
151            try {
152                _inCheck = true;
153
154                int minValue = ((IntToken) min.getToken()).intValue();
155
156                if (getCurrentValue() < minValue) {
157                    setToken(min.getToken());
158                }
159            } finally {
160                _inCheck = false;
161            }
162        } else {
163            super.attributeChanged(attribute);
164        }
165    }
166
167    /** Return the current value of this parameter as an integer.
168     *  @return The current value.
169     *  @exception IllegalActionException If the expression cannot
170     *   be parsed or cannot be evaluated, or if the result of evaluation
171     *   violates type constraints, or if the result of evaluation is null
172     *   and there are variables that depend on this one.
173     */
174    public int getCurrentValue() throws IllegalActionException {
175        return ((IntToken) getToken()).intValue();
176    }
177
178    /** Return the maximum value of this parameter as an integer.
179     *  @return The maximum value.
180     *  @exception IllegalActionException If the expression cannot
181     *   be parsed or cannot be evaluated, or if the result of evaluation
182     *   violates type constraints, or if the result of evaluation is null
183     *   and there are variables that depend on this one.
184     */
185    public int getMaxValue() throws IllegalActionException {
186        return ((IntToken) max.getToken()).intValue();
187    }
188
189    /** Return the minimum value of this parameter.
190     *  @return The minimum value.
191     *  @exception IllegalActionException If the expression cannot
192     *   be parsed or cannot be evaluated, or if the result of evaluation
193     *   violates type constraints, or if the result of evaluation is null
194     *   and there are variables that depend on this one.
195     */
196    public int getMinValue() throws IllegalActionException {
197        return ((IntToken) min.getToken()).intValue();
198    }
199
200    ///////////////////////////////////////////////////////////////////
201    ////                         protected methods                 ////
202
203    /*  Set the token value and type of the variable, and notify the
204     *  container that the value (and type, if appropriate) has changed.
205     *  Also notify value dependents that they need to be re-evaluated,
206     *  and notify any listeners that have been registered with
207     *  addValueListener().
208     *  If setTypeEquals() has been called, then attempt to convert
209     *  the specified token into one of the appropriate type, if needed,
210     *  rather than changing the type.
211     *  @param newToken The new value of the variable.
212     *  @exception IllegalActionException If the token is not an IntToken
213     *   or its value is out of range.
214     */
215    @Override
216    protected void _setTokenAndNotify(Token newToken)
217            throws IllegalActionException {
218        if (_inCheck) {
219            super._setTokenAndNotify(newToken);
220            return;
221        }
222
223        if (newToken instanceof IntToken) {
224            try {
225                _inCheck = true;
226
227                int minValue = ((IntToken) min.getToken()).intValue();
228                int maxValue = ((IntToken) max.getToken()).intValue();
229                int currentValue = ((IntToken) newToken).intValue();
230
231                if (minValue <= currentValue && currentValue <= maxValue) {
232                    // All is OK.
233                    super._setTokenAndNotify(newToken);
234                    return;
235                }
236
237                throw new IllegalActionException(this,
238                        "Value is required to lie between " + min + " and "
239                                + max + ".");
240            } finally {
241                _inCheck = false;
242            }
243        }
244
245        throw new IllegalActionException(this,
246                "Value is required to be an integer token.");
247    }
248
249    ///////////////////////////////////////////////////////////////////
250    ////                         private variables                 ////
251
252    /** Indicator that we are in the middle of a check, so skip
253     *  circular dependency.
254     */
255    private boolean _inCheck = false;
256}