001    /* JScrollBar.java --
002       Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing;
040    
041    import gnu.java.lang.CPStringBuilder;
042    
043    import java.awt.Adjustable;
044    import java.awt.Dimension;
045    import java.awt.event.AdjustmentEvent;
046    import java.awt.event.AdjustmentListener;
047    import java.beans.PropertyChangeEvent;
048    
049    import javax.accessibility.Accessible;
050    import javax.accessibility.AccessibleContext;
051    import javax.accessibility.AccessibleRole;
052    import javax.accessibility.AccessibleState;
053    import javax.accessibility.AccessibleStateSet;
054    import javax.accessibility.AccessibleValue;
055    import javax.swing.event.ChangeEvent;
056    import javax.swing.event.ChangeListener;
057    import javax.swing.plaf.ScrollBarUI;
058    
059    /**
060     * The JScrollBar. Two buttons control how the values that the 
061     * scroll bar can take. You can also drag the thumb or click the track
062     * to move the scroll bar. Typically, the JScrollBar is used with
063     * other components to translate the value of the bar to the viewable
064     * contents of the other components.
065     */
066    public class JScrollBar extends JComponent implements Adjustable, Accessible
067    {
068      /**
069       * Provides the accessibility features for the <code>JScrollBar</code>
070       * component.
071       */
072      protected class AccessibleJScrollBar extends JComponent.AccessibleJComponent
073        implements AccessibleValue
074      {
075        private static final long serialVersionUID = -7758162392045586663L;
076        
077        /**
078         * Creates a new <code>AccessibleJScrollBar</code> instance.
079         */
080        protected AccessibleJScrollBar()
081        {
082          super();
083        }
084    
085        /**
086         * Returns a set containing the current state of the {@link JScrollBar} 
087         * component.
088         *
089         * @return The accessible state set.
090         */
091        public AccessibleStateSet getAccessibleStateSet()
092        {
093          AccessibleStateSet result = super.getAccessibleStateSet();
094          if (orientation == JScrollBar.HORIZONTAL)
095            result.add(AccessibleState.HORIZONTAL);
096          else if (orientation == JScrollBar.VERTICAL)
097            result.add(AccessibleState.VERTICAL);
098          return result;
099        }
100    
101        /**
102         * Returns the accessible role for the <code>JScrollBar</code> component.
103         *
104         * @return {@link AccessibleRole#SCROLL_BAR}.
105         */
106        public AccessibleRole getAccessibleRole()
107        {
108          return AccessibleRole.SCROLL_BAR;
109        }
110    
111        /**
112         * Returns an object that provides access to the current, minimum and 
113         * maximum values.
114         *
115         * @return The accessible value.
116         */
117        public AccessibleValue getAccessibleValue()
118        {
119          return this;
120        }
121    
122        /**
123         * Returns the current value of the {@link JScrollBar} component, as an
124         * {@link Integer}.
125         *
126         * @return The current value of the {@link JScrollBar} component.
127         */
128        public Number getCurrentAccessibleValue()
129        {
130          return new Integer(getValue());
131        }
132    
133        /**
134         * Sets the current value of the {@link JScrollBar} component and sends a
135         * {@link PropertyChangeEvent} (with the property name 
136         * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
137         * listeners.  If the supplied value is <code>null</code>, this method 
138         * does nothing and returns <code>false</code>.
139         *
140         * @param value  the new slider value (<code>null</code> permitted).
141         *
142         * @return <code>true</code> if the slider value is updated, and 
143         *     <code>false</code> otherwise.
144         */
145        public boolean setCurrentAccessibleValue(Number value)
146        {
147          if (value == null)
148            return false;
149          Number oldValue = getCurrentAccessibleValue();
150          setValue(value.intValue());
151          firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue, 
152                             new Integer(getValue()));
153          return true;
154        }
155    
156        /**
157         * Returns the minimum value of the {@link JScrollBar} component, as an
158         * {@link Integer}.
159         *
160         * @return The minimum value of the {@link JScrollBar} component.
161         */
162        public Number getMinimumAccessibleValue()
163        {
164          return new Integer(getMinimum());
165        }
166    
167        /**
168         * Returns the maximum value of the {@link JScrollBar} component, as an
169         * {@link Integer}.
170         *
171         * @return The maximum value of the {@link JScrollBar} component.
172         */
173        public Number getMaximumAccessibleValue()
174        {
175          return new Integer(getMaximum() - model.getExtent());
176        }
177      }
178    
179      /**
180       * Listens for changes on the model and fires them to interested
181       * listeners on the JScrollBar, after re-sourcing them.
182       */
183      private class ScrollBarChangeListener
184        implements ChangeListener
185      {
186    
187        public void stateChanged(ChangeEvent event)
188        {
189          Object o = event.getSource();
190          if (o instanceof BoundedRangeModel)
191            {
192              BoundedRangeModel m = (BoundedRangeModel) o;
193              fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
194                                         AdjustmentEvent.TRACK, m.getValue(),
195                                         m.getValueIsAdjusting());
196            }
197        }
198        
199      }
200    
201      private static final long serialVersionUID = -8195169869225066566L;
202      
203      /** How much the thumb moves when moving in a block. */
204      protected int blockIncrement = 10;
205    
206      /** The model that holds the scroll bar's data. */
207      protected BoundedRangeModel model;
208    
209      /** The orientation of the scroll bar. */
210      protected int orientation = SwingConstants.VERTICAL;
211    
212      /** How much the thumb moves when moving in a unit. */
213      protected int unitIncrement = 1;
214    
215      /**
216       * This ChangeListener forwards events fired from the model and re-sources
217       * them to originate from this JScrollBar.
218       */
219      private ChangeListener sbChangeListener;
220    
221      /** 
222       * Creates a new horizontal JScrollBar object with a minimum
223       * of 0, a maxmium of 100, a value of 0 and an extent of 10.
224       */
225      public JScrollBar()
226      {
227        this(SwingConstants.VERTICAL, 0, 10, 0, 100);
228      }
229    
230      /**
231       * Creates a new JScrollBar object with a minimum of 0, a 
232       * maximum of 100, a value of 0, an extent of 10 and the given
233       * orientation.
234       *
235       * @param orientation The orientation of the JScrollBar.
236       */
237      public JScrollBar(int orientation)
238      {
239        this(orientation, 0, 10, 0, 100);
240      }
241    
242      /**
243       * Creates a new JScrollBar object with the given orientation, 
244       * value, min, max, and extent.
245       *
246       * @param orientation The orientation to use.
247       * @param value The value to use.
248       * @param extent The extent to use.
249       * @param min The minimum value of the scrollbar.
250       * @param max The maximum value of the scrollbar.
251       */
252      public JScrollBar(int orientation, int value, int extent, int min, int max)
253      {
254        model = new DefaultBoundedRangeModel(value, extent, min, max);
255        sbChangeListener = new ScrollBarChangeListener();
256        model.addChangeListener(sbChangeListener);
257        if (orientation != SwingConstants.HORIZONTAL
258            && orientation != SwingConstants.VERTICAL)
259          throw new IllegalArgumentException(orientation
260                                             + " is not a legal orientation");
261        this.orientation = orientation;
262        updateUI();
263      }
264    
265      /**
266       * This method sets the UI of this scrollbar to
267       * the given UI.
268       *
269       * @param ui The UI to use with this scrollbar.
270       */
271      public void setUI(ScrollBarUI ui)
272      {
273        super.setUI(ui);
274      }
275    
276      /**
277       * This method returns the UI that is being used
278       * with this scrollbar.
279       *
280       * @return The scrollbar's current UI.
281       */
282      public ScrollBarUI getUI()
283      {
284        return (ScrollBarUI) ui;
285      }
286    
287      /**
288       * This method changes the UI to be the
289       * default for the current look and feel.
290       */
291      public void updateUI()
292      {
293        setUI((ScrollBarUI) UIManager.getUI(this));
294      }
295    
296      /**
297       * This method returns an identifier to 
298       * choose the correct UI delegate for the
299       * scrollbar.
300       *
301       * @return The identifer to choose the UI delegate; "ScrollBarUI"
302       */
303      public String getUIClassID()
304      {
305        return "ScrollBarUI";
306      }
307    
308      /**
309       * This method returns the orientation of the scrollbar.
310       *
311       * @return The orientation of the scrollbar.
312       */
313      public int getOrientation()
314      {
315        return orientation;
316      }
317    
318      /**
319       * This method sets the orientation of the scrollbar.
320       *
321       * @param orientation The orientation of the scrollbar.
322       */
323      public void setOrientation(int orientation)
324      {
325        if (orientation != SwingConstants.HORIZONTAL
326            && orientation != SwingConstants.VERTICAL)
327          throw new IllegalArgumentException("orientation must be one of HORIZONTAL or VERTICAL");
328        if (orientation != this.orientation)
329          {
330            int oldOrientation = this.orientation;
331            this.orientation = orientation;
332            firePropertyChange("orientation", oldOrientation,
333                               this.orientation);
334          }
335      }
336    
337      /**
338       * This method returns the model being used with 
339       * the scrollbar.
340       *
341       * @return The scrollbar's model.
342       */
343      public BoundedRangeModel getModel()
344      {
345        return model;
346      }
347    
348      /**
349       * This method sets the model to use with
350       * the scrollbar.
351       *
352       * @param newModel The new model to use with the scrollbar.
353       */
354      public void setModel(BoundedRangeModel newModel)
355      {
356        BoundedRangeModel oldModel = model;
357        if (oldModel != null)
358          oldModel.removeChangeListener(sbChangeListener);
359        model = newModel;
360        if (model != null)
361          model.addChangeListener(sbChangeListener);
362        firePropertyChange("model", oldModel, model);
363      }
364    
365      /**
366       * This method returns how much the scrollbar's value
367       * should change for a unit increment depending on the 
368       * given direction.
369       *
370       * @param direction The direction to scroll in.
371       *
372       * @return The amount the scrollbar's value will change given the direction.
373       */
374      public int getUnitIncrement(int direction)
375      {
376        return unitIncrement;
377      }
378    
379      /**
380       * This method sets the unitIncrement property.
381       *
382       * @param unitIncrement The new unitIncrement.
383       */
384      public void setUnitIncrement(int unitIncrement)
385      {
386        if (unitIncrement != this.unitIncrement)
387          {
388            int oldInc = this.unitIncrement;
389            this.unitIncrement = unitIncrement;
390            firePropertyChange("unitIncrement", oldInc,
391                               this.unitIncrement);
392          }
393      }
394    
395      /**
396       * The method returns how much the scrollbar's value
397       * should change for a block increment depending on
398       * the given direction.
399       *
400       * @param direction The direction to scroll in.
401       *
402       * @return The amount the scrollbar's value will change given the direction.
403       */
404      public int getBlockIncrement(int direction)
405      {
406        return blockIncrement;
407      }
408    
409      /**
410       * This method sets the blockIncrement property.
411       *
412       * @param blockIncrement The new blockIncrement.
413       */
414      public void setBlockIncrement(int blockIncrement)
415      {
416        if (blockIncrement != this.blockIncrement)
417          {
418            int oldInc = this.blockIncrement;
419            this.blockIncrement = blockIncrement;
420            firePropertyChange("blockIncrement", oldInc,
421                               this.blockIncrement);
422          }
423      }
424    
425      /**
426       * This method returns the unitIncrement.
427       *
428       * @return The unitIncrement.
429       */
430      public int getUnitIncrement()
431      {
432        return unitIncrement;
433      }
434    
435      /**
436       * This method returns the blockIncrement.
437       *
438       * @return The blockIncrement.
439       */
440      public int getBlockIncrement()
441      {
442        return blockIncrement;
443      }
444    
445      /**
446       * This method returns the value of the scrollbar.
447       *
448       * @return The value of the scrollbar.
449       */
450      public int getValue()
451      {
452        return model.getValue();
453      }
454    
455      /**
456       * This method changes the value of the scrollbar.
457       *
458       * @param value The new value of the scrollbar.
459       */
460      public void setValue(int value)
461      {
462        model.setValue(value);
463      }
464    
465      /**
466       * This method returns the visible amount (AKA extent). 
467       * The visible amount can be used by UI delegates to 
468       * determine the size of the thumb.
469       *
470       * @return The visible amount (AKA extent).
471       */
472      public int getVisibleAmount()
473      {
474        return model.getExtent();
475      }
476    
477      /**
478       * This method sets the visible amount (AKA extent).
479       *
480       * @param extent The visible amount (AKA extent).
481       */
482      public void setVisibleAmount(int extent)
483      {
484        model.setExtent(extent);
485      }
486    
487      /**
488       * This method returns the minimum value of the scrollbar.
489       *
490       * @return The minimum value of the scrollbar.
491       */
492      public int getMinimum()
493      {
494        return model.getMinimum();
495      }
496    
497      /**
498       * This method sets the minimum value of the scrollbar.
499       *
500       * @param minimum The minimum value of the scrollbar.
501       */
502      public void setMinimum(int minimum)
503      {
504        model.setMinimum(minimum);
505      }
506    
507      /**
508       * This method returns the maximum value of the scrollbar.
509       *
510       * @return The maximum value of the scrollbar.
511       */
512      public int getMaximum()
513      {
514        return model.getMaximum();
515      }
516    
517      /**
518       * This method sets the maximum value of the scrollbar.
519       *
520       * @param maximum The maximum value of the scrollbar.
521       */
522      public void setMaximum(int maximum)
523      {
524        model.setMaximum(maximum);
525      }
526    
527      /**
528       * This method returns the model's isAjusting value.
529       *
530       * @return The model's isAdjusting value.
531       */
532      public boolean getValueIsAdjusting()
533      {
534        return model.getValueIsAdjusting();
535      }
536    
537      /**
538       * This method sets the model's isAdjusting value.
539       *
540       * @param b The new isAdjusting value.
541       */
542      public void setValueIsAdjusting(boolean b)
543      {
544        model.setValueIsAdjusting(b);
545      }
546    
547      /**
548       * This method sets the value, extent, minimum and 
549       * maximum.
550       *
551       * @param newValue The new value.
552       * @param newExtent The new extent.
553       * @param newMin The new minimum.
554       * @param newMax The new maximum.
555       */
556      public void setValues(int newValue, int newExtent, int newMin, int newMax)
557      {
558        model.setRangeProperties(newValue, newExtent, newMin, newMax,
559                                 model.getValueIsAdjusting());
560      }
561    
562      /**
563       * This method adds an AdjustmentListener to the scroll bar.
564       *
565       * @param listener The listener to add.
566       */
567      public void addAdjustmentListener(AdjustmentListener listener)
568      {
569        listenerList.add(AdjustmentListener.class, listener);
570      }
571    
572      /**
573       * This method removes an AdjustmentListener from the scroll bar. 
574       *
575       * @param listener The listener to remove.
576       */
577      public void removeAdjustmentListener(AdjustmentListener listener)
578      {
579        listenerList.remove(AdjustmentListener.class, listener);
580      }
581    
582      /**
583       * This method returns an arry of all AdjustmentListeners listening to 
584       * this scroll bar.
585       *
586       * @return An array of AdjustmentListeners listening to this scroll bar.
587       */
588      public AdjustmentListener[] getAdjustmentListeners()
589      {
590        return (AdjustmentListener[]) listenerList.getListeners(AdjustmentListener.class);
591      }
592    
593      /**
594       * This method is called to fired AdjustmentEvents to the listeners
595       * of this scroll bar. All AdjustmentEvents that are fired
596       * will have an ID of ADJUSTMENT_VALUE_CHANGED and a type of
597       * TRACK. 
598       *
599       * @param id The ID of the adjustment event.
600       * @param type The Type of change.
601       * @param value The new value for the property that was changed..
602       */
603      protected void fireAdjustmentValueChanged(int id, int type, int value)
604      {
605        fireAdjustmentValueChanged(id, type, value, getValueIsAdjusting());
606      }
607    
608      /**
609       * Helper method for firing adjustment events that can have their
610       * isAdjusting field modified.
611       *
612       * This is package private to avoid an accessor method.
613       *
614       * @param id the ID of the event
615       * @param type the type of the event
616       * @param value the value
617       * @param isAdjusting if the scrollbar is adjusting or not
618       */
619      void fireAdjustmentValueChanged(int id, int type, int value,
620                                              boolean isAdjusting)
621      {
622        Object[] adjustmentListeners = listenerList.getListenerList();
623        AdjustmentEvent adjustmentEvent = new AdjustmentEvent(this, id, type,
624                                                              value, isAdjusting);
625        for (int i = adjustmentListeners.length - 2; i >= 0; i -= 2)
626          {
627            if (adjustmentListeners[i] == AdjustmentListener.class)
628              ((AdjustmentListener) adjustmentListeners[i + 1]).adjustmentValueChanged(adjustmentEvent);
629          }
630      }
631    
632      /**
633       * This method returns the minimum size for this scroll bar.
634       *
635       * @return The minimum size.
636       */
637      public Dimension getMinimumSize()
638      {
639        return ui.getMinimumSize(this);
640      }
641    
642      /**
643       * This method returns the maximum size for this scroll bar.
644       *
645       * @return The maximum size.
646       */
647      public Dimension getMaximumSize()
648      {
649        return ui.getMaximumSize(this);
650      }
651    
652      /**
653       * This method overrides the setEnabled in JComponent.
654       * When the scroll bar is disabled, the knob cannot
655       * be moved.
656       *
657       * @param x Whether the scrollbar is enabled.
658       */
659      public void setEnabled(boolean x)
660      {
661        // nothing special needs to be done here since we 
662        // just check the enabled setting before changing the value.
663        super.setEnabled(x);
664      }
665    
666      /**
667       * Returns a string describing the attributes for the <code>JScrollBar</code>
668       * component, for use in debugging.  The return value is guaranteed to be 
669       * non-<code>null</code>, but the format of the string may vary between
670       * implementations.
671       *
672       * @return A string describing the attributes of the <code>JScrollBar</code>.
673       */
674      protected String paramString()
675      {
676        CPStringBuilder sb = new CPStringBuilder(super.paramString());
677        sb.append(",blockIncrement=").append(blockIncrement);
678        sb.append(",orientation=");
679        if (this.orientation == JScrollBar.HORIZONTAL)
680          sb.append("HORIZONTAL");
681        else 
682          sb.append("VERTICAL");
683        sb.append(",unitIncrement=").append(unitIncrement);
684        return sb.toString();
685      }
686    
687      /**
688       * Returns the object that provides accessibility features for this
689       * <code>JScrollBar</code> component.
690       *
691       * @return The accessible context (an instance of 
692       *     {@link AccessibleJScrollBar}).
693       */
694      public AccessibleContext getAccessibleContext()
695      {
696        if (accessibleContext == null)
697          accessibleContext = new AccessibleJScrollBar();
698        return accessibleContext;
699      }
700    }