001    /* DefaultBoundedRangeModel.java -- Default implementation
002       of BoundedRangeModel.
003       Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package javax.swing;
041    
042    import java.io.IOException;
043    import java.io.ObjectInputStream;
044    import java.io.ObjectOutputStream;
045    import java.io.Serializable;
046    import java.util.EventListener;
047    
048    import javax.swing.event.ChangeEvent;
049    import javax.swing.event.ChangeListener;
050    import javax.swing.event.EventListenerList;
051    
052    /**
053     * The default implementation of <code>BoundedRangeModel</code>.
054     *
055     * @author Andrew Selkirk (aselkirk@sympatico.ca)
056     * @author Sascha Brawer (brawer@dandelis.ch)
057     */
058    public class DefaultBoundedRangeModel
059      implements BoundedRangeModel, Serializable
060    {
061      /**
062       * The identifier of this class in object serialization. Verified
063       * using the serialver tool of Sun J2SE 1.4.1_01.
064       */
065      private static final long serialVersionUID = 5034068491295259790L;
066    
067      /**
068       * An event that is sent to all registered {@link ChangeListener}s
069       * when the state of this range model has changed.
070       *
071       * <p>The event object is created on demand, the first time it
072       * is actually needed.</p>
073       *
074       * @see #fireStateChanged()
075       */
076      protected transient ChangeEvent changeEvent;
077    
078      /**
079       * The list of the currently registered EventListeners.
080       */
081      protected EventListenerList listenerList = new EventListenerList();
082    
083      /**
084       * The current value of the range model, which is always between
085       * {@link #minimum} and ({@link #maximum} - {@link #extent}). In a
086       * scroll bar visualization of a {@link BoundedRangeModel}, the
087       * <code>value</code> is displayed as the position of the thumb.
088       */
089      private int value;
090    
091      /**
092       * The current extent of the range model, which is a number greater
093       * than or equal to zero. In a scroll bar visualization of a {@link
094       * BoundedRangeModel}, the <code>extent</code> is displayed as the
095       * size of the thumb.
096       */
097      private int extent;
098    
099      /**
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    }