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 <= value <= value+extent <= 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 <= value <= value+extent <= 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 <= value <= value+extent <= 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 <= value <= value+extent <= 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 <= value <= value+extent <= 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 <= value <= value+extent <= 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