001/* JScrollBar.java --
002   Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing;
040
041import gnu.java.lang.CPStringBuilder;
042
043import java.awt.Adjustable;
044import java.awt.Dimension;
045import java.awt.event.AdjustmentEvent;
046import java.awt.event.AdjustmentListener;
047import java.beans.PropertyChangeEvent;
048
049import javax.accessibility.Accessible;
050import javax.accessibility.AccessibleContext;
051import javax.accessibility.AccessibleRole;
052import javax.accessibility.AccessibleState;
053import javax.accessibility.AccessibleStateSet;
054import javax.accessibility.AccessibleValue;
055import javax.swing.event.ChangeEvent;
056import javax.swing.event.ChangeListener;
057import 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 */
066public 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}