001/* Scrollbar.java -- AWT Scrollbar widget
002   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
003   Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039
040package java.awt;
041
042import java.awt.event.AdjustmentEvent;
043import java.awt.event.AdjustmentListener;
044import java.awt.peer.ScrollbarPeer;
045import java.util.EventListener;
046
047import javax.accessibility.Accessible;
048import javax.accessibility.AccessibleContext;
049import javax.accessibility.AccessibleRole;
050import javax.accessibility.AccessibleState;
051import javax.accessibility.AccessibleStateSet;
052import javax.accessibility.AccessibleValue;
053
054/**
055 * This class implements a scrollbar widget.
056 *
057 * @author Aaron M. Renn (arenn@urbanophile.com)
058 * @author Tom Tromey (tromey@cygnus.com)
059 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
060 */
061public class Scrollbar extends Component implements Accessible, Adjustable
062{
063  // FIXME: Serialization readObject/writeObject
064
065  /**
066   * Constant indicating that a scrollbar is horizontal.
067   */
068  public static final int HORIZONTAL = 0;
069
070  /**
071   * Constant indicating that a scrollbar is vertical.
072   */
073  public static final int VERTICAL = 1;
074
075  /**
076   * Serialization Constant.
077   */
078  private static final long serialVersionUID = 8451667562882310543L;
079
080  /**
081   * @serial The amount by which the value of the scrollbar is changed
082   * when incrementing in line mode.
083   */
084  private int lineIncrement;
085
086  /**
087   * @serial The amount by which the value of the scrollbar is changed
088   * when incrementing in page mode.
089   */
090  private int pageIncrement;
091
092  /**
093   * @serial The maximum value for this scrollbar
094   */
095  private int maximum;
096
097  /**
098   * @serial The minimum value for this scrollbar
099   */
100  private int minimum;
101
102  /**
103   * @serial The orientation of this scrollbar, which will be either
104   * the <code>HORIZONTAL</code> or <code>VERTICAL</code> constant
105   * from this class.
106   */
107  private int orientation;
108
109  /**
110   * @serial The current value of this scrollbar.
111   */
112  private int value;
113
114  /**
115   * @serial The width of the scrollbar's thumb, which is relative
116   * to the minimum and maximum value of the scrollbar.
117   */
118  private int visibleAmount;
119
120  /**
121   * List of AdjustmentListener's.
122   */
123  private AdjustmentListener adjustment_listeners;
124
125  /**
126   * true if the scrollbar is adjusting, false otherwise.
127   */
128  private transient boolean valueIsAdjusting = false;
129
130  /**
131   * The number used to generate the name returned by getName.
132   */
133  private static transient long next_scrollbar_number;
134
135  /**
136   * Initializes a new instance of <code>Scrollbar</code> with a
137   * vertical orientation and default values for all other parameters.
138   *
139   * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true,
140   */
141  public Scrollbar()
142  {
143    this(VERTICAL);
144  }
145
146  /**
147   * Initializes a new instance of <code>Scrollbar</code> with the
148   * specified orientation and default values for all other parameters.
149   * The orientation must be either the constant <code>HORIZONTAL</code> or
150   * <code>VERTICAL</code> from this class.  An incorrect value will throw
151   * an exception.
152   *
153   * @param orientation The orientation of this scrollbar.
154   *
155   * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true,
156   * @exception IllegalArgumentException If the orientation value is not valid.
157   */
158  public Scrollbar(int orientation) throws IllegalArgumentException
159  {
160    this(orientation, 0, 10, 0, 100);
161  }
162
163  /**
164   * Initializes a new instance of <code>Scrollbar</code> with the
165   * specified parameters.  The orientation must be either the constant
166   * <code>HORIZONTAL</code> or <code>VERTICAL</code>.  An incorrect value
167   * will throw an exception.  Inconsistent values for other parameters
168   * are silently corrected to valid values.
169   *
170   * @param orientation The orientation of this scrollbar.
171   * @param value The initial value of the scrollbar.
172   * @param visibleAmount The width of the scrollbar thumb.
173   * @param minimum The minimum value of the scrollbar.
174   * @param maximum The maximum value of the scrollbar.
175   *
176   * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true,
177   * @exception IllegalArgumentException If the orientation value is not valid.
178   */
179  public Scrollbar(int orientation, int value, int visibleAmount, int minimum,
180                   int maximum) throws IllegalArgumentException
181  {
182    if (GraphicsEnvironment.isHeadless())
183      throw new HeadlessException();
184
185    if ((orientation != HORIZONTAL) && (orientation != VERTICAL))
186      throw new IllegalArgumentException("Bad orientation value: "
187                                         + orientation);
188
189    this.orientation = orientation;
190
191    setValues(value, visibleAmount, minimum, maximum);
192
193    // Default is 1 according to online docs.
194    lineIncrement = 1;
195
196    // Default is 10 according to javadocs.
197    pageIncrement = 10;
198  }
199
200  /**
201   * Returns the orientation constant for this object.
202   *
203   * @return The orientation constant for this object.
204   */
205  public int getOrientation()
206  {
207    return orientation;
208  }
209
210  /**
211   * Sets the orientation of this scrollbar to the specified value.  This
212   * value must be either the constant <code>HORIZONTAL</code> or
213   * <code>VERTICAL</code> from this class or an exception will be thrown.
214   *
215   * @param orientation The new orientation value.
216   *
217   * @exception IllegalArgumentException If the orientation value is not valid.
218   */
219  public void setOrientation(int orientation)
220  {
221    if ((orientation != HORIZONTAL) && (orientation != VERTICAL))
222      throw new IllegalArgumentException("Bad orientation value: "
223                                         + orientation);
224
225    // FIXME: Communicate to peer?  Or must this be called before peer creation?
226    this.orientation = orientation;
227  }
228
229  /**
230   * Returns the current value for this scrollbar.
231   *
232   * @return The current value for this scrollbar.
233   */
234  public int getValue()
235  {
236    return value;
237  }
238
239  /**
240   * Sets the current value for this scrollbar to the specified value.
241   * If this is inconsistent with the minimum and maximum values for this
242   * scrollbar, the value is silently adjusted.
243   *
244   * @param value The new value for this scrollbar.
245   */
246  public void setValue(int value)
247  {
248    setValues(value, visibleAmount, minimum, maximum);
249  }
250
251  /**
252   * Returns the maximum value for this scrollbar.
253   *
254   * @return The maximum value for this scrollbar.
255   */
256  public int getMaximum()
257  {
258    return maximum;
259  }
260
261  /**
262   * Sets the maximum value for this scrollbar to the specified value.
263   * If the value is less than the current minimum value, it is silent
264   * set to equal the minimum value.
265   *
266   * @param maximum The new maximum value for this scrollbar.
267   */
268  public void setMaximum(int maximum)
269  {
270    setValues(value, visibleAmount, minimum, maximum);
271  }
272
273  /**
274   * Returns the minimum value for this scrollbar.
275   *
276   * @return The minimum value for this scrollbar.
277   */
278  public int getMinimum()
279  {
280    return minimum;
281  }
282
283  /**
284   * Sets the minimum value for this scrollbar to the specified value.  If
285   * this is not consistent with the current value and maximum, it is
286   * silently adjusted to be consistent.
287   *
288   * @param minimum The new minimum value for this scrollbar.
289   */
290  public void setMinimum(int minimum)
291  {
292    setValues(value, visibleAmount, minimum, maximum);
293  }
294
295  /**
296   * Returns the width of the scrollbar's thumb, in units relative to the
297   * maximum and minimum value of the scrollbar.
298   *
299   * @return The width of the scrollbar's thumb.
300   */
301  public int getVisibleAmount()
302  {
303    return getVisible();
304  }
305
306  /**
307   * Returns the width of the scrollbar's thumb, in units relative to the
308   * maximum and minimum value of the scrollbar.
309   *
310   * @return The width of the scrollbar's thumb.
311   *
312   * @deprecated This method is deprecated in favor of
313   * <code>getVisibleAmount()</code>.
314   */
315  public int getVisible()
316  {
317    return visibleAmount;
318  }
319
320  /**
321   * Sets the width of the scrollbar's thumb, in units relative to the
322   * maximum and minimum value of the scrollbar.
323   *
324   * @param visibleAmount The new visible amount value of the scrollbar.
325   */
326  public void setVisibleAmount(int visibleAmount)
327  {
328    setValues(value, visibleAmount, minimum, maximum);
329  }
330
331  /**
332   * Sets the current value, visible amount, minimum, and maximum for this
333   * scrollbar.  These values are adjusted to be internally consistent
334   * if necessary.
335   *
336   * @param value The new value for this scrollbar.
337   * @param visibleAmount The new visible amount for this scrollbar.
338   * @param minimum The new minimum value for this scrollbar.
339   * @param maximum The new maximum value for this scrollbar.
340   */
341  public synchronized void setValues(int value, int visibleAmount,
342                                     int minimum, int maximum)
343  {
344    if (visibleAmount <= 0)
345      visibleAmount = 1;
346
347    if (maximum <= minimum)
348      maximum = minimum + 1;
349
350    if (value < minimum)
351      value = minimum;
352
353    if (visibleAmount > maximum - minimum)
354      visibleAmount = maximum - minimum;
355
356    // According to documentation, the actual maximum
357    // value is (maximum - visibleAmount)
358    if (value > maximum - visibleAmount)
359      value = maximum - visibleAmount;
360
361    ScrollbarPeer peer = (ScrollbarPeer) getPeer();
362    if (peer != null
363        && (this.value != value || this.visibleAmount != visibleAmount
364            || this.minimum != minimum || this.maximum != maximum))
365      peer.setValues(value, visibleAmount, minimum, maximum);
366
367    this.value = value;
368    this.visibleAmount = visibleAmount;
369    this.minimum = minimum;
370    this.maximum = maximum;
371  }
372
373  /**
374   * Returns the value added or subtracted when the user activates the scrollbar
375   * scroll by a "unit" amount.
376   *
377   * @return The unit increment value.
378   */
379  public int getUnitIncrement()
380  {
381    return getLineIncrement();
382  }
383
384  /**
385   * Returns the value added or subtracted when the user selects the scrollbar
386   * scroll by a "unit" amount control.
387   *
388   * @return The unit increment value.
389   *
390   * @deprecated This method is deprecated in favor of
391   * <code>getUnitIncrement()</code>.
392   */
393  public int getLineIncrement()
394  {
395    return lineIncrement;
396  }
397
398  /**
399   * Sets the value added or subtracted to the scrollbar value when the
400   * user selects the scroll by a "unit" amount control.
401   *
402   * @param unitIncrement The new unit increment amount.
403   */
404  public synchronized void setUnitIncrement(int unitIncrement)
405  {
406    setLineIncrement(unitIncrement);
407  }
408
409  /**
410   * Sets the value added or subtracted to the scrollbar value when the
411   * user selects the scroll by a "unit" amount control.
412   *
413   * @param lineIncrement The new unit increment amount.
414   *
415   * @deprecated This method is deprecated in favor of
416   * <code>setUnitIncrement()</code>.
417   */
418  public void setLineIncrement(int lineIncrement)
419  {
420    if (lineIncrement < 0)
421      throw new IllegalArgumentException("Unit increment less than zero.");
422
423    if (lineIncrement == 0)
424      lineIncrement = 1;
425
426   if (lineIncrement == this.lineIncrement)
427      return;
428
429    this.lineIncrement = lineIncrement;
430
431    ScrollbarPeer peer = (ScrollbarPeer) getPeer();
432    if (peer != null)
433      peer.setLineIncrement(this.lineIncrement);
434  }
435
436  /**
437   * Returns the value added or subtracted when the user activates the scrollbar
438   * scroll by a "block" amount.
439   *
440   * @return The block increment value.
441   */
442  public int getBlockIncrement()
443  {
444    return getPageIncrement();
445  }
446
447  /**
448   * Returns the value added or subtracted when the user selects the scrollbar
449   * scroll by a "block" amount control.
450   *
451   * @return The block increment value.
452   *
453   * @deprecated This method is deprecated in favor of
454   * <code>getBlockIncrement()</code>.
455   */
456  public int getPageIncrement()
457  {
458    return pageIncrement;
459  }
460
461  /**
462   * Sets the value added or subtracted to the scrollbar value when the
463   * user selects the scroll by a "block" amount control.
464   *
465   * @param blockIncrement The new block increment amount.
466   */
467  public synchronized void setBlockIncrement(int blockIncrement)
468  {
469    setPageIncrement(blockIncrement);
470  }
471
472  /**
473   * Sets the value added or subtracted to the scrollbar value when the
474   * user selects the scroll by a "block" amount control.
475   *
476   * @param pageIncrement The new block increment amount.
477   *
478   * @deprecated This method is deprecated in favor of
479   * <code>setBlockIncrement()</code>.
480   */
481  public void setPageIncrement(int pageIncrement)
482  {
483    if (pageIncrement < 0)
484      throw new IllegalArgumentException("Block increment less than zero.");
485
486    if (pageIncrement == 0)
487      pageIncrement = 1;
488
489    if (pageIncrement == this.pageIncrement)
490      return;
491
492    this.pageIncrement = pageIncrement;
493
494    ScrollbarPeer peer = (ScrollbarPeer) getPeer();
495    if (peer != null)
496      peer.setPageIncrement(this.pageIncrement);
497  }
498
499  /**
500   * Notifies this object to create its native peer.
501   */
502  public synchronized void addNotify()
503  {
504    if (peer == null)
505      peer = getToolkit().createScrollbar(this);
506    super.addNotify();
507  }
508
509  /**
510   * Adds a new adjustment listener to the list of registered listeners
511   * for this object.
512   *
513   * @param listener The listener to add.
514   */
515  public synchronized void addAdjustmentListener(AdjustmentListener listener)
516  {
517    adjustment_listeners = AWTEventMulticaster.add(adjustment_listeners,
518                                                   listener);
519    enableEvents(AWTEvent.ADJUSTMENT_EVENT_MASK);
520  }
521
522  /**
523   * Removes the specified listener from the list of registered listeners
524   * for this object.
525   *
526   * @param listener The listener to remove.
527   */
528  public synchronized void removeAdjustmentListener(AdjustmentListener listener)
529  {
530    adjustment_listeners = AWTEventMulticaster.remove(adjustment_listeners,
531                                                      listener);
532  }
533
534  /**
535   * Processes events for this scrollbar.  It does this by calling
536   * <code>processAdjustmentEvent()</code> if the event is an instance of
537   * <code>AdjustmentEvent</code>, otherwise it calls the superclass to
538   * process the event.
539   *
540   * @param event The event to process.
541   */
542  protected void processEvent(AWTEvent event)
543  {
544    if (event instanceof AdjustmentEvent)
545      processAdjustmentEvent((AdjustmentEvent) event);
546    else
547      super.processEvent(event);
548  }
549
550  /**
551   * Processes adjustment events for this object by dispatching them to
552   * any registered listeners.  Note that this method will only be called
553   * if adjustment events are enabled.  This will happen automatically if
554   * any listeners are registered.  Otherwise, it can be enabled by a
555   * call to <code>enableEvents()</code>.
556   *
557   * @param event The event to process.
558   */
559  protected void processAdjustmentEvent(AdjustmentEvent event)
560  {
561    value = event.getValue();
562    if (adjustment_listeners != null)
563      adjustment_listeners.adjustmentValueChanged(event);
564  }
565
566  /**
567   * Package private method to determine whether to call
568   * processEvent() or not.  Will handle events from peer and update
569   * the current value.
570   */
571  void dispatchEventImpl(AWTEvent e)
572  {
573    if (e.id <= AdjustmentEvent.ADJUSTMENT_LAST
574        && e.id >= AdjustmentEvent.ADJUSTMENT_FIRST)
575      {
576        AdjustmentEvent ae = (AdjustmentEvent) e;
577        boolean adjusting = ae.getValueIsAdjusting();
578        if (adjusting)
579          setValueIsAdjusting(true);
580        try
581          {
582            setValue(((AdjustmentEvent) e).getValue());
583            if (adjustment_listeners != null
584                || (eventMask & AWTEvent.ADJUSTMENT_EVENT_MASK) != 0)
585              processEvent(e);
586          }
587        finally
588          {
589            if (adjusting)
590              setValueIsAdjusting(false);
591          }
592      }
593    else
594      super.dispatchEventImpl(e);
595  }
596
597  /**
598   * Returns a debugging string for this object.
599   *
600   * @return A debugging string for this object.
601   */
602  protected String paramString()
603  {
604    return ("value=" + getValue() + ",visibleAmount=" + getVisibleAmount()
605            + ",minimum=" + getMinimum() + ",maximum=" + getMaximum()
606            + ",pageIncrement=" + pageIncrement + ",lineIncrement="
607            + lineIncrement + ",orientation="
608            + (orientation == HORIZONTAL ? "HORIZONTAL" : "VERTICAL")
609            + super.paramString());
610  }
611
612  /**
613   * Returns an array of all the objects currently registered as FooListeners
614   * upon this <code>Scrollbar</code>. FooListeners are registered using the
615   * addFooListener method.
616   *
617   * @exception ClassCastException If listenerType doesn't specify a class or
618   * interface that implements java.util.EventListener.
619   */
620  public <T extends EventListener> T[] getListeners(Class<T> listenerType)
621  {
622    if (listenerType == AdjustmentListener.class)
623      return AWTEventMulticaster.getListeners(adjustment_listeners,
624                                              listenerType);
625
626    return super.getListeners(listenerType);
627  }
628
629  /**
630   * Returns an array of all registered adjustment listeners.
631   */
632  public AdjustmentListener[] getAdjustmentListeners()
633  {
634    return (AdjustmentListener[]) getListeners(AdjustmentListener.class);
635  }
636
637  /**
638   * Returns true if the value is in the process of changing.
639   *
640   * @since 1.4
641   */
642  public boolean getValueIsAdjusting()
643  {
644    return valueIsAdjusting;
645  }
646
647  /**
648   * Sets the value of valueIsAdjusting.
649   *
650   * @since 1.4
651   */
652  public void setValueIsAdjusting(boolean valueIsAdjusting)
653  {
654    this.valueIsAdjusting = valueIsAdjusting;
655  }
656
657  /**
658   * Generate a unique name for this scroll bar.
659   *
660   * @return A unique name for this scroll bar.
661   */
662  String generateName()
663  {
664    return "scrollbar" + getUniqueLong();
665  }
666
667  private static synchronized long getUniqueLong()
668  {
669    return next_scrollbar_number++;
670  }
671
672  /**
673   * This class provides accessibility support for the
674   * scrollbar.
675   *
676   * @author Jerry Quinn (jlquinn@optonline.net)
677   * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
678   */
679  protected class AccessibleAWTScrollBar extends AccessibleAWTComponent
680    implements AccessibleValue
681  {
682    /**
683     * Serialization constant to match JDK 1.5
684     */
685    private static final long serialVersionUID = -344337268523697807L;
686
687    /**
688     * Returns the role of this accessible object.
689     *
690     * @return the instance of <code>AccessibleRole</code>,
691     * which describes this object.
692     *
693     * @see javax.accessibility.AccessibleRole
694     */
695    public AccessibleRole getAccessibleRole()
696    {
697      return AccessibleRole.SCROLL_BAR;
698    }
699
700    /**
701     * Returns the state set of this accessible object.
702     *
703     * @return a set of <code>AccessibleState</code>s which
704     * represent the current state of the accessible object.
705     *
706     * @see javax.accessibility.AccessibleState
707     * @see javax.accessibility.AccessibleStateSet
708     */
709    public AccessibleStateSet getAccessibleStateSet()
710    {
711      AccessibleStateSet states = super.getAccessibleStateSet();
712      if (getOrientation() == HORIZONTAL)
713        states.add(AccessibleState.HORIZONTAL);
714      else
715        states.add(AccessibleState.VERTICAL);
716      if (getValueIsAdjusting())
717        states.add(AccessibleState.BUSY);
718      return states;
719    }
720
721    /**
722     * Returns an implementation of the <code>AccessibleValue</code>
723     * interface for this accessible object.  In this case, the
724     * current instance is simply returned (with a more appropriate
725     * type), as it also implements the accessible value as well as
726     * the context.
727     *
728     * @return the accessible value associated with this context.
729     *
730     * @see javax.accessibility.AccessibleValue
731     */
732    public AccessibleValue getAccessibleValue()
733    {
734      return this;
735    }
736
737    /**
738     * Returns the current value of this accessible object.
739     * In this case, this is the same as the value for
740     * the scrollbar, wrapped in an <code>Integer</code>
741     * object.
742     *
743     * @return the numeric value of this scrollbar.
744     *
745     * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue()
746     */
747    public Number getCurrentAccessibleValue()
748    {
749      return new Integer(getValue());
750    }
751
752    /**
753     * Sets the current value of this accessible object
754     * to that supplied.  In this case, the value of the
755     * scrollbar is set, and this method always returns
756     * true.
757     *
758     * @param number the new accessible value.
759     *
760     * @return true if the value was set.
761     *
762     * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number)
763     */
764    public boolean setCurrentAccessibleValue(Number number)
765    {
766      setValue(number.intValue());
767      return true;
768    }
769
770    /**
771     * Returns the minimum acceptable accessible value used
772     * by this object.  In this case, this is the same as
773     * the minimum value of the scrollbar, wrapped in an
774     * object.
775     *
776     * @return the minimum value of this scrollbar.
777     *
778     * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue()
779     */
780    public Number getMinimumAccessibleValue()
781    {
782      return new Integer(getMinimum());
783    }
784
785    /**
786     * Returns the maximum acceptable accessible value used
787     * by this object.  In this case, this is the same as
788     * the maximum value of the scrollbar, wrapped in an
789     * object.
790     *
791     * @return the maximum value of this scrollbar.
792     *
793     * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue()
794     */
795    public Number getMaximumAccessibleValue()
796    {
797      return new Integer(getMaximum());
798    }
799  }
800
801  /**
802   * Gets the AccessibleContext associated with this <code>Scrollbar</code>.
803   * The context is created, if necessary.
804   *
805   * @return the associated context
806   */
807  public AccessibleContext getAccessibleContext()
808  {
809    /* Create the context if this is the first request */
810    if (accessibleContext == null)
811      accessibleContext = new AccessibleAWTScrollBar();
812
813    return accessibleContext;
814  }
815}