Source for javax.swing.DefaultBoundedRangeModel

   1: /* DefaultBoundedRangeModel.java -- Default implementation
   2:    of BoundedRangeModel.
   3:    Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11: 
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package javax.swing;
  41: 
  42: import java.io.IOException;
  43: import java.io.ObjectInputStream;
  44: import java.io.ObjectOutputStream;
  45: import java.io.Serializable;
  46: import java.util.EventListener;
  47: 
  48: import javax.swing.event.ChangeEvent;
  49: import javax.swing.event.ChangeListener;
  50: import javax.swing.event.EventListenerList;
  51: 
  52: /**
  53:  * The default implementation of <code>BoundedRangeModel</code>.
  54:  *
  55:  * @author Andrew Selkirk (aselkirk@sympatico.ca)
  56:  * @author Sascha Brawer (brawer@dandelis.ch)
  57:  */
  58: public class DefaultBoundedRangeModel
  59:   implements BoundedRangeModel, Serializable
  60: {
  61:   /**
  62:    * The identifier of this class in object serialization. Verified
  63:    * using the serialver tool of Sun J2SE 1.4.1_01.
  64:    */
  65:   private static final long serialVersionUID = 5034068491295259790L;
  66: 
  67:   /**
  68:    * An event that is sent to all registered {@link ChangeListener}s
  69:    * when the state of this range model has changed.
  70:    *
  71:    * <p>The event object is created on demand, the first time it
  72:    * is actually needed.</p>
  73:    *
  74:    * @see #fireStateChanged()
  75:    */
  76:   protected transient ChangeEvent changeEvent;
  77: 
  78:   /**
  79:    * The list of the currently registered EventListeners.
  80:    */
  81:   protected EventListenerList listenerList = new EventListenerList();
  82: 
  83:   /**
  84:    * The current value of the range model, which is always between
  85:    * {@link #minimum} and ({@link #maximum} - {@link #extent}). In a
  86:    * scroll bar visualization of a {@link BoundedRangeModel}, the
  87:    * <code>value</code> is displayed as the position of the thumb.
  88:    */
  89:   private int value;
  90: 
  91:   /**
  92:    * The current extent of the range model, which is a number greater
  93:    * than or equal to zero. In a scroll bar visualization of a {@link
  94:    * BoundedRangeModel}, the <code>extent</code> is displayed as the
  95:    * size of the thumb.
  96:    */
  97:   private int extent;
  98: 
  99:   /**
 100:    * The current minimum value of the range model, which is always
 101:    * less than or equal to {@link #maximum}.
 102:    */
 103:   private int minimum;
 104: 
 105:   /**
 106:    * The current maximum value of the range model, which is always
 107:    * greater than or equal to {@link #minimum}.
 108:    */
 109:   private int maximum;
 110: 
 111:   /**
 112:    * A property that indicates whether the value of this {@link
 113:    * BoundedRangeModel} is going to change in the immediate future.
 114:    */
 115:   private boolean isAdjusting;
 116: 
 117:   /**
 118:    * Constructs a <code>DefaultBoundedRangeModel</code> with default
 119:    * values for the properties. The properties <code>value</code>,
 120:    * <code>extent</code> and <code>minimum</code> will be initialized
 121:    * to zero; <code>maximum</code> will be set to 100; the property
 122:    * <code>valueIsAdjusting</code> will be <code>false</code>.
 123:    */
 124:   public DefaultBoundedRangeModel()
 125:   {
 126:     // The fields value, extent, minimum have the default value 0, and
 127:     // isAdjusting is already false. These fields no not need to be
 128:     // set explicitly.
 129:     maximum = 100;
 130:   }
 131: 
 132:   /**
 133:    * Constructs a <code>DefaultBoundedRangeModel</code> with the
 134:    * specified values for some properties.
 135:    *
 136:    * @param value the initial value of the range model, which must be
 137:    *     a number between <code>minimum</code> and <code>(maximum -
 138:    *     extent)</code>. In a scroll bar visualization of a {@link
 139:    *     BoundedRangeModel}, the <code>value</code> is displayed as the
 140:    *     position of the thumb.
 141:    * @param extent the initial extent of the range model, which is a
 142:    *     number greater than or equal to zero. In a scroll bar
 143:    *     visualization of a {@link BoundedRangeModel}, the
 144:    *     <code>extent</code> is displayed as the size of the thumb.
 145:    * @param minimum the initial minimal value of the range model.
 146:    * @param maximum the initial maximal value of the range model.
 147:    *
 148:    * @throws IllegalArgumentException if the following condition is
 149:    *     not satisfied: <code>minimum &lt;= value &lt;= value + extent &lt;=
 150:    *     maximum</code>.
 151:    */
 152:   public DefaultBoundedRangeModel(int value, int extent, int minimum,
 153:                                   int maximum)
 154:   {
 155:     if (!(minimum <= value && extent >= 0 && (value + extent) <= maximum))
 156:       throw new IllegalArgumentException();
 157: 
 158:     this.value = value;
 159:     this.extent = extent;
 160:     this.minimum = minimum;
 161:     this.maximum = maximum;
 162: 
 163:     // The isAdjusting field already has a false value by default.
 164:   }
 165: 
 166:   /**
 167:    * Returns a string with all relevant properties of this range
 168:    * model.
 169:    *
 170:    * @return a string representing the object
 171:    */
 172:   public String toString()
 173:   {
 174:     return getClass().getName()
 175:       + "[value=" + value
 176:       + ", extent=" + extent
 177:       + ", min=" + minimum
 178:       + ", max=" + maximum
 179:       + ", adj=" + isAdjusting
 180:       + ']';
 181:   }
 182: 
 183:   /**
 184:    * Returns the current value of this bounded range model.  In a
 185:    * scroll bar visualization of a {@link BoundedRangeModel}, the
 186:    * <code>value</code> is displayed as the position of the thumb.
 187:    *
 188:    * @return the value
 189:    */
 190:   public int getValue()
 191:   {
 192:     return value;
 193:   }
 194: 
 195:   /**
 196:    * Changes the current value of this bounded range model. In a
 197:    * scroll bar visualization of a {@link BoundedRangeModel}, the
 198:    * <code>value</code> is displayed as the position of the thumb;
 199:    * changing the <code>value</code> of a scroll bar's model
 200:    * thus moves the thumb to a different position.
 201:    *
 202:    * @param value the value
 203:    */
 204:   public void setValue(int value)
 205:   {
 206:     value = Math.max(minimum, value);
 207:     if (value + extent > maximum)
 208:       value = maximum - extent;
 209: 
 210:     if (value != this.value)
 211:       {
 212:         this.value = value;
 213:         fireStateChanged();
 214:       }
 215:   }
 216: 
 217:   /**
 218:    * Returns the current extent of this bounded range model, which is
 219:    * a number greater than or equal to zero. In a scroll bar
 220:    * visualization of a {@link BoundedRangeModel}, the
 221:    * <code>extent</code> is displayed as the size of the thumb.
 222:    *
 223:    * @return the extent
 224:    */
 225:   public int getExtent()
 226:   {
 227:     return extent;
 228:   }
 229: 
 230:   /**
 231:    * Changes the current extent of this bounded range model. In a
 232:    * scroll bar visualization of a {@link BoundedRangeModel}, the
 233:    * <code>extent</code> is displayed as the size of the thumb.
 234:    *
 235:    * @param extent the new extent of the range model, which is a
 236:    *     number greater than or equal to zero.
 237:    */
 238:   public void setExtent(int extent)
 239:   {
 240:     extent = Math.max(extent, 0);
 241:     if (value + extent > maximum)
 242:       extent = maximum - value;
 243: 
 244:     if (extent != this.extent)
 245:       {
 246:         this.extent = extent;
 247:         fireStateChanged();
 248:       }
 249:   }
 250: 
 251:   /**
 252:    * Returns the current minimal value of this bounded range model.
 253:    */
 254:   public int getMinimum()
 255:   {
 256:     return minimum;
 257:   }
 258: 
 259:   /**
 260:    * Changes the current minimal value of this bounded range model.
 261:    *
 262:    * @param minimum the new minimal value.
 263:    */
 264:   public void setMinimum(int minimum)
 265:   {
 266:     int value, maximum;
 267: 
 268:     maximum = Math.max(minimum, this.maximum);
 269:     value = Math.max(minimum, this.value);
 270: 
 271:     setRangeProperties(value, extent, minimum, maximum, isAdjusting);
 272:   }
 273: 
 274:   /**
 275:    * Returns the current maximal value of this bounded range model.
 276:    *
 277:    * @return the maximum
 278:    */
 279:   public int getMaximum()
 280:   {
 281:     return maximum;
 282:   }
 283: 
 284:   /**
 285:    * Changes the current maximal value of this bounded range model.
 286:    *
 287:    * @param maximum the new maximal value.
 288:    */
 289:   public void setMaximum(int maximum)
 290:   {
 291:     int value, extent, minimum;
 292: 
 293:     minimum = Math.min(this.minimum, maximum);
 294:     extent = Math.min(this.extent, maximum - minimum);
 295:     value = Math.min(this.value, maximum - extent);
 296: 
 297:     setRangeProperties(value, extent, minimum, maximum, isAdjusting);
 298:   }
 299: 
 300:   /**
 301:    * Returns whether or not the value of this bounded range model is
 302:    * going to change in the immediate future. Scroll bars set this
 303:    * property to <code>true</code> while the thumb is being dragged
 304:    * around; when the mouse is relased, they set the property to
 305:    * <code>false</code> and post a final {@link ChangeEvent}.
 306:    *
 307:    * @return <code>true</code> if the value will change soon again;
 308:    *     <code>false</code> if the value will probably not change soon.
 309:    */
 310:   public boolean getValueIsAdjusting()
 311:   {
 312:     return isAdjusting;
 313:   }
 314: 
 315:   /**
 316:    * Specifies whether or not the value of this bounded range model is
 317:    * going to change in the immediate future. Scroll bars set this
 318:    * property to <code>true</code> while the thumb is being dragged
 319:    * around; when the mouse is relased, they set the property to
 320:    * <code>false</code>.
 321:    *
 322:    * @param isAdjusting <code>true</code> if the value will change
 323:    *     soon again; <code>false</code> if the value will probably not
 324:    *     change soon.
 325:    */
 326:   public void setValueIsAdjusting(boolean isAdjusting)
 327:   {
 328:     if (isAdjusting == this.isAdjusting)
 329:       return;
 330: 
 331:     this.isAdjusting = isAdjusting;
 332:     fireStateChanged();
 333:   }
 334: 
 335:   /**
 336:    * Sets all properties.
 337:    *
 338:    * @param value the new value of the range model.  In a scroll bar
 339:    *     visualization of a {@link BoundedRangeModel}, the
 340:    *     <code>value</code> is displayed as the position of the thumb.
 341:    * @param extent the new extent of the range model, which is a
 342:    *     number greater than or equal to zero. In a scroll bar
 343:    *     visualization of a {@link BoundedRangeModel}, the
 344:    *     <code>extent</code> is displayed as the size of the thumb.
 345:    * @param minimum the new minimal value of the range model.
 346:    * @param maximum the new maximal value of the range model.
 347:    * @param isAdjusting whether or not the value of this bounded range
 348:    *     model is going to change in the immediate future. Scroll bars set
 349:    *     this property to <code>true</code> while the thumb is being
 350:    *     dragged around; when the mouse is relased, they set the property
 351:    *     to <code>false</code>.
 352:    */
 353:   public void setRangeProperties(int value, int extent, int minimum,
 354:                                  int maximum, boolean isAdjusting)
 355:   {
 356:     minimum = Math.min(Math.min(minimum, maximum), value);
 357:     maximum = Math.max(value, maximum);
 358:     if (extent + value > maximum)
 359:       extent = maximum - value;
 360:     extent = Math.max(0, extent);
 361: 
 362:     if ((value == this.value)
 363:         && (extent == this.extent)
 364:         && (minimum == this.minimum)
 365:         && (maximum == this.maximum)
 366:         && (isAdjusting == this.isAdjusting))
 367:       return;
 368: 
 369:     this.value = value;
 370:     this.extent = extent;
 371:     this.minimum = minimum;
 372:     this.maximum = maximum;
 373:     this.isAdjusting = isAdjusting;
 374:         
 375:     fireStateChanged();
 376:   }
 377: 
 378:   /**
 379:    * Subscribes a ChangeListener to state changes.
 380:    *
 381:    * @param listener the listener to be subscribed.
 382:    */
 383:   public void addChangeListener(ChangeListener listener)
 384:   {
 385:     listenerList.add(ChangeListener.class, listener);
 386:   }
 387: 
 388:   /**
 389:    * Cancels the subscription of a ChangeListener.
 390:    *
 391:    * @param listener the listener to be unsubscribed.
 392:    */
 393:   public void removeChangeListener(ChangeListener listener)
 394:   {
 395:     listenerList.remove(ChangeListener.class, listener);
 396:   }
 397: 
 398:   /**
 399:    * Sends a {@link ChangeEvent} to any registered {@link
 400:    * ChangeListener}s.
 401:    *
 402:    * @see #addChangeListener(ChangeListener)
 403:    * @see #removeChangeListener(ChangeListener)
 404:    */
 405:   protected void fireStateChanged()
 406:   {
 407:     ChangeListener[] listeners = getChangeListeners();
 408:     
 409:     if (changeEvent == null)
 410:       changeEvent = new ChangeEvent(this);
 411: 
 412:     for (int i = listeners.length - 1; i >= 0; --i)
 413:       listeners[i].stateChanged(changeEvent);
 414:   }
 415: 
 416:   /**
 417:    * Retrieves the current listeners of the specified class.
 418:    *
 419:    * @param listenerType the class of listeners; usually {@link
 420:    *     ChangeListener}<code>.class</code>.
 421:    *
 422:    * @return an array with the currently subscribed listeners, or
 423:    *     an empty array if there are currently no listeners.
 424:    *
 425:    * @since 1.3
 426:    */
 427:   public <T extends EventListener> T[] getListeners(Class<T> listenerType)
 428:   {
 429:     return listenerList.getListeners(listenerType);
 430:   }
 431: 
 432:   /**
 433:    * Returns all <code>ChangeListeners</code> that are currently
 434:    * subscribed for changes to this
 435:    * <code>DefaultBoundedRangeModel</code>.
 436:    *
 437:    * @return an array with the currently subscribed listeners, or
 438:    *     an empty array if there are currently no listeners.
 439:    *
 440:    * @since 1.4
 441:    */
 442:   public ChangeListener[] getChangeListeners()
 443:   {
 444:     return (ChangeListener[]) getListeners(ChangeListener.class);
 445:   }
 446:   
 447:   /**
 448:    * Provides serialization support.
 449:    *
 450:    * @param stream  the output stream (<code>null</code> not permitted).
 451:    *
 452:    * @throws IOException  if there is an I/O error.
 453:    */
 454:   private void writeObject(ObjectOutputStream stream) 
 455:     throws IOException 
 456:   {
 457:     stream.defaultWriteObject();
 458:   }
 459: 
 460:   /**
 461:    * Provides serialization support.
 462:    *
 463:    * @param stream  the input stream (<code>null</code> not permitted).
 464:    *
 465:    * @throws IOException  if there is an I/O error.
 466:    * @throws ClassNotFoundException  if there is a classpath problem.
 467:    */
 468:   private void readObject(ObjectInputStream stream)
 469:     throws ClassNotFoundException, IOException
 470:   {
 471:     stream.defaultReadObject();
 472:     listenerList = new EventListenerList();
 473:   }
 474: 
 475: }