|
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 } |