2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java
.awt
.Dimension
;
42 import java
.awt
.MenuContainer
;
43 import java
.awt
.image
.ImageObserver
;
44 import java
.beans
.PropertyChangeEvent
;
45 import java
.io
.Serializable
;
46 import java
.util
.Dictionary
;
47 import java
.util
.Enumeration
;
48 import java
.util
.Hashtable
;
50 import javax
.accessibility
.Accessible
;
51 import javax
.accessibility
.AccessibleContext
;
52 import javax
.accessibility
.AccessibleRole
;
53 import javax
.accessibility
.AccessibleStateSet
;
54 import javax
.accessibility
.AccessibleValue
;
55 import javax
.swing
.event
.ChangeEvent
;
56 import javax
.swing
.event
.ChangeListener
;
57 import javax
.swing
.plaf
.SliderUI
;
60 * The JSlider is a Swing component that allows selection of a value within a
61 * range by adjusting a thumb in a track. The values for the minimum,
62 * maximum, extent and value are stored in a {@link
63 * DefaultBoundedRangeModel}.
66 * JSliders have the following properties:
70 * <tr><th> Property </th><th> Stored in </th><th> Bound? </th></tr>
71 * <tr><td> extent </td><td> model </td><td> no </td></tr>
72 * <tr><td> inverted </td><td> slider </td><td> yes </td></tr>
73 * <tr><td> labelTable </td><td> slider </td><td> yes </td></tr>
74 * <tr><td> majorTickSpacing </td><td> slider </td><td> yes </td></tr>
75 * <tr><td> maximum </td><td> model </td><td> no </td></tr>
76 * <tr><td> minimum </td><td> model </td><td> no </td></tr>
77 * <tr><td> minorTickSpacing </td><td> slider </td><td> yes </td></tr>
78 * <tr><td> model </td><td> slider </td><td> yes </td></tr>
79 * <tr><td> orientation </td><td> slider </td><td> yes </td></tr>
80 * <tr><td> paintLabels </td><td> slider </td><td> yes </td></tr>
81 * <tr><td> paintTicks </td><td> slider </td><td> yes </td></tr>
82 * <tr><td> snapToTicks </td><td> slider </td><td> no </td></tr>
83 * <tr><td> value </td><td> model </td><td> no </td></tr>
84 * <tr><td> valueIsAdjusting </td><td> model </td><td> no </td></tr>
88 * The various behavioral aspects of these properties follows:
93 * When non-bound properties stored in the slider change, the slider fires
94 * ChangeEvents to its ChangeListeners.
97 * When bound properties stored in the slider change, the slider fires
98 * PropertyChangeEvents to its PropertyChangeListeners
101 * If any of the model's properties change, it fires a ChangeEvent to its
102 * ChangeListeners, which include the slider.
105 * If the slider receives a ChangeEvent from its model, it will propagate the
106 * ChangeEvent to its ChangeListeners, with the ChangeEvent's "source"
107 * property set to refer to the slider, rather than the model.
111 public class JSlider
extends JComponent
implements SwingConstants
, Accessible
,
113 MenuContainer
, Serializable
116 private static final long serialVersionUID
= -1441275936141218479L;
121 // FIXME: This inner class is a complete stub and needs to be implemented
123 protected class AccessibleJSlider
extends JComponent
.AccessibleJComponent
124 implements AccessibleValue
126 private static final long serialVersionUID
= -6301740148041106789L;
129 * Creates a new AccessibleJSlider object.
131 protected AccessibleJSlider()
133 // Nothing to do here.
139 * @return DOCUMENT ME!
141 public AccessibleStateSet
getAccessibleStateSet()
149 * @return DOCUMENT ME!
151 public AccessibleRole
getAccessibleRole()
159 * @return DOCUMENT ME!
161 public AccessibleValue
getAccessibleValue()
169 * @return DOCUMENT ME!
171 public Number
getCurrentAccessibleValue()
177 * setCurrentAccessibleValue
183 public boolean setCurrentAccessibleValue(Number value0
)
189 * getMinimumAccessibleValue
193 public Number
getMinimumAccessibleValue()
199 * getMaximumAccessibleValue
203 public Number
getMaximumAccessibleValue()
209 /** Whether or not this slider paints its ticks. */
210 private transient boolean paintTicks
= false;
212 /** Whether or not this slider paints its track. */
213 private transient boolean paintTrack
= true;
215 /** Whether or not this slider paints its labels. */
216 private transient boolean paintLabels
= false;
219 * A dictionary of (Integer, Component) pairs where each Component is a
220 * JLabel and the Integer determines where the label will be painted.
222 private transient Dictionary labelTable
;
224 /** The model used to describe the slider. */
225 protected BoundedRangeModel sliderModel
;
227 /** The space between major ticks. */
228 protected int majorTickSpacing
;
230 /** The space between minor ticks. */
231 protected int minorTickSpacing
;
233 /** Whether the slider snaps its values to ticks. */
234 protected boolean snapToTicks
= false;
236 /** The orientation of the slider. */
237 protected int orientation
= HORIZONTAL
;
239 /** Whether the slider is inverted. */
240 private transient boolean isInverted
;
242 /** The ChangeListener that listens to the model. */
243 protected ChangeListener changeListener
;
245 /** The ChangeEvent that is passed to all listeners of this slider. */
246 protected transient ChangeEvent changeEvent
;
249 * Creates a new horizontal JSlider object with a minimum of 0, a maximum of
250 * 100, and a value of 50.
254 this(HORIZONTAL
, 0, 100, 50);
258 * Creates a new JSlider object with the given orientation and a minimum of
259 * 0, a maximum of 100, and a value of 50.
261 * @param orientation The orientation of the slider ({@link #HORIZONTAL} or
262 * {@link #VERTICAL}).
264 * @throws IllegalArgumentException if <code>orientation</code> is not one of
265 * the specified values.
267 public JSlider(int orientation
)
269 this(orientation
, 0, 100, 50);
273 * Creates a new horizontal JSlider object with the given maximum and
274 * minimum and a value that is halfway between the minimum and the
277 * @param minimum The minimum value of the JSlider.
278 * @param maximum The maximum value of the JSlider.
280 public JSlider(int minimum
, int maximum
)
282 this(HORIZONTAL
, minimum
, maximum
, (maximum
+ minimum
) / 2);
286 * Creates a new horizontal JSlider object with the given minimum, maximum,
289 * @param minimum The minimum value of the JSlider.
290 * @param maximum The maximum value of the JSlider.
291 * @param value The initial value of the JSlider.
293 public JSlider(int minimum
, int maximum
, int value
)
295 this(HORIZONTAL
, minimum
, maximum
, value
);
299 * Creates a new JSlider object with the given orientation, minimum,
300 * maximum, and value.
302 * @param orientation The orientation of the slider ({@link #HORIZONTAL} or
303 * {@link #VERTICAL}).
304 * @param minimum The minimum value of the JSlider.
305 * @param maximum The maximum value of the JSlider.
306 * @param value The initial value of the JSlider.
308 * @throws IllegalArgumentException if <code>orientation</code> is not one of
309 * the specified values.
311 public JSlider(int orientation
, int minimum
, int maximum
, int value
)
313 sliderModel
= new DefaultBoundedRangeModel(value
, 0, minimum
, maximum
);
314 if (orientation
!= HORIZONTAL
&& orientation
!= VERTICAL
)
315 throw new IllegalArgumentException(orientation
+ " is not a legal orientation");
316 this.orientation
= orientation
;
317 changeListener
= createChangeListener();
318 sliderModel
.addChangeListener(changeListener
);
323 * Creates a new horizontal JSlider object with the given model.
325 * @param model The model (<code>null</code> not permitted).
327 * @throws NullPointerException if <code>model</code> is <code>null</code>.
329 public JSlider(BoundedRangeModel model
)
332 changeListener
= createChangeListener();
333 sliderModel
.addChangeListener(changeListener
);
338 * This method returns the current value of the slider.
340 * @return The value of the slider stored in the model.
342 public int getValue()
344 return sliderModel
.getValue();
348 * This method sets the value of the slider.
350 * @param value The slider's new value.
352 public void setValue(int value
)
354 sliderModel
.setValue(value
);
358 * This method returns the slider's UI delegate.
360 * @return The slider's UI delegate.
362 public SliderUI
getUI()
364 return (SliderUI
) ui
;
368 * This method sets the slider's UI delegate.
370 * @param ui A SliderUI object to use with this slider.
372 public void setUI(SliderUI ui
)
378 * This method sets this slider's UI to the UIManager's default for the
379 * current look and feel.
381 public void updateUI()
383 setUI((SliderUI
) UIManager
.getUI(this));
389 * This method returns a name to identify which look and feel class will be
390 * the UI delegate for the slider.
392 * @return The Look and Feel classID. "SliderUI"
394 public String
getUIClassID()
400 * Creates a ChangeListener for this Slider.
402 * @return A new ChangeListener.
404 protected ChangeListener
createChangeListener()
406 return new ChangeListener()
408 public void stateChanged(ChangeEvent ce
)
410 // No need to trigger a repaint since the UI listens to the model
411 // as well. All we need to do is pass on the stateChanged event
419 * This method registers a listener to this slider. The listener will be
420 * informed of new ChangeEvents.
422 * @param listener The listener to register.
424 public void addChangeListener(ChangeListener listener
)
426 listenerList
.add(ChangeListener
.class, listener
);
430 * This method removes a listener from this slider.
432 * @param listener The listener to remove.
434 public void removeChangeListener(ChangeListener listener
)
436 listenerList
.remove(ChangeListener
.class, listener
);
440 * This method is called whenever the model fires a ChangeEvent. It should
441 * propagate the ChangeEvent to its listeners with a new ChangeEvent that
442 * identifies the slider as the source.
444 protected void fireStateChanged()
446 Object
[] changeListeners
= listenerList
.getListenerList();
447 if (changeEvent
== null)
448 changeEvent
= new ChangeEvent(this);
449 for (int i
= changeListeners
.length
- 2; i
>= 0; i
-= 2)
451 if (changeListeners
[i
] == ChangeListener
.class)
452 ((ChangeListener
) changeListeners
[i
+ 1]).stateChanged(changeEvent
);
457 * This method returns an array of all ChangeListeners listening to this
460 * @return An array of ChangeListeners listening to this slider.
462 public ChangeListener
[] getChangeListeners()
464 return (ChangeListener
[]) listenerList
.getListeners(ChangeListener
.class);
468 * This method returns the model of the slider.
470 * @return The slider's model.
472 public BoundedRangeModel
getModel()
478 * This method changes the "model" property. It also needs to unregister
479 * any listeners to the old model and register any listeners to the new
482 * @param model The model to use with the slider.
484 public void setModel(BoundedRangeModel model
)
486 // I didn't do the null pointer check on purpose.
487 // If you try it with Sun's, it'll go ahead and set it to null
488 // and bork the next time it tries to access the model.
489 if (model
!= sliderModel
)
491 BoundedRangeModel oldModel
= sliderModel
;
493 oldModel
.removeChangeListener(changeListener
);
494 sliderModel
.addChangeListener(changeListener
);
495 firePropertyChange("model", oldModel
, sliderModel
);
500 * This method returns the minimum value of the slider.
502 * @return The minimum value of the slider.
504 public int getMinimum()
506 return sliderModel
.getMinimum();
510 * This method sets the minimum value of the slider.
512 * @param minimum The minimum value of the slider.
514 public void setMinimum(int minimum
)
516 int old
= sliderModel
.getMinimum();
517 sliderModel
.setMinimum(minimum
);
519 firePropertyChange("minimum", old
, minimum
);
523 * This method returns the maximum value of the slider.
525 * @return The maximum value of the slider.
527 public int getMaximum()
529 return sliderModel
.getMaximum();
533 * This method sets the maximum value of the slider.
535 * @param maximum The maximum value of the slider.
537 public void setMaximum(int maximum
)
539 int old
= sliderModel
.getMaximum();
540 sliderModel
.setMaximum(maximum
);
542 firePropertyChange("maximum", old
, maximum
);
546 * This method returns this slider's isAdjusting value which is true if the
547 * thumb is being dragged.
549 * @return The slider's isAdjusting value.
551 public boolean getValueIsAdjusting()
553 return sliderModel
.getValueIsAdjusting();
557 * This method sets the isAdjusting value for the slider.
559 * @param adjusting The slider's isAdjusting value.
561 public void setValueIsAdjusting(boolean adjusting
)
563 sliderModel
.setValueIsAdjusting(adjusting
);
567 * This method returns the extent value for this slider.
569 * @return The extent value for this slider.
571 public int getExtent()
573 return sliderModel
.getExtent();
577 * This method sets the extent value for this slider.
579 * @param extent The extent value for this slider.
581 public void setExtent(int extent
)
583 sliderModel
.setExtent(extent
);
587 * This method returns the slider orientation.
589 * @return The orientation of the slider.
591 public int getOrientation()
597 * This method changes the "orientation" property of this slider. If the
598 * orientation is not VERTICAL or HORIZONTAL, this method does nothing.
600 * @param orientation The orientation of this slider.
602 public void setOrientation(int orientation
)
604 if (orientation
!= VERTICAL
&& orientation
!= HORIZONTAL
)
605 throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
606 if (orientation
!= this.orientation
)
608 int oldOrientation
= this.orientation
;
609 this.orientation
= orientation
;
610 firePropertyChange("orientation", oldOrientation
,
616 * This method returns the label table for this slider.
618 * @return The label table for this slider.
620 public Dictionary
getLabelTable()
626 * This method changes the "labelTable" property of this slider.
628 * @param table The label table for this slider.
630 public void setLabelTable(Dictionary table
)
632 if (table
!= labelTable
)
634 Dictionary oldTable
= labelTable
;
636 firePropertyChange("labelTable", oldTable
, labelTable
);
641 * This method is called to reset UI delegates for the labels in the
642 * labelTable to a default for the current look and feel.
644 protected void updateLabelUIs()
646 if (labelTable
== null)
648 for (Enumeration list
= labelTable
.elements(); list
.hasMoreElements();)
650 JLabel label
= (JLabel
) list
.nextElement();
656 * Creates a hashtable of (Integer, JLabel) pairs that can be used as a
657 * label table for this slider. The labels will start from the sliders
658 * minimum and increase by the increment. Each label will have a text
659 * string indicating their integer value.
661 * @param increment The increment between labels (must be > 0).
663 * @return A hashtable with the labels and their keys.
665 * @throws IllegalArgumentException if <code>increment</code> is not greater
668 public Hashtable
createStandardLabels(int increment
)
670 return createStandardLabels(increment
, sliderModel
.getMinimum());
674 * Creates a hashtable of (Integer, JLabel) pairs that can be used as a
675 * label table for this slider. The labels will start from the given start
676 * value and increase by the increment. Each label will have a text string
677 * indicating its integer value.
679 * @param increment The increment between labels (must be > 0).
680 * @param start The value to start from.
682 * @return A hashtable with the labels and their keys.
684 * @throws IllegalArgumentException if <code>increment</code> is not greater
685 * than zero, or <code>start</code> is not within the range of the
688 public Hashtable
createStandardLabels(int increment
, int start
)
691 throw new IllegalArgumentException("Requires 'increment' > 0.");
692 if (start
< getMinimum() || start
> getMaximum())
693 throw new IllegalArgumentException("The 'start' value is out of range.");
694 Hashtable table
= new Hashtable();
698 int max
= sliderModel
.getMaximum();
700 for (int i
= start
; i
<= max
; i
+= increment
)
702 label
= new JLabel(String
.valueOf(i
));
703 label
.setVerticalAlignment(CENTER
);
704 label
.setHorizontalAlignment(CENTER
);
706 // Make sure these labels have the width and height
708 dim
= label
.getPreferredSize();
709 label
.setBounds(label
.getX(), label
.getY(),
710 (int) dim
.getWidth(),
711 (int) dim
.getHeight());
712 table
.put(new Integer(i
), label
);
718 * This method returns whether the slider is inverted. Horizontal sliders
719 * that are not inverted will have the minimums on the left. If they are
720 * inverted, the minimums will be on the right. Vertical sliders that are
721 * not inverted will have the minimums at the bottom. If they are inverted,
722 * the minimums will be at the top.
724 * @return Whether this slider is inverted.
726 public boolean getInverted()
732 * This method changes the "inverted" property for this slider.Horizontal
733 * sliders that are not inverted will have the minimums on the left. If
734 * they are inverted, the minimums will be on the right. Vertical sliders
735 * that are not inverted will have the minimums at the bottom. If they are
736 * inverted, the minimums will be at the top. However, if the slider's
737 * componentOrientation is set to RIGHT_TO_LEFT, then everything gets
740 * @param inverted Whether the slider should be inverted.
742 public void setInverted(boolean inverted
)
744 if (isInverted
!= inverted
)
746 boolean oldInverted
= isInverted
;
747 isInverted
= inverted
;
748 firePropertyChange("inverted", oldInverted
, isInverted
);
753 * This method returns the amount of units between each major tick mark.
755 * @return The amount of units between each major tick mark.
757 public int getMajorTickSpacing()
759 return majorTickSpacing
;
763 * This method changes the "majorTickSpacing" property for this slider. The
764 * major tick spacing is the amount of units between each major tick mark.
766 * @param spacing The amount of units between each major tick mark.
768 public void setMajorTickSpacing(int spacing
)
770 if (majorTickSpacing
!= spacing
)
772 int oldSpacing
= majorTickSpacing
;
773 majorTickSpacing
= spacing
;
774 firePropertyChange("majorTickSpacing", oldSpacing
,
780 * This method returns the amount of units between each minor tick mark.
782 * @return The amount of units between each minor tick mark.
784 public int getMinorTickSpacing()
786 return minorTickSpacing
;
790 * This method changes the "minorTickSpacing" property for this slider. The
791 * minor tick spacing is the amount of units between each minor tick mark.
793 * @param spacing The amount of units between each minor tick mark.
795 public void setMinorTickSpacing(int spacing
)
797 if (minorTickSpacing
!= spacing
)
799 int oldSpacing
= minorTickSpacing
;
800 minorTickSpacing
= spacing
;
801 firePropertyChange("minorTickSpacing", oldSpacing
,
807 * This method returns whether this slider is snapping to ticks. Sliders
808 * that snap to ticks will automatically move the thumb to the nearest tick
811 * @return Whether this slider snaps to ticks.
813 public boolean getSnapToTicks()
819 * This method sets whether this slider will snap to ticks. Sliders that
820 * snap to ticks will automatically move the thumb to the nearest tick
823 * @param snap Whether this slider snaps to ticks.
825 public void setSnapToTicks(boolean snap
)
827 if (snap
!= snapToTicks
)
830 firePropertyChange("snapToTicks", !snap
, snap
);
835 * This method returns whether the slider will paint its tick marks. In
836 * addition to setting this property to true, one of minor tick spacing or
837 * major tick spacing must be set to a value greater than 0 in order for
838 * ticks to be painted.
840 * @return Whether ticks will be painted.
842 public boolean getPaintTicks()
848 * This method changes the "paintTicks" property for this slider. In
849 * addition to setting this property to true, one of minor tick spacing or
850 * major tick spacing must be set to a value greater than 0 in order for
851 * ticks to be painted.
853 * @param paint Whether ticks will be painted.
855 public void setPaintTicks(boolean paint
)
857 if (paint
!= paintTicks
)
859 boolean oldPaintTicks
= paintTicks
;
861 firePropertyChange("paintTicks", oldPaintTicks
, paintTicks
);
866 * This method returns whether the track will be painted.
868 * @return Whether the track will be painted.
870 public boolean getPaintTrack()
876 * Sets the flag that controls whether or not the track is painted, and
877 * sends a {@link PropertyChangeEvent} (for the "paintTrack" property) to all
878 * registered listeners.
880 * @param paint Whether the track will be painted.
882 public void setPaintTrack(boolean paint
)
884 if (paintTrack
!= paint
)
887 firePropertyChange("paintTrack", !paint
, paint
);
892 * This method returns whether labels will be painted.
894 * @return Whether labels will be painted.
896 public boolean getPaintLabels()
902 * This method changes the "paintLabels" property.
904 * @param paint Whether labels will be painted.
906 public void setPaintLabels(boolean paint
)
908 if (paint
!= paintLabels
)
911 if (paint
&& majorTickSpacing
> 0)
912 labelTable
= createStandardLabels(majorTickSpacing
);
913 firePropertyChange("paintLabels", !paint
, paint
);
918 * This method is used primarily for debugging purposes and returns a string
919 * that can be used to represent this slider.
921 * @return A string representing this slider.
923 protected String
paramString()
931 * @return DOCUMENT ME!
933 public AccessibleContext
getAccessibleContext()
935 if (accessibleContext
== null)
936 accessibleContext
= new AccessibleJSlider();
938 return accessibleContext
;