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