1/*
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing;
27
28import javax.swing.event.*;
29import java.io.Serializable;
30import java.util.EventListener;
31
32/**
33 * A generic implementation of BoundedRangeModel.
34 * <p>
35 * <strong>Warning:</strong>
36 * Serialized objects of this class will not be compatible with
37 * future Swing releases. The current serialization support is
38 * appropriate for short term storage or RMI between applications running
39 * the same version of Swing.  As of 1.4, support for long term storage
40 * of all JavaBeans&trade;
41 * has been added to the <code>java.beans</code> package.
42 * Please see {@link java.beans.XMLEncoder}.
43 *
44 * @author David Kloba
45 * @author Hans Muller
46 * @see BoundedRangeModel
47 * @since 1.2
48 */
49@SuppressWarnings("serial") // Same-version serialization only
50public class DefaultBoundedRangeModel implements BoundedRangeModel, Serializable
51{
52    /**
53     * Only one <code>ChangeEvent</code> is needed per model instance since the
54     * event's only (read-only) state is the source property.  The source
55     * of events generated here is always "this".
56     */
57    protected transient ChangeEvent changeEvent = null;
58
59    /** The listeners waiting for model changes. */
60    protected EventListenerList listenerList = new EventListenerList();
61
62    private int value = 0;
63    private int extent = 0;
64    private int min = 0;
65    private int max = 100;
66    private boolean isAdjusting = false;
67
68
69    /**
70     * Initializes all of the properties with default values.
71     * Those values are:
72     * <ul>
73     * <li><code>value</code> = 0
74     * <li><code>extent</code> = 0
75     * <li><code>minimum</code> = 0
76     * <li><code>maximum</code> = 100
77     * <li><code>adjusting</code> = false
78     * </ul>
79     */
80    public DefaultBoundedRangeModel() {
81    }
82
83
84    /**
85     * Initializes value, extent, minimum and maximum. Adjusting is false.
86     * Throws an <code>IllegalArgumentException</code> if the following
87     * constraints aren't satisfied:
88     * <pre>
89     * min &lt;= value &lt;= value+extent &lt;= max
90     * </pre>
91     *
92     * @param value  an int giving the current value
93     * @param extent the length of the inner range that begins at the model's value
94     * @param min    an int giving the minimum value
95     * @param max    an int giving the maximum value
96     */
97    public DefaultBoundedRangeModel(int value, int extent, int min, int max)
98    {
99        if ((max >= min) &&
100            (value >= min) &&
101            ((value + extent) >= value) &&
102            ((value + extent) <= max)) {
103            this.value = value;
104            this.extent = extent;
105            this.min = min;
106            this.max = max;
107        }
108        else {
109            throw new IllegalArgumentException("invalid range properties");
110        }
111    }
112
113
114    /**
115     * Returns the model's current value.
116     * @return the model's current value
117     * @see #setValue
118     * @see BoundedRangeModel#getValue
119     */
120    public int getValue() {
121      return value;
122    }
123
124
125    /**
126     * Returns the model's extent.
127     * @return the model's extent
128     * @see #setExtent
129     * @see BoundedRangeModel#getExtent
130     */
131    public int getExtent() {
132      return extent;
133    }
134
135
136    /**
137     * Returns the model's minimum.
138     * @return the model's minimum
139     * @see #setMinimum
140     * @see BoundedRangeModel#getMinimum
141     */
142    public int getMinimum() {
143      return min;
144    }
145
146
147    /**
148     * Returns the model's maximum.
149     * @return  the model's maximum
150     * @see #setMaximum
151     * @see BoundedRangeModel#getMaximum
152     */
153    public int getMaximum() {
154        return max;
155    }
156
157
158    /**
159     * Sets the current value of the model. For a slider, that
160     * determines where the knob appears. Ensures that the new
161     * value, <I>n</I> falls within the model's constraints:
162     * <pre>
163     *     minimum &lt;= value &lt;= value+extent &lt;= maximum
164     * </pre>
165     *
166     * @see BoundedRangeModel#setValue
167     */
168    public void setValue(int n) {
169        n = Math.min(n, Integer.MAX_VALUE - extent);
170
171        int newValue = Math.max(n, min);
172        if (newValue + extent > max) {
173            newValue = max - extent;
174        }
175        setRangeProperties(newValue, extent, min, max, isAdjusting);
176    }
177
178
179    /**
180     * Sets the extent to <I>n</I> after ensuring that <I>n</I>
181     * is greater than or equal to zero and falls within the model's
182     * constraints:
183     * <pre>
184     *     minimum &lt;= value &lt;= value+extent &lt;= maximum
185     * </pre>
186     * @see BoundedRangeModel#setExtent
187     */
188    public void setExtent(int n) {
189        int newExtent = Math.max(0, n);
190        if(value + newExtent > max) {
191            newExtent = max - value;
192        }
193        setRangeProperties(value, newExtent, min, max, isAdjusting);
194    }
195
196
197    /**
198     * Sets the minimum to <I>n</I> after ensuring that <I>n</I>
199     * that the other three properties obey the model's constraints:
200     * <pre>
201     *     minimum &lt;= value &lt;= value+extent &lt;= maximum
202     * </pre>
203     * @see #getMinimum
204     * @see BoundedRangeModel#setMinimum
205     */
206    public void setMinimum(int n) {
207        int newMax = Math.max(n, max);
208        int newValue = Math.max(n, value);
209        int newExtent = Math.min(newMax - newValue, extent);
210        setRangeProperties(newValue, newExtent, n, newMax, isAdjusting);
211    }
212
213
214    /**
215     * Sets the maximum to <I>n</I> after ensuring that <I>n</I>
216     * that the other three properties obey the model's constraints:
217     * <pre>
218     *     minimum &lt;= value &lt;= value+extent &lt;= maximum
219     * </pre>
220     * @see BoundedRangeModel#setMaximum
221     */
222    public void setMaximum(int n) {
223        int newMin = Math.min(n, min);
224        int newExtent = Math.min(n - newMin, extent);
225        int newValue = Math.min(n - newExtent, value);
226        setRangeProperties(newValue, newExtent, newMin, n, isAdjusting);
227    }
228
229
230    /**
231     * Sets the <code>valueIsAdjusting</code> property.
232     *
233     * @see #getValueIsAdjusting
234     * @see #setValue
235     * @see BoundedRangeModel#setValueIsAdjusting
236     */
237    public void setValueIsAdjusting(boolean b) {
238        setRangeProperties(value, extent, min, max, b);
239    }
240
241
242    /**
243     * Returns true if the value is in the process of changing
244     * as a result of actions being taken by the user.
245     *
246     * @return the value of the <code>valueIsAdjusting</code> property
247     * @see #setValue
248     * @see BoundedRangeModel#getValueIsAdjusting
249     */
250    public boolean getValueIsAdjusting() {
251        return isAdjusting;
252    }
253
254
255    /**
256     * Sets all of the <code>BoundedRangeModel</code> properties after forcing
257     * the arguments to obey the usual constraints:
258     * <pre>
259     *     minimum &lt;= value &lt;= value+extent &lt;= maximum
260     * </pre>
261     * <p>
262     * At most, one <code>ChangeEvent</code> is generated.
263     *
264     * @see BoundedRangeModel#setRangeProperties
265     * @see #setValue
266     * @see #setExtent
267     * @see #setMinimum
268     * @see #setMaximum
269     * @see #setValueIsAdjusting
270     */
271    public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting)
272    {
273        if (newMin > newMax) {
274            newMin = newMax;
275        }
276        if (newValue > newMax) {
277            newMax = newValue;
278        }
279        if (newValue < newMin) {
280            newMin = newValue;
281        }
282
283        /* Convert the addends to long so that extent can be
284         * Integer.MAX_VALUE without rolling over the sum.
285         * A JCK test covers this, see bug 4097718.
286         */
287        if (((long)newExtent + (long)newValue) > newMax) {
288            newExtent = newMax - newValue;
289        }
290
291        if (newExtent < 0) {
292            newExtent = 0;
293        }
294
295        boolean isChange =
296            (newValue != value) ||
297            (newExtent != extent) ||
298            (newMin != min) ||
299            (newMax != max) ||
300            (adjusting != isAdjusting);
301
302        if (isChange) {
303            value = newValue;
304            extent = newExtent;
305            min = newMin;
306            max = newMax;
307            isAdjusting = adjusting;
308
309            fireStateChanged();
310        }
311    }
312
313
314    /**
315     * Adds a <code>ChangeListener</code>.  The change listeners are run each
316     * time any one of the Bounded Range model properties changes.
317     *
318     * @param l the ChangeListener to add
319     * @see #removeChangeListener
320     * @see BoundedRangeModel#addChangeListener
321     */
322    public void addChangeListener(ChangeListener l) {
323        listenerList.add(ChangeListener.class, l);
324    }
325
326
327    /**
328     * Removes a <code>ChangeListener</code>.
329     *
330     * @param l the <code>ChangeListener</code> to remove
331     * @see #addChangeListener
332     * @see BoundedRangeModel#removeChangeListener
333     */
334    public void removeChangeListener(ChangeListener l) {
335        listenerList.remove(ChangeListener.class, l);
336    }
337
338
339    /**
340     * Returns an array of all the change listeners
341     * registered on this <code>DefaultBoundedRangeModel</code>.
342     *
343     * @return all of this model's <code>ChangeListener</code>s
344     *         or an empty
345     *         array if no change listeners are currently registered
346     *
347     * @see #addChangeListener
348     * @see #removeChangeListener
349     *
350     * @since 1.4
351     */
352    public ChangeListener[] getChangeListeners() {
353        return listenerList.getListeners(ChangeListener.class);
354    }
355
356
357    /**
358     * Runs each <code>ChangeListener</code>'s <code>stateChanged</code> method.
359     *
360     * @see #setRangeProperties
361     * @see EventListenerList
362     */
363    protected void fireStateChanged()
364    {
365        Object[] listeners = listenerList.getListenerList();
366        for (int i = listeners.length - 2; i >= 0; i -=2 ) {
367            if (listeners[i] == ChangeListener.class) {
368                if (changeEvent == null) {
369                    changeEvent = new ChangeEvent(this);
370                }
371                ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
372            }
373        }
374    }
375
376
377    /**
378     * Returns a string that displays all of the
379     * <code>BoundedRangeModel</code> properties.
380     */
381    public String toString()  {
382        String modelString =
383            "value=" + getValue() + ", " +
384            "extent=" + getExtent() + ", " +
385            "min=" + getMinimum() + ", " +
386            "max=" + getMaximum() + ", " +
387            "adj=" + getValueIsAdjusting();
388
389        return getClass().getName() + "[" + modelString + "]";
390    }
391
392    /**
393     * Returns an array of all the objects currently registered as
394     * <code><em>Foo</em>Listener</code>s
395     * upon this model.
396     * <code><em>Foo</em>Listener</code>s
397     * are registered using the <code>add<em>Foo</em>Listener</code> method.
398     * <p>
399     * You can specify the <code>listenerType</code> argument
400     * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
401     * For example, you can query a <code>DefaultBoundedRangeModel</code>
402     * instance <code>m</code>
403     * for its change listeners
404     * with the following code:
405     *
406     * <pre>ChangeListener[] cls = (ChangeListener[])(m.getListeners(ChangeListener.class));</pre>
407     *
408     * If no such listeners exist,
409     * this method returns an empty array.
410     *
411     * @param <T> the type of {@code EventListener} class being requested
412     * @param listenerType  the type of listeners requested;
413     *          this parameter should specify an interface
414     *          that descends from <code>java.util.EventListener</code>
415     * @return an array of all objects registered as
416     *          <code><em>Foo</em>Listener</code>s
417     *          on this model,
418     *          or an empty array if no such
419     *          listeners have been added
420     * @exception ClassCastException if <code>listenerType</code> doesn't
421     *          specify a class or interface that implements
422     *          <code>java.util.EventListener</code>
423     *
424     * @see #getChangeListeners
425     *
426     * @since 1.3
427     */
428    public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
429        return listenerList.getListeners(listenerType);
430    }
431}
432