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 
26 package javax.swing;
27 
28 import javax.swing.event.*;
29 import java.io.Serializable;
30 import 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
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
50 public 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      */
DefaultBoundedRangeModel()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      */
DefaultBoundedRangeModel(int value, int extent, int min, int max)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      */
getValue()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      */
getExtent()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      */
getMinimum()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      */
getMaximum()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      */
setValue(int n)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      */
setExtent(int n)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      */
setMinimum(int n)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      */
setMaximum(int n)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      */
setValueIsAdjusting(boolean b)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      */
getValueIsAdjusting()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      */
setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting)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      */
addChangeListener(ChangeListener l)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      */
removeChangeListener(ChangeListener l)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      */
getChangeListeners()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      */
fireStateChanged()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      */
toString()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      */
getListeners(Class<T> listenerType)428     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
429         return listenerList.getListeners(listenerType);
430     }
431 }
432