001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.swing;
027
028 import javax.swing.event.*;
029 import java.io.Serializable;
030 import java.util.EventListener;
031
032 /**
033 * A generic implementation of BoundedRangeModel.
034 * <p>
035 * <strong>Warning:</strong>
036 * Serialized objects of this class will not be compatible with
037 * future Swing releases. The current serialization support is
038 * appropriate for short term storage or RMI between applications running
039 * the same version of Swing. As of 1.4, support for long term storage
040 * of all JavaBeans<sup><font size="-2">TM</font></sup>
041 * has been added to the <code>java.beans</code> package.
042 * Please see {@link java.beans.XMLEncoder}.
043 *
044 * @version 1.54 05/05/07
045 * @author David Kloba
046 * @author Hans Muller
047 * @see BoundedRangeModel
048 */
049 public class DefaultBoundedRangeModel implements BoundedRangeModel,
050 Serializable {
051 /**
052 * Only one <code>ChangeEvent</code> is needed per model instance since the
053 * event's only (read-only) state is the source property. The source
054 * of events generated here is always "this".
055 */
056 protected transient ChangeEvent changeEvent = null;
057
058 /** The listeners waiting for model changes. */
059 protected EventListenerList listenerList = new EventListenerList();
060
061 private int value = 0;
062 private int extent = 0;
063 private int min = 0;
064 private int max = 100;
065 private boolean isAdjusting = false;
066
067 /**
068 * Initializes all of the properties with default values.
069 * Those values are:
070 * <ul>
071 * <li><code>value</code> = 0
072 * <li><code>extent</code> = 0
073 * <li><code>minimum</code> = 0
074 * <li><code>maximum</code> = 100
075 * <li><code>adjusting</code> = false
076 * </ul>
077 */
078 public DefaultBoundedRangeModel() {
079 }
080
081 /**
082 * Initializes value, extent, minimum and maximum. Adjusting is false.
083 * Throws an <code>IllegalArgumentException</code> if the following
084 * constraints aren't satisfied:
085 * <pre>
086 * min <= value <= value+extent <= max
087 * </pre>
088 */
089 public DefaultBoundedRangeModel(int value, int extent, int min,
090 int max) {
091 if ((max >= min) && (value >= min)
092 && ((value + extent) >= value)
093 && ((value + extent) <= max)) {
094 this .value = value;
095 this .extent = extent;
096 this .min = min;
097 this .max = max;
098 } else {
099 throw new IllegalArgumentException(
100 "invalid range properties");
101 }
102 }
103
104 /**
105 * Returns the model's current value.
106 * @return the model's current value
107 * @see #setValue
108 * @see BoundedRangeModel#getValue
109 */
110 public int getValue() {
111 return value;
112 }
113
114 /**
115 * Returns the model's extent.
116 * @return the model's extent
117 * @see #setExtent
118 * @see BoundedRangeModel#getExtent
119 */
120 public int getExtent() {
121 return extent;
122 }
123
124 /**
125 * Returns the model's minimum.
126 * @return the model's minimum
127 * @see #setMinimum
128 * @see BoundedRangeModel#getMinimum
129 */
130 public int getMinimum() {
131 return min;
132 }
133
134 /**
135 * Returns the model's maximum.
136 * @return the model's maximum
137 * @see #setMaximum
138 * @see BoundedRangeModel#getMaximum
139 */
140 public int getMaximum() {
141 return max;
142 }
143
144 /**
145 * Sets the current value of the model. For a slider, that
146 * determines where the knob appears. Ensures that the new
147 * value, <I>n</I> falls within the model's constraints:
148 * <pre>
149 * minimum <= value <= value+extent <= maximum
150 * </pre>
151 *
152 * @see BoundedRangeModel#setValue
153 */
154 public void setValue(int n) {
155 n = Math.min(n, Integer.MAX_VALUE - extent);
156
157 int newValue = Math.max(n, min);
158 if (newValue + extent > max) {
159 newValue = max - extent;
160 }
161 setRangeProperties(newValue, extent, min, max, isAdjusting);
162 }
163
164 /**
165 * Sets the extent to <I>n</I> after ensuring that <I>n</I>
166 * is greater than or equal to zero and falls within the model's
167 * constraints:
168 * <pre>
169 * minimum <= value <= value+extent <= maximum
170 * </pre>
171 * @see BoundedRangeModel#setExtent
172 */
173 public void setExtent(int n) {
174 int newExtent = Math.max(0, n);
175 if (value + newExtent > max) {
176 newExtent = max - value;
177 }
178 setRangeProperties(value, newExtent, min, max, isAdjusting);
179 }
180
181 /**
182 * Sets the minimum to <I>n</I> after ensuring that <I>n</I>
183 * that the other three properties obey the model's constraints:
184 * <pre>
185 * minimum <= value <= value+extent <= maximum
186 * </pre>
187 * @see #getMinimum
188 * @see BoundedRangeModel#setMinimum
189 */
190 public void setMinimum(int n) {
191 int newMax = Math.max(n, max);
192 int newValue = Math.max(n, value);
193 int newExtent = Math.min(newMax - newValue, extent);
194 setRangeProperties(newValue, newExtent, n, newMax, isAdjusting);
195 }
196
197 /**
198 * Sets the maximum 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 <= value <= value+extent <= maximum
202 * </pre>
203 * @see BoundedRangeModel#setMaximum
204 */
205 public void setMaximum(int n) {
206 int newMin = Math.min(n, min);
207 int newExtent = Math.min(n - newMin, extent);
208 int newValue = Math.min(n - newExtent, value);
209 setRangeProperties(newValue, newExtent, newMin, n, isAdjusting);
210 }
211
212 /**
213 * Sets the <code>valueIsAdjusting</code> property.
214 *
215 * @see #getValueIsAdjusting
216 * @see #setValue
217 * @see BoundedRangeModel#setValueIsAdjusting
218 */
219 public void setValueIsAdjusting(boolean b) {
220 setRangeProperties(value, extent, min, max, b);
221 }
222
223 /**
224 * Returns true if the value is in the process of changing
225 * as a result of actions being taken by the user.
226 *
227 * @return the value of the <code>valueIsAdjusting</code> property
228 * @see #setValue
229 * @see BoundedRangeModel#getValueIsAdjusting
230 */
231 public boolean getValueIsAdjusting() {
232 return isAdjusting;
233 }
234
235 /**
236 * Sets all of the <code>BoundedRangeModel</code> properties after forcing
237 * the arguments to obey the usual constraints:
238 * <pre>
239 * minimum <= value <= value+extent <= maximum
240 * </pre>
241 * <p>
242 * At most, one <code>ChangeEvent</code> is generated.
243 *
244 * @see BoundedRangeModel#setRangeProperties
245 * @see #setValue
246 * @see #setExtent
247 * @see #setMinimum
248 * @see #setMaximum
249 * @see #setValueIsAdjusting
250 */
251 public void setRangeProperties(int newValue, int newExtent,
252 int newMin, int newMax, boolean adjusting) {
253 if (newMin > newMax) {
254 newMin = newMax;
255 }
256 if (newValue > newMax) {
257 newMax = newValue;
258 }
259 if (newValue < newMin) {
260 newMin = newValue;
261 }
262
263 /* Convert the addends to long so that extent can be
264 * Integer.MAX_VALUE without rolling over the sum.
265 * A JCK test covers this, see bug 4097718.
266 */
267 if (((long) newExtent + (long) newValue) > newMax) {
268 newExtent = newMax - newValue;
269 }
270
271 if (newExtent < 0) {
272 newExtent = 0;
273 }
274
275 boolean isChange = (newValue != value) || (newExtent != extent)
276 || (newMin != min) || (newMax != max)
277 || (adjusting != isAdjusting);
278
279 if (isChange) {
280 value = newValue;
281 extent = newExtent;
282 min = newMin;
283 max = newMax;
284 isAdjusting = adjusting;
285
286 fireStateChanged();
287 }
288 }
289
290 /**
291 * Adds a <code>ChangeListener</code>. The change listeners are run each
292 * time any one of the Bounded Range model properties changes.
293 *
294 * @param l the ChangeListener to add
295 * @see #removeChangeListener
296 * @see BoundedRangeModel#addChangeListener
297 */
298 public void addChangeListener(ChangeListener l) {
299 listenerList.add(ChangeListener.class, l);
300 }
301
302 /**
303 * Removes a <code>ChangeListener</code>.
304 *
305 * @param l the <code>ChangeListener</code> to remove
306 * @see #addChangeListener
307 * @see BoundedRangeModel#removeChangeListener
308 */
309 public void removeChangeListener(ChangeListener l) {
310 listenerList.remove(ChangeListener.class, l);
311 }
312
313 /**
314 * Returns an array of all the change listeners
315 * registered on this <code>DefaultBoundedRangeModel</code>.
316 *
317 * @return all of this model's <code>ChangeListener</code>s
318 * or an empty
319 * array if no change listeners are currently registered
320 *
321 * @see #addChangeListener
322 * @see #removeChangeListener
323 *
324 * @since 1.4
325 */
326 public ChangeListener[] getChangeListeners() {
327 return (ChangeListener[]) listenerList
328 .getListeners(ChangeListener.class);
329 }
330
331 /**
332 * Runs each <code>ChangeListener</code>'s <code>stateChanged</code> method.
333 *
334 * @see #setRangeProperties
335 * @see EventListenerList
336 */
337 protected void fireStateChanged() {
338 Object[] listeners = listenerList.getListenerList();
339 for (int i = listeners.length - 2; i >= 0; i -= 2) {
340 if (listeners[i] == ChangeListener.class) {
341 if (changeEvent == null) {
342 changeEvent = new ChangeEvent(this );
343 }
344 ((ChangeListener) listeners[i + 1])
345 .stateChanged(changeEvent);
346 }
347 }
348 }
349
350 /**
351 * Returns a string that displays all of the
352 * <code>BoundedRangeModel</code> properties.
353 */
354 public String toString() {
355 String modelString = "value=" + getValue() + ", " + "extent="
356 + getExtent() + ", " + "min=" + getMinimum() + ", "
357 + "max=" + getMaximum() + ", " + "adj="
358 + getValueIsAdjusting();
359
360 return getClass().getName() + "[" + modelString + "]";
361 }
362
363 /**
364 * Returns an array of all the objects currently registered as
365 * <code><em>Foo</em>Listener</code>s
366 * upon this model.
367 * <code><em>Foo</em>Listener</code>s
368 * are registered using the <code>add<em>Foo</em>Listener</code> method.
369 * <p>
370 * You can specify the <code>listenerType</code> argument
371 * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
372 * For example, you can query a <code>DefaultBoundedRangeModel</code>
373 * instance <code>m</code>
374 * for its change listeners
375 * with the following code:
376 *
377 * <pre>ChangeListener[] cls = (ChangeListener[])(m.getListeners(ChangeListener.class));</pre>
378 *
379 * If no such listeners exist,
380 * this method returns an empty array.
381 *
382 * @param listenerType the type of listeners requested;
383 * this parameter should specify an interface
384 * that descends from <code>java.util.EventListener</code>
385 * @return an array of all objects registered as
386 * <code><em>Foo</em>Listener</code>s
387 * on this model,
388 * or an empty array if no such
389 * listeners have been added
390 * @exception ClassCastException if <code>listenerType</code> doesn't
391 * specify a class or interface that implements
392 * <code>java.util.EventListener</code>
393 *
394 * @see #getChangeListeners
395 *
396 * @since 1.3
397 */
398 public <T extends EventListener> T[] getListeners(
399 Class<T> listenerType) {
400 return listenerList.getListeners(listenerType);
401 }
402 }
|