1 /*
2 * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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<sup><font size="-2">TM</font></sup>
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 */
48 public class DefaultBoundedRangeModel implements BoundedRangeModel, Serializable
49 {
50 /**
51 * Only one <code>ChangeEvent</code> is needed per model instance since the
52 * event's only (read-only) state is the source property. The source
53 * of events generated here is always "this".
54 */
55 protected transient ChangeEvent changeEvent = null;
56
57 /** The listeners waiting for model changes. */
58 protected EventListenerList listenerList = new EventListenerList();
59
60 private int value = 0;
61 private int extent = 0;
62 private int min = 0;
63 private int max = 100;
64 private boolean isAdjusting = false;
65
66
67 /**
68 * Initializes all of the properties with default values.
69 * Those values are:
70 * <ul>
71 * <li><code>value</code> = 0
72 * <li><code>extent</code> = 0
73 * <li><code>minimum</code> = 0
74 * <li><code>maximum</code> = 100
75 * <li><code>adjusting</code> = false
76 * </ul>
77 */
78 public DefaultBoundedRangeModel() {
79 }
80
81
82 /**
83 * Initializes value, extent, minimum and maximum. Adjusting is false.
84 * Throws an <code>IllegalArgumentException</code> if the following
85 * constraints aren't satisfied:
86 * <pre>
87 * min <= value <= value+extent <= max
88 * </pre>
89 */
90 public DefaultBoundedRangeModel(int value, int extent, int min, int max)
91 {
92 if ((max >= min) &&
93 (value >= min) &&
94 ((value + extent) >= value) &&
95 ((value + extent) <= max)) {
96 this.value = value;
97 this.extent = extent;
98 this.min = min;
99 this.max = max;
100 }
101 else {
102 throw new IllegalArgumentException("invalid range properties");
103 }
104 }
105
106
107 /**
108 * Returns the model's current value.
109 * @return the model's current value
110 * @see #setValue
111 * @see BoundedRangeModel#getValue
112 */
113 public int getValue() {
114 return value;
115 }
116
117
118 /**
119 * Returns the model's extent.
120 * @return the model's extent
121 * @see #setExtent
122 * @see BoundedRangeModel#getExtent
123 */
124 public int getExtent() {
125 return extent;
126 }
127
128
129 /**
130 * Returns the model's minimum.
131 * @return the model's minimum
132 * @see #setMinimum
133 * @see BoundedRangeModel#getMinimum
134 */
135 public int getMinimum() {
136 return min;
137 }
138
139
140 /**
141 * Returns the model's maximum.
142 * @return the model's maximum
143 * @see #setMaximum
144 * @see BoundedRangeModel#getMaximum
145 */
146 public int getMaximum() {
147 return max;
148 }
149
150
151 /**
152 * Sets the current value of the model. For a slider, that
153 * determines where the knob appears. Ensures that the new
154 * value, <I>n</I> falls within the model's constraints:
155 * <pre>
156 * minimum <= value <= value+extent <= maximum
157 * </pre>
158 *
159 * @see BoundedRangeModel#setValue
160 */
161 public void setValue(int n) {
162 n = Math.min(n, Integer.MAX_VALUE - extent);
163
164 int newValue = Math.max(n, min);
165 if (newValue + extent > max) {
166 newValue = max - extent;
167 }
168 setRangeProperties(newValue, extent, min, max, isAdjusting);
169 }
170
171
172 /**
173 * Sets the extent to <I>n</I> after ensuring that <I>n</I>
174 * is greater than or equal to zero and falls within the model's
175 * constraints:
176 * <pre>
177 * minimum <= value <= value+extent <= maximum
178 * </pre>
179 * @see BoundedRangeModel#setExtent
180 */
181 public void setExtent(int n) {
182 int newExtent = Math.max(0, n);
183 if(value + newExtent > max) {
184 newExtent = max - value;
185 }
186 setRangeProperties(value, newExtent, min, max, isAdjusting);
187 }
188
189
190 /**
191 * Sets the minimum to <I>n</I> after ensuring that <I>n</I>
192 * that the other three properties obey the model's constraints:
193 * <pre>
194 * minimum <= value <= value+extent <= maximum
195 * </pre>
196 * @see #getMinimum
197 * @see BoundedRangeModel#setMinimum
198 */
199 public void setMinimum(int n) {
200 int newMax = Math.max(n, max);
201 int newValue = Math.max(n, value);
202 int newExtent = Math.min(newMax - newValue, extent);
203 setRangeProperties(newValue, newExtent, n, newMax, isAdjusting);
204 }
205
206
207 /**
208 * Sets the maximum to <I>n</I> after ensuring that <I>n</I>
209 * that the other three properties obey the model's constraints:
210 * <pre>
211 * minimum <= value <= value+extent <= maximum
212 * </pre>
213 * @see BoundedRangeModel#setMaximum
214 */
215 public void setMaximum(int n) {
216 int newMin = Math.min(n, min);
217 int newExtent = Math.min(n - newMin, extent);
218 int newValue = Math.min(n - newExtent, value);
219 setRangeProperties(newValue, newExtent, newMin, n, isAdjusting);
220 }
221
222
223 /**
224 * Sets the <code>valueIsAdjusting</code> property.
225 *
226 * @see #getValueIsAdjusting
227 * @see #setValue
228 * @see BoundedRangeModel#setValueIsAdjusting
229 */
230 public void setValueIsAdjusting(boolean b) {
231 setRangeProperties(value, extent, min, max, b);
232 }
233
234
235 /**
236 * Returns true if the value is in the process of changing
237 * as a result of actions being taken by the user.
238 *
239 * @return the value of the <code>valueIsAdjusting</code> property
240 * @see #setValue
241 * @see BoundedRangeModel#getValueIsAdjusting
242 */
243 public boolean getValueIsAdjusting() {
244 return isAdjusting;
245 }
246
247
248 /**
249 * Sets all of the <code>BoundedRangeModel</code> properties after forcing
250 * the arguments to obey the usual constraints:
251 * <pre>
252 * minimum <= value <= value+extent <= maximum
253 * </pre>
254 * <p>
255 * At most, one <code>ChangeEvent</code> is generated.
256 *
257 * @see BoundedRangeModel#setRangeProperties
258 * @see #setValue
259 * @see #setExtent
260 * @see #setMinimum
261 * @see #setMaximum
262 * @see #setValueIsAdjusting
263 */
264 public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting)
265 {
266 if (newMin > newMax) {
267 newMin = newMax;
268 }
269 if (newValue > newMax) {
270 newMax = newValue;
271 }
272 if (newValue < newMin) {
273 newMin = newValue;
274 }
275
276 /* Convert the addends to long so that extent can be
277 * Integer.MAX_VALUE without rolling over the sum.
278 * A JCK test covers this, see bug 4097718.
279 */
280 if (((long)newExtent + (long)newValue) > newMax) {
281 newExtent = newMax - newValue;
282 }
283
284 if (newExtent < 0) {
285 newExtent = 0;
286 }
287
288 boolean isChange =
289 (newValue != value) ||
290 (newExtent != extent) ||
291 (newMin != min) ||
292 (newMax != max) ||
293 (adjusting != isAdjusting);
294
295 if (isChange) {
296 value = newValue;
297 extent = newExtent;
298 min = newMin;
299 max = newMax;
300 isAdjusting = adjusting;
301
302 fireStateChanged();
303 }
304 }
305
306
307 /**
308 * Adds a <code>ChangeListener</code>. The change listeners are run each
309 * time any one of the Bounded Range model properties changes.
310 *
311 * @param l the ChangeListener to add
312 * @see #removeChangeListener
313 * @see BoundedRangeModel#addChangeListener
314 */
315 public void addChangeListener(ChangeListener l) {
316 listenerList.add(ChangeListener.class, l);
317 }
318
319
320 /**
321 * Removes a <code>ChangeListener</code>.
322 *
323 * @param l the <code>ChangeListener</code> to remove
324 * @see #addChangeListener
325 * @see BoundedRangeModel#removeChangeListener
326 */
327 public void removeChangeListener(ChangeListener l) {
328 listenerList.remove(ChangeListener.class, l);
329 }
330
331
332 /**
333 * Returns an array of all the change listeners
334 * registered on this <code>DefaultBoundedRangeModel</code>.
335 *
336 * @return all of this model's <code>ChangeListener</code>s
337 * or an empty
338 * array if no change listeners are currently registered
339 *
340 * @see #addChangeListener
341 * @see #removeChangeListener
342 *
343 * @since 1.4
344 */
345 public ChangeListener[] getChangeListeners() {
346 return (ChangeListener[])listenerList.getListeners(
347 ChangeListener.class);
348 }
349
350
351 /**
352 * Runs each <code>ChangeListener</code>'s <code>stateChanged</code> method.
353 *
354 * @see #setRangeProperties
355 * @see EventListenerList
356 */
357 protected void fireStateChanged()
358 {
359 Object[] listeners = listenerList.getListenerList();
360 for (int i = listeners.length - 2; i >= 0; i -=2 ) {
361 if (listeners[i] == ChangeListener.class) {
362 if (changeEvent == null) {
363 changeEvent = new ChangeEvent(this);
364 }
365 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
366 }
367 }
368 }
369
370
371 /**
372 * Returns a string that displays all of the
373 * <code>BoundedRangeModel</code> properties.
374 */
375 public String toString() {
376 String modelString =
377 "value=" + getValue() + ", " +
378 "extent=" + getExtent() + ", " +
379 "min=" + getMinimum() + ", " +
380 "max=" + getMaximum() + ", " +
381 "adj=" + getValueIsAdjusting();
382
383 return getClass().getName() + "[" + modelString + "]";
384 }
385
386 /**
387 * Returns an array of all the objects currently registered as
388 * <code><em>Foo</em>Listener</code>s
389 * upon this model.
390 * <code><em>Foo</em>Listener</code>s
391 * are registered using the <code>add<em>Foo</em>Listener</code> method.
392 * <p>
393 * You can specify the <code>listenerType</code> argument
394 * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
395 * For example, you can query a <code>DefaultBoundedRangeModel</code>
396 * instance <code>m</code>
397 * for its change listeners
398 * with the following code:
399 *
400 * <pre>ChangeListener[] cls = (ChangeListener[])(m.getListeners(ChangeListener.class));</pre>
401 *
402 * If no such listeners exist,
403 * this method returns an empty array.
404 *
405 * @param listenerType the type of listeners requested;
406 * this parameter should specify an interface
407 * that descends from <code>java.util.EventListener</code>
408 * @return an array of all objects registered as
409 * <code><em>Foo</em>Listener</code>s
410 * on this model,
411 * or an empty array if no such
412 * listeners have been added
413 * @exception ClassCastException if <code>listenerType</code> doesn't
414 * specify a class or interface that implements
415 * <code>java.util.EventListener</code>
416 *
417 * @see #getChangeListeners
418 *
419 * @since 1.3
420 */
421 public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
422 return listenerList.getListeners(listenerType);
423 }
424 }