1 /*
   2  * Copyright (c) 1997, 2006, 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<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 &lt;= value &lt;= value+extent &lt;= 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 &lt;= value &lt;= value+extent &lt;= 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 &lt;= value &lt;= value+extent &lt;= 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 &lt;= value &lt;= value+extent &lt;= 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 &lt;= value &lt;= value+extent &lt;= 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 &lt;= value &lt;= value+extent &lt;= 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 listenerList.getListeners(ChangeListener.class);
 347     }
 348 
 349 
 350     /**
 351      * Runs each <code>ChangeListener</code>'s <code>stateChanged</code> method.
 352      *
 353      * @see #setRangeProperties
 354      * @see EventListenerList
 355      */
 356     protected void fireStateChanged()
 357     {
 358         Object[] listeners = listenerList.getListenerList();
 359         for (int i = listeners.length - 2; i >= 0; i -=2 ) {
 360             if (listeners[i] == ChangeListener.class) {
 361                 if (changeEvent == null) {
 362                     changeEvent = new ChangeEvent(this);
 363                 }
 364                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
 365             }
 366         }
 367     }
 368 
 369 
 370     /**
 371      * Returns a string that displays all of the
 372      * <code>BoundedRangeModel</code> properties.
 373      */
 374     public String toString()  {
 375         String modelString =
 376             "value=" + getValue() + ", " +
 377             "extent=" + getExtent() + ", " +
 378             "min=" + getMinimum() + ", " +
 379             "max=" + getMaximum() + ", " +
 380             "adj=" + getValueIsAdjusting();
 381 
 382         return getClass().getName() + "[" + modelString + "]";
 383     }
 384 
 385     /**
 386      * Returns an array of all the objects currently registered as
 387      * <code><em>Foo</em>Listener</code>s
 388      * upon this model.
 389      * <code><em>Foo</em>Listener</code>s
 390      * are registered using the <code>add<em>Foo</em>Listener</code> method.
 391      * <p>
 392      * You can specify the <code>listenerType</code> argument
 393      * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
 394      * For example, you can query a <code>DefaultBoundedRangeModel</code>
 395      * instance <code>m</code>
 396      * for its change listeners
 397      * with the following code:
 398      *
 399      * <pre>ChangeListener[] cls = (ChangeListener[])(m.getListeners(ChangeListener.class));</pre>
 400      *
 401      * If no such listeners exist,
 402      * this method returns an empty array.
 403      *
 404      * @param listenerType  the type of listeners requested;
 405      *          this parameter should specify an interface
 406      *          that descends from <code>java.util.EventListener</code>
 407      * @return an array of all objects registered as
 408      *          <code><em>Foo</em>Listener</code>s
 409      *          on this model,
 410      *          or an empty array if no such
 411      *          listeners have been added
 412      * @exception ClassCastException if <code>listenerType</code> doesn't
 413      *          specify a class or interface that implements
 414      *          <code>java.util.EventListener</code>
 415      *
 416      * @see #getChangeListeners
 417      *
 418      * @since 1.3
 419      */
 420     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 421         return listenerList.getListeners(listenerType);
 422     }
 423 }