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