29 import javax.swing.plaf.*;
30 import javax.accessibility.*;
31
32 import java.io.Serializable;
33 import java.io.ObjectOutputStream;
34 import java.io.IOException;
35
36 import java.awt.*;
37 import java.util.*;
38 import java.beans.*;
39
40
41 /**
42 * A component that lets the user graphically select a value by sliding
43 * a knob within a bounded interval. The knob is always positioned
44 * at the points that match integer values within the specified interval.
45 * <p>
46 * The slider can show both
47 * major tick marks, and minor tick marks between the major ones. The number of
48 * values between the tick marks is controlled with
49 * <code>setMajorTickSpacing</code> and <code>setMinorTickSpacing</code>.
50 * Painting of tick marks is controlled by {@code setPaintTicks}.
51 * <p>
52 * Sliders can also print text labels at regular intervals (or at
53 * arbitrary locations) along the slider track. Painting of labels is
54 * controlled by {@code setLabelTable} and {@code setPaintLabels}.
55 * <p>
56 * For further information and examples see
57 * <a
58 href="http://docs.oracle.com/javase/tutorial/uiswing/components/slider.html">How to Use Sliders</a>,
59 * a section in <em>The Java Tutorial.</em>
60 * <p>
61 * <strong>Warning:</strong> Swing is not thread safe. For more
62 * information see <a
63 * href="package-summary.html#threading">Swing's Threading
64 * Policy</a>.
65 * <p>
66 * <strong>Warning:</strong>
67 * Serialized objects of this class will not be compatible with
68 * future Swing releases. The current serialization support is
69 * appropriate for short term storage or RMI between applications running
70 * the same version of Swing. As of 1.4, support for long term storage
71 * of all JavaBeans™
72 * has been added to the <code>java.beans</code> package.
73 * Please see {@link java.beans.XMLEncoder}.
74 *
75 * @beaninfo
76 * attribute: isContainer false
77 * description: A component that supports selecting a integer value from a range.
78 *
79 * @author David Kloba
80 * @since 1.2
81 */
82 @SuppressWarnings("serial") // Same-version serialization only
83 public class JSlider extends JComponent implements SwingConstants, Accessible {
84 /**
85 * @see #getUIClassID
86 * @see #readObject
87 */
88 private static final String uiClassID = "SliderUI";
89
90 private boolean paintTicks = false;
91 private boolean paintTrack = true;
92 private boolean paintLabels = false;
141 @SuppressWarnings("rawtypes")
142 private Dictionary labelTable;
143 // For better source compatibility, the labelTable field and
144 // associated getter and setter methods are being left as raw
145 // types.
146
147 /**
148 * The changeListener (no suffix) is the listener we add to the
149 * slider's model. This listener is initialized to the
150 * {@code ChangeListener} returned from {@code createChangeListener},
151 * which by default just forwards events
152 * to {@code ChangeListener}s (if any) added directly to the slider.
153 *
154 * @see #addChangeListener
155 * @see #createChangeListener
156 */
157 protected ChangeListener changeListener = createChangeListener();
158
159
160 /**
161 * Only one <code>ChangeEvent</code> is needed per slider instance since the
162 * event's only (read-only) state is the source property. The source
163 * of events generated here is always "this". The event is lazily
164 * created the first time that an event notification is fired.
165 *
166 * @see #fireStateChanged
167 */
168 protected transient ChangeEvent changeEvent = null;
169
170
171 private void checkOrientation(int orientation) {
172 switch (orientation) {
173 case VERTICAL:
174 case HORIZONTAL:
175 break;
176 default:
177 throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
178 }
179 }
180
181
182 /**
183 * Creates a horizontal slider with the range 0 to 100 and
184 * an initial value of 50.
185 */
186 public JSlider() {
187 this(HORIZONTAL, 0, 100, 50);
188 }
189
190
191 /**
192 * Creates a slider using the specified orientation with the
193 * range {@code 0} to {@code 100} and an initial value of {@code 50}.
194 * The orientation can be
195 * either <code>SwingConstants.VERTICAL</code> or
196 * <code>SwingConstants.HORIZONTAL</code>.
197 *
198 * @param orientation the orientation of the slider
199 * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
200 * @see #setOrientation
201 */
202 public JSlider(int orientation) {
203 this(orientation, 0, 100, 50);
204 }
205
206
207 /**
208 * Creates a horizontal slider using the specified min and max
209 * with an initial value equal to the average of the min plus max.
210 * <p>
211 * The <code>BoundedRangeModel</code> that holds the slider's data
212 * handles any issues that may arise from improperly setting the
213 * minimum and maximum values on the slider. See the
214 * {@code BoundedRangeModel} documentation for details.
215 *
216 * @param min the minimum value of the slider
217 * @param max the maximum value of the slider
218 *
219 * @see BoundedRangeModel
220 * @see #setMinimum
221 * @see #setMaximum
222 */
223 public JSlider(int min, int max) {
224 this(HORIZONTAL, min, max, (min + max) / 2);
225 }
226
227
228 /**
229 * Creates a horizontal slider using the specified min, max and value.
230 * <p>
231 * The <code>BoundedRangeModel</code> that holds the slider's data
232 * handles any issues that may arise from improperly setting the
233 * minimum, initial, and maximum values on the slider. See the
234 * {@code BoundedRangeModel} documentation for details.
235 *
236 * @param min the minimum value of the slider
237 * @param max the maximum value of the slider
238 * @param value the initial value of the slider
239 *
240 * @see BoundedRangeModel
241 * @see #setMinimum
242 * @see #setMaximum
243 * @see #setValue
244 */
245 public JSlider(int min, int max, int value) {
246 this(HORIZONTAL, min, max, value);
247 }
248
249
250 /**
251 * Creates a slider with the specified orientation and the
252 * specified minimum, maximum, and initial values.
253 * The orientation can be
254 * either <code>SwingConstants.VERTICAL</code> or
255 * <code>SwingConstants.HORIZONTAL</code>.
256 * <p>
257 * The <code>BoundedRangeModel</code> that holds the slider's data
258 * handles any issues that may arise from improperly setting the
259 * minimum, initial, and maximum values on the slider. See the
260 * {@code BoundedRangeModel} documentation for details.
261 *
262 * @param orientation the orientation of the slider
263 * @param min the minimum value of the slider
264 * @param max the maximum value of the slider
265 * @param value the initial value of the slider
266 *
267 * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
268 *
269 * @see BoundedRangeModel
270 * @see #setOrientation
271 * @see #setMinimum
272 * @see #setMaximum
273 * @see #setValue
274 */
275 public JSlider(int orientation, int min, int max, int value)
276 {
277 checkOrientation(orientation);
346 public String getUIClassID() {
347 return uiClassID;
348 }
349
350
351 /**
352 * We pass Change events along to the listeners with the
353 * the slider (instead of the model itself) as the event source.
354 */
355 private class ModelListener implements ChangeListener, Serializable {
356 public void stateChanged(ChangeEvent e) {
357 fireStateChanged();
358 }
359 }
360
361
362 /**
363 * Subclasses that want to handle {@code ChangeEvent}s
364 * from the model differently
365 * can override this to return
366 * an instance of a custom <code>ChangeListener</code> implementation.
367 * The default {@code ChangeListener} simply calls the
368 * {@code fireStateChanged} method to forward {@code ChangeEvent}s
369 * to the {@code ChangeListener}s that have been added directly to the
370 * slider.
371 *
372 * @return a instance of new {@code ChangeListener}
373 * @see #changeListener
374 * @see #fireStateChanged
375 * @see javax.swing.event.ChangeListener
376 * @see javax.swing.BoundedRangeModel
377 */
378 protected ChangeListener createChangeListener() {
379 return new ModelListener();
380 }
381
382
383 /**
384 * Adds a ChangeListener to the slider.
385 *
386 * @param l the ChangeListener to add
389 */
390 public void addChangeListener(ChangeListener l) {
391 listenerList.add(ChangeListener.class, l);
392 }
393
394
395 /**
396 * Removes a ChangeListener from the slider.
397 *
398 * @param l the ChangeListener to remove
399 * @see #fireStateChanged
400 * @see #addChangeListener
401
402 */
403 public void removeChangeListener(ChangeListener l) {
404 listenerList.remove(ChangeListener.class, l);
405 }
406
407
408 /**
409 * Returns an array of all the <code>ChangeListener</code>s added
410 * to this JSlider with addChangeListener().
411 *
412 * @return all of the <code>ChangeListener</code>s added or an empty
413 * array if no listeners have been added
414 * @since 1.4
415 */
416 public ChangeListener[] getChangeListeners() {
417 return listenerList.getListeners(ChangeListener.class);
418 }
419
420
421 /**
422 * Send a {@code ChangeEvent}, whose source is this {@code JSlider}, to
423 * all {@code ChangeListener}s that have registered interest in
424 * {@code ChangeEvent}s.
425 * This method is called each time a {@code ChangeEvent} is received from
426 * the model.
427 * <p>
428 * The event instance is created if necessary, and stored in
429 * {@code changeEvent}.
430 *
431 * @see #addChangeListener
432 * @see EventListenerList
447 /**
448 * Returns the {@code BoundedRangeModel} that handles the slider's three
449 * fundamental properties: minimum, maximum, value.
450 *
451 * @return the data model for this component
452 * @see #setModel
453 * @see BoundedRangeModel
454 */
455 public BoundedRangeModel getModel() {
456 return sliderModel;
457 }
458
459
460 /**
461 * Sets the {@code BoundedRangeModel} that handles the slider's three
462 * fundamental properties: minimum, maximum, value.
463 *<p>
464 * Attempts to pass a {@code null} model to this method result in
465 * undefined behavior, and, most likely, exceptions.
466 *
467 * @param newModel the new, {@code non-null} <code>BoundedRangeModel</code> to use
468 *
469 * @see #getModel
470 * @see BoundedRangeModel
471 * @beaninfo
472 * bound: true
473 * description: The sliders BoundedRangeModel.
474 */
475 public void setModel(BoundedRangeModel newModel)
476 {
477 BoundedRangeModel oldModel = getModel();
478
479 if (oldModel != null) {
480 oldModel.removeChangeListener(changeListener);
481 }
482
483 sliderModel = newModel;
484
485 if (newModel != null) {
486 newModel.addChangeListener(changeListener);
487 }
533 */
534 public void setValue(int n) {
535 BoundedRangeModel m = getModel();
536 int oldValue = m.getValue();
537 if (oldValue == n) {
538 return;
539 }
540 m.setValue(n);
541
542 if (accessibleContext != null) {
543 accessibleContext.firePropertyChange(
544 AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
545 Integer.valueOf(oldValue),
546 Integer.valueOf(m.getValue()));
547 }
548 }
549
550
551 /**
552 * Returns the minimum value supported by the slider
553 * from the <code>BoundedRangeModel</code>.
554 *
555 * @return the value of the model's minimum property
556 * @see #setMinimum
557 * @see BoundedRangeModel#getMinimum
558 */
559 public int getMinimum() {
560 return getModel().getMinimum();
561 }
562
563
564 /**
565 * Sets the slider's minimum value to {@code minimum}. This method
566 * forwards the new minimum value to the model.
567 * <p>
568 * The data model (an instance of {@code BoundedRangeModel})
569 * handles any mathematical
570 * issues arising from assigning faulty values. See the
571 * {@code BoundedRangeModel} documentation for details.
572 * <p>
573 * If the new minimum value is different from the previous minimum value,
574 * all change listeners are notified.
575 *
576 * @param minimum the new minimum
577 * @see #getMinimum
578 * @see #addChangeListener
579 * @see BoundedRangeModel#setMinimum
580 * @beaninfo
581 * bound: true
582 * preferred: true
583 * description: The sliders minimum value.
584 */
585 public void setMinimum(int minimum) {
586 int oldMin = getModel().getMinimum();
587 getModel().setMinimum(minimum);
588 firePropertyChange( "minimum", Integer.valueOf( oldMin ), Integer.valueOf( minimum ) );
589 }
590
591
592 /**
593 * Returns the maximum value supported by the slider
594 * from the <code>BoundedRangeModel</code>.
595 *
596 * @return the value of the model's maximum property
597 * @see #setMaximum
598 * @see BoundedRangeModel#getMaximum
599 */
600 public int getMaximum() {
601 return getModel().getMaximum();
602 }
603
604
605 /**
606 * Sets the slider's maximum value to {@code maximum}. This method
607 * forwards the new maximum value to the model.
608 * <p>
609 * The data model (an instance of {@code BoundedRangeModel})
610 * handles any mathematical
611 * issues arising from assigning faulty values. See the
612 * {@code BoundedRangeModel} documentation for details.
613 * <p>
614 * If the new maximum value is different from the previous maximum value,
653 * @see BoundedRangeModel#setValueIsAdjusting
654 * @beaninfo
655 * expert: true
656 * description: True if the slider knob is being dragged.
657 */
658 public void setValueIsAdjusting(boolean b) {
659 BoundedRangeModel m = getModel();
660 boolean oldValue = m.getValueIsAdjusting();
661 m.setValueIsAdjusting(b);
662
663 if ((oldValue != b) && (accessibleContext != null)) {
664 accessibleContext.firePropertyChange(
665 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
666 ((oldValue) ? AccessibleState.BUSY : null),
667 ((b) ? AccessibleState.BUSY : null));
668 }
669 }
670
671
672 /**
673 * Returns the "extent" from the <code>BoundedRangeModel</code>.
674 * This represents the range of values "covered" by the knob.
675 *
676 * @return an int representing the extent
677 * @see #setExtent
678 * @see BoundedRangeModel#getExtent
679 */
680 public int getExtent() {
681 return getModel().getExtent();
682 }
683
684
685 /**
686 * Sets the size of the range "covered" by the knob. Most look
687 * and feel implementations will change the value by this amount
688 * if the user clicks on either side of the knob. This method just
689 * forwards the new extent value to the model.
690 * <p>
691 * The data model (an instance of {@code BoundedRangeModel})
692 * handles any mathematical
693 * issues arising from assigning faulty values. See the
780
781 while (elements.hasMoreElements()) {
782 Component component = (Component) elements.nextElement();
783
784 if (component instanceof JLabel) {
785 JLabel label = (JLabel) component;
786
787 if (SwingUtilities.doesIconReferenceImage(label.getIcon(), img) ||
788 SwingUtilities.doesIconReferenceImage(label.getDisabledIcon(), img)) {
789 return super.imageUpdate(img, infoflags, x, y, w, h);
790 }
791 }
792 }
793
794 return false;
795 }
796
797 /**
798 * Returns the dictionary of what labels to draw at which values.
799 *
800 * @return the <code>Dictionary</code> containing labels and
801 * where to draw them
802 */
803 @SuppressWarnings("rawtypes")
804 public Dictionary getLabelTable() {
805 /*
806 if ( labelTable == null && getMajorTickSpacing() > 0 ) {
807 setLabelTable( createStandardLabels( getMajorTickSpacing() ) );
808 }
809 */
810 return labelTable;
811 }
812
813
814 /**
815 * Used to specify what label will be drawn at any given value.
816 * The key-value pairs are of this format:
817 * <code>{ Integer value, java.swing.JComponent label }</code>.
818 * <p>
819 * An easy way to generate a standard table of value labels is by using the
820 * {@code createStandardLabels} method.
821 * <p>
822 * Once the labels have been set, this method calls {@link #updateLabelUIs}.
823 * Note that the labels are only painted if the {@code paintLabels}
824 * property is {@code true}.
825 *
826 * @param labels new {@code Dictionary} of labels, or {@code null} to
827 * remove all labels
828 * @see #createStandardLabels(int)
829 * @see #getLabelTable
830 * @see #setPaintLabels
831 * @beaninfo
832 * hidden: true
833 * bound: true
834 * attribute: visualUpdate true
835 * description: Specifies what labels will be drawn for any given value.
836 */
837 @SuppressWarnings("rawtypes")
870 component.setSize(component.getPreferredSize());
871 }
872 }
873
874 private void updateLabelSizes() {
875 @SuppressWarnings("rawtypes")
876 Dictionary labelTable = getLabelTable();
877 if (labelTable != null) {
878 Enumeration<?> labels = labelTable.elements();
879 while (labels.hasMoreElements()) {
880 JComponent component = (JComponent) labels.nextElement();
881 component.setSize(component.getPreferredSize());
882 }
883 }
884 }
885
886
887 /**
888 * Creates a {@code Hashtable} of numerical text labels, starting at the
889 * slider minimum, and using the increment specified.
890 * For example, if you call <code>createStandardLabels( 10 )</code>
891 * and the slider minimum is zero,
892 * then labels will be created for the values 0, 10, 20, 30, and so on.
893 * <p>
894 * For the labels to be drawn on the slider, the returned {@code Hashtable}
895 * must be passed into {@code setLabelTable}, and {@code setPaintLabels}
896 * must be set to {@code true}.
897 * <p>
898 * For further details on the makeup of the returned {@code Hashtable}, see
899 * the {@code setLabelTable} documentation.
900 *
901 * @param increment distance between labels in the generated hashtable
902 * @return a new {@code Hashtable} of labels
903 * @see #setLabelTable
904 * @see #setPaintLabels
905 * @throws IllegalArgumentException if {@code increment} is less than or
906 * equal to zero
907 */
908 public Hashtable<Integer, JComponent> createStandardLabels( int increment ) {
909 return createStandardLabels( increment, getMinimum() );
910 }
911
912
913 /**
914 * Creates a {@code Hashtable} of numerical text labels, starting at the
915 * starting point specified, and using the increment specified.
916 * For example, if you call
917 * <code>createStandardLabels( 10, 2 )</code>,
918 * then labels will be created for the values 2, 12, 22, 32, and so on.
919 * <p>
920 * For the labels to be drawn on the slider, the returned {@code Hashtable}
921 * must be passed into {@code setLabelTable}, and {@code setPaintLabels}
922 * must be set to {@code true}.
923 * <p>
924 * For further details on the makeup of the returned {@code Hashtable}, see
925 * the {@code setLabelTable} documentation.
926 *
927 * @param increment distance between labels in the generated hashtable
928 * @param start value at which the labels will begin
929 * @return a new {@code Hashtable} of labels
930 * @see #setLabelTable
931 * @see #setPaintLabels
932 * @exception IllegalArgumentException if {@code start} is
933 * out of range, or if {@code increment} is less than or equal
934 * to zero
935 */
936 public Hashtable<Integer, JComponent> createStandardLabels( int increment, int start ) {
937 if ( start > getMaximum() || start < getMinimum() ) {
1034 addPropertyChangeListener( table );
1035
1036 return table;
1037 }
1038
1039
1040 /**
1041 * Returns true if the value-range shown for the slider is reversed,
1042 *
1043 * @return true if the slider values are reversed from their normal order
1044 * @see #setInverted
1045 */
1046 public boolean getInverted() {
1047 return isInverted;
1048 }
1049
1050
1051 /**
1052 * Specify true to reverse the value-range shown for the slider and false to
1053 * put the value range in the normal order. The order depends on the
1054 * slider's <code>ComponentOrientation</code> property. Normal (non-inverted)
1055 * horizontal sliders with a <code>ComponentOrientation</code> value of
1056 * <code>LEFT_TO_RIGHT</code> have their maximum on the right.
1057 * Normal horizontal sliders with a <code>ComponentOrientation</code> value of
1058 * <code>RIGHT_TO_LEFT</code> have their maximum on the left. Normal vertical
1059 * sliders have their maximum on the top. These labels are reversed when the
1060 * slider is inverted.
1061 * <p>
1062 * By default, the value of this property is {@code false}.
1063 *
1064 * @param b true to reverse the slider values from their normal order
1065 * @beaninfo
1066 * bound: true
1067 * attribute: visualUpdate true
1068 * description: If true reverses the slider values from their normal order
1069 *
1070 */
1071 public void setInverted( boolean b ) {
1072 boolean oldValue = isInverted;
1073 isInverted = b;
1074 firePropertyChange("inverted", oldValue, isInverted);
1075 if (b != oldValue) {
1076 repaint();
1077 }
1078 }
1359 * See readObject() and writeObject() in JComponent for more
1360 * information about serialization in Swing.
1361 */
1362 private void writeObject(ObjectOutputStream s) throws IOException {
1363 s.defaultWriteObject();
1364 if (getUIClassID().equals(uiClassID)) {
1365 byte count = JComponent.getWriteObjCounter(this);
1366 JComponent.setWriteObjCounter(this, --count);
1367 if (count == 0 && ui != null) {
1368 ui.installUI(this);
1369 }
1370 }
1371 }
1372
1373
1374 /**
1375 * Returns a string representation of this JSlider. This method
1376 * is intended to be used only for debugging purposes, and the
1377 * content and format of the returned string may vary between
1378 * implementations. The returned string may be empty but may not
1379 * be <code>null</code>.
1380 *
1381 * @return a string representation of this JSlider.
1382 */
1383 protected String paramString() {
1384 String paintTicksString = (paintTicks ?
1385 "true" : "false");
1386 String paintTrackString = (paintTrack ?
1387 "true" : "false");
1388 String paintLabelsString = (paintLabels ?
1389 "true" : "false");
1390 String isInvertedString = (isInverted ?
1391 "true" : "false");
1392 String snapToTicksString = (snapToTicks ?
1393 "true" : "false");
1394 String snapToValueString = (snapToValue ?
1395 "true" : "false");
1396 String orientationString = (orientation == HORIZONTAL ?
1397 "HORIZONTAL" : "VERTICAL");
1398
1399 return super.paramString() +
1414 ////////////////
1415
1416 /**
1417 * Gets the AccessibleContext associated with this JSlider.
1418 * For sliders, the AccessibleContext takes the form of an
1419 * AccessibleJSlider.
1420 * A new AccessibleJSlider instance is created if necessary.
1421 *
1422 * @return an AccessibleJSlider that serves as the
1423 * AccessibleContext of this JSlider
1424 */
1425 public AccessibleContext getAccessibleContext() {
1426 if (accessibleContext == null) {
1427 accessibleContext = new AccessibleJSlider();
1428 }
1429 return accessibleContext;
1430 }
1431
1432 /**
1433 * This class implements accessibility support for the
1434 * <code>JSlider</code> class. It provides an implementation of the
1435 * Java Accessibility API appropriate to slider user-interface elements.
1436 * <p>
1437 * <strong>Warning:</strong>
1438 * Serialized objects of this class will not be compatible with
1439 * future Swing releases. The current serialization support is
1440 * appropriate for short term storage or RMI between applications running
1441 * the same version of Swing. As of 1.4, support for long term storage
1442 * of all JavaBeans™
1443 * has been added to the <code>java.beans</code> package.
1444 * Please see {@link java.beans.XMLEncoder}.
1445 */
1446 @SuppressWarnings("serial") // Same-version serialization only
1447 protected class AccessibleJSlider extends AccessibleJComponent
1448 implements AccessibleValue {
1449
1450 /**
1451 * Get the state set of this object.
1452 *
1453 * @return an instance of AccessibleState containing the current state
1454 * of the object
1455 * @see AccessibleState
1456 */
1457 public AccessibleStateSet getAccessibleStateSet() {
1458 AccessibleStateSet states = super.getAccessibleStateSet();
1459 if (getValueIsAdjusting()) {
1460 states.add(AccessibleState.BUSY);
1461 }
1462 if (getOrientation() == VERTICAL) {
1463 states.add(AccessibleState.VERTICAL);
|
29 import javax.swing.plaf.*;
30 import javax.accessibility.*;
31
32 import java.io.Serializable;
33 import java.io.ObjectOutputStream;
34 import java.io.IOException;
35
36 import java.awt.*;
37 import java.util.*;
38 import java.beans.*;
39
40
41 /**
42 * A component that lets the user graphically select a value by sliding
43 * a knob within a bounded interval. The knob is always positioned
44 * at the points that match integer values within the specified interval.
45 * <p>
46 * The slider can show both
47 * major tick marks, and minor tick marks between the major ones. The number of
48 * values between the tick marks is controlled with
49 * {@code setMajorTickSpacing} and {@code setMinorTickSpacing}.
50 * Painting of tick marks is controlled by {@code setPaintTicks}.
51 * <p>
52 * Sliders can also print text labels at regular intervals (or at
53 * arbitrary locations) along the slider track. Painting of labels is
54 * controlled by {@code setLabelTable} and {@code setPaintLabels}.
55 * <p>
56 * For further information and examples see
57 * <a
58 href="http://docs.oracle.com/javase/tutorial/uiswing/components/slider.html">How to Use Sliders</a>,
59 * a section in <em>The Java Tutorial.</em>
60 * <p>
61 * <strong>Warning:</strong> Swing is not thread safe. For more
62 * information see <a
63 * href="package-summary.html#threading">Swing's Threading
64 * Policy</a>.
65 * <p>
66 * <strong>Warning:</strong>
67 * Serialized objects of this class will not be compatible with
68 * future Swing releases. The current serialization support is
69 * appropriate for short term storage or RMI between applications running
70 * the same version of Swing. As of 1.4, support for long term storage
71 * of all JavaBeans™
72 * has been added to the {@code java.beans} package.
73 * Please see {@link java.beans.XMLEncoder}.
74 *
75 * @beaninfo
76 * attribute: isContainer false
77 * description: A component that supports selecting a integer value from a range.
78 *
79 * @author David Kloba
80 * @since 1.2
81 */
82 @SuppressWarnings("serial") // Same-version serialization only
83 public class JSlider extends JComponent implements SwingConstants, Accessible {
84 /**
85 * @see #getUIClassID
86 * @see #readObject
87 */
88 private static final String uiClassID = "SliderUI";
89
90 private boolean paintTicks = false;
91 private boolean paintTrack = true;
92 private boolean paintLabels = false;
141 @SuppressWarnings("rawtypes")
142 private Dictionary labelTable;
143 // For better source compatibility, the labelTable field and
144 // associated getter and setter methods are being left as raw
145 // types.
146
147 /**
148 * The changeListener (no suffix) is the listener we add to the
149 * slider's model. This listener is initialized to the
150 * {@code ChangeListener} returned from {@code createChangeListener},
151 * which by default just forwards events
152 * to {@code ChangeListener}s (if any) added directly to the slider.
153 *
154 * @see #addChangeListener
155 * @see #createChangeListener
156 */
157 protected ChangeListener changeListener = createChangeListener();
158
159
160 /**
161 * Only one {@code ChangeEvent} is needed per slider instance since the
162 * event's only (read-only) state is the source property. The source
163 * of events generated here is always "this". The event is lazily
164 * created the first time that an event notification is fired.
165 *
166 * @see #fireStateChanged
167 */
168 protected transient ChangeEvent changeEvent = null;
169
170
171 private void checkOrientation(int orientation) {
172 switch (orientation) {
173 case VERTICAL:
174 case HORIZONTAL:
175 break;
176 default:
177 throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
178 }
179 }
180
181
182 /**
183 * Creates a horizontal slider with the range 0 to 100 and
184 * an initial value of 50.
185 */
186 public JSlider() {
187 this(HORIZONTAL, 0, 100, 50);
188 }
189
190
191 /**
192 * Creates a slider using the specified orientation with the
193 * range {@code 0} to {@code 100} and an initial value of {@code 50}.
194 * The orientation can be
195 * either {@code SwingConstants.VERTICAL} or
196 * {@code SwingConstants.HORIZONTAL}.
197 *
198 * @param orientation the orientation of the slider
199 * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
200 * @see #setOrientation
201 */
202 public JSlider(int orientation) {
203 this(orientation, 0, 100, 50);
204 }
205
206
207 /**
208 * Creates a horizontal slider using the specified min and max
209 * with an initial value equal to the average of the min plus max.
210 * <p>
211 * The {@code BoundedRangeModel} that holds the slider's data
212 * handles any issues that may arise from improperly setting the
213 * minimum and maximum values on the slider. See the
214 * {@code BoundedRangeModel} documentation for details.
215 *
216 * @param min the minimum value of the slider
217 * @param max the maximum value of the slider
218 *
219 * @see BoundedRangeModel
220 * @see #setMinimum
221 * @see #setMaximum
222 */
223 public JSlider(int min, int max) {
224 this(HORIZONTAL, min, max, (min + max) / 2);
225 }
226
227
228 /**
229 * Creates a horizontal slider using the specified min, max and value.
230 * <p>
231 * The {@code BoundedRangeModel} that holds the slider's data
232 * handles any issues that may arise from improperly setting the
233 * minimum, initial, and maximum values on the slider. See the
234 * {@code BoundedRangeModel} documentation for details.
235 *
236 * @param min the minimum value of the slider
237 * @param max the maximum value of the slider
238 * @param value the initial value of the slider
239 *
240 * @see BoundedRangeModel
241 * @see #setMinimum
242 * @see #setMaximum
243 * @see #setValue
244 */
245 public JSlider(int min, int max, int value) {
246 this(HORIZONTAL, min, max, value);
247 }
248
249
250 /**
251 * Creates a slider with the specified orientation and the
252 * specified minimum, maximum, and initial values.
253 * The orientation can be
254 * either {@code SwingConstants.VERTICAL} or
255 * {@code SwingConstants.HORIZONTAL}.
256 * <p>
257 * The {@code BoundedRangeModel} that holds the slider's data
258 * handles any issues that may arise from improperly setting the
259 * minimum, initial, and maximum values on the slider. See the
260 * {@code BoundedRangeModel} documentation for details.
261 *
262 * @param orientation the orientation of the slider
263 * @param min the minimum value of the slider
264 * @param max the maximum value of the slider
265 * @param value the initial value of the slider
266 *
267 * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
268 *
269 * @see BoundedRangeModel
270 * @see #setOrientation
271 * @see #setMinimum
272 * @see #setMaximum
273 * @see #setValue
274 */
275 public JSlider(int orientation, int min, int max, int value)
276 {
277 checkOrientation(orientation);
346 public String getUIClassID() {
347 return uiClassID;
348 }
349
350
351 /**
352 * We pass Change events along to the listeners with the
353 * the slider (instead of the model itself) as the event source.
354 */
355 private class ModelListener implements ChangeListener, Serializable {
356 public void stateChanged(ChangeEvent e) {
357 fireStateChanged();
358 }
359 }
360
361
362 /**
363 * Subclasses that want to handle {@code ChangeEvent}s
364 * from the model differently
365 * can override this to return
366 * an instance of a custom {@code ChangeListener} implementation.
367 * The default {@code ChangeListener} simply calls the
368 * {@code fireStateChanged} method to forward {@code ChangeEvent}s
369 * to the {@code ChangeListener}s that have been added directly to the
370 * slider.
371 *
372 * @return a instance of new {@code ChangeListener}
373 * @see #changeListener
374 * @see #fireStateChanged
375 * @see javax.swing.event.ChangeListener
376 * @see javax.swing.BoundedRangeModel
377 */
378 protected ChangeListener createChangeListener() {
379 return new ModelListener();
380 }
381
382
383 /**
384 * Adds a ChangeListener to the slider.
385 *
386 * @param l the ChangeListener to add
389 */
390 public void addChangeListener(ChangeListener l) {
391 listenerList.add(ChangeListener.class, l);
392 }
393
394
395 /**
396 * Removes a ChangeListener from the slider.
397 *
398 * @param l the ChangeListener to remove
399 * @see #fireStateChanged
400 * @see #addChangeListener
401
402 */
403 public void removeChangeListener(ChangeListener l) {
404 listenerList.remove(ChangeListener.class, l);
405 }
406
407
408 /**
409 * Returns an array of all the {@code ChangeListener}s added
410 * to this JSlider with addChangeListener().
411 *
412 * @return all of the {@code ChangeListener}s added or an empty
413 * array if no listeners have been added
414 * @since 1.4
415 */
416 public ChangeListener[] getChangeListeners() {
417 return listenerList.getListeners(ChangeListener.class);
418 }
419
420
421 /**
422 * Send a {@code ChangeEvent}, whose source is this {@code JSlider}, to
423 * all {@code ChangeListener}s that have registered interest in
424 * {@code ChangeEvent}s.
425 * This method is called each time a {@code ChangeEvent} is received from
426 * the model.
427 * <p>
428 * The event instance is created if necessary, and stored in
429 * {@code changeEvent}.
430 *
431 * @see #addChangeListener
432 * @see EventListenerList
447 /**
448 * Returns the {@code BoundedRangeModel} that handles the slider's three
449 * fundamental properties: minimum, maximum, value.
450 *
451 * @return the data model for this component
452 * @see #setModel
453 * @see BoundedRangeModel
454 */
455 public BoundedRangeModel getModel() {
456 return sliderModel;
457 }
458
459
460 /**
461 * Sets the {@code BoundedRangeModel} that handles the slider's three
462 * fundamental properties: minimum, maximum, value.
463 *<p>
464 * Attempts to pass a {@code null} model to this method result in
465 * undefined behavior, and, most likely, exceptions.
466 *
467 * @param newModel the new, {@code non-null BoundedRangeModel} to use
468 *
469 * @see #getModel
470 * @see BoundedRangeModel
471 * @beaninfo
472 * bound: true
473 * description: The sliders BoundedRangeModel.
474 */
475 public void setModel(BoundedRangeModel newModel)
476 {
477 BoundedRangeModel oldModel = getModel();
478
479 if (oldModel != null) {
480 oldModel.removeChangeListener(changeListener);
481 }
482
483 sliderModel = newModel;
484
485 if (newModel != null) {
486 newModel.addChangeListener(changeListener);
487 }
533 */
534 public void setValue(int n) {
535 BoundedRangeModel m = getModel();
536 int oldValue = m.getValue();
537 if (oldValue == n) {
538 return;
539 }
540 m.setValue(n);
541
542 if (accessibleContext != null) {
543 accessibleContext.firePropertyChange(
544 AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
545 Integer.valueOf(oldValue),
546 Integer.valueOf(m.getValue()));
547 }
548 }
549
550
551 /**
552 * Returns the minimum value supported by the slider
553 * from the {@code BoundedRangeModel}.
554 *
555 * @return the value of the model's minimum property
556 * @see #setMinimum
557 * @see BoundedRangeModel#getMinimum
558 */
559 public int getMinimum() {
560 return getModel().getMinimum();
561 }
562
563
564 /**
565 * Sets the slider's minimum value to {@code minimum}. This method
566 * forwards the new minimum value to the model.
567 * <p>
568 * The data model (an instance of {@code BoundedRangeModel})
569 * handles any mathematical
570 * issues arising from assigning faulty values. See the
571 * {@code BoundedRangeModel} documentation for details.
572 * <p>
573 * If the new minimum value is different from the previous minimum value,
574 * all change listeners are notified.
575 *
576 * @param minimum the new minimum
577 * @see #getMinimum
578 * @see #addChangeListener
579 * @see BoundedRangeModel#setMinimum
580 * @beaninfo
581 * bound: true
582 * preferred: true
583 * description: The sliders minimum value.
584 */
585 public void setMinimum(int minimum) {
586 int oldMin = getModel().getMinimum();
587 getModel().setMinimum(minimum);
588 firePropertyChange( "minimum", Integer.valueOf( oldMin ), Integer.valueOf( minimum ) );
589 }
590
591
592 /**
593 * Returns the maximum value supported by the slider
594 * from the {@code BoundedRangeModel}.
595 *
596 * @return the value of the model's maximum property
597 * @see #setMaximum
598 * @see BoundedRangeModel#getMaximum
599 */
600 public int getMaximum() {
601 return getModel().getMaximum();
602 }
603
604
605 /**
606 * Sets the slider's maximum value to {@code maximum}. This method
607 * forwards the new maximum value to the model.
608 * <p>
609 * The data model (an instance of {@code BoundedRangeModel})
610 * handles any mathematical
611 * issues arising from assigning faulty values. See the
612 * {@code BoundedRangeModel} documentation for details.
613 * <p>
614 * If the new maximum value is different from the previous maximum value,
653 * @see BoundedRangeModel#setValueIsAdjusting
654 * @beaninfo
655 * expert: true
656 * description: True if the slider knob is being dragged.
657 */
658 public void setValueIsAdjusting(boolean b) {
659 BoundedRangeModel m = getModel();
660 boolean oldValue = m.getValueIsAdjusting();
661 m.setValueIsAdjusting(b);
662
663 if ((oldValue != b) && (accessibleContext != null)) {
664 accessibleContext.firePropertyChange(
665 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
666 ((oldValue) ? AccessibleState.BUSY : null),
667 ((b) ? AccessibleState.BUSY : null));
668 }
669 }
670
671
672 /**
673 * Returns the "extent" from the {@code BoundedRangeModel}.
674 * This represents the range of values "covered" by the knob.
675 *
676 * @return an int representing the extent
677 * @see #setExtent
678 * @see BoundedRangeModel#getExtent
679 */
680 public int getExtent() {
681 return getModel().getExtent();
682 }
683
684
685 /**
686 * Sets the size of the range "covered" by the knob. Most look
687 * and feel implementations will change the value by this amount
688 * if the user clicks on either side of the knob. This method just
689 * forwards the new extent value to the model.
690 * <p>
691 * The data model (an instance of {@code BoundedRangeModel})
692 * handles any mathematical
693 * issues arising from assigning faulty values. See the
780
781 while (elements.hasMoreElements()) {
782 Component component = (Component) elements.nextElement();
783
784 if (component instanceof JLabel) {
785 JLabel label = (JLabel) component;
786
787 if (SwingUtilities.doesIconReferenceImage(label.getIcon(), img) ||
788 SwingUtilities.doesIconReferenceImage(label.getDisabledIcon(), img)) {
789 return super.imageUpdate(img, infoflags, x, y, w, h);
790 }
791 }
792 }
793
794 return false;
795 }
796
797 /**
798 * Returns the dictionary of what labels to draw at which values.
799 *
800 * @return the {@code Dictionary} containing labels and
801 * where to draw them
802 */
803 @SuppressWarnings("rawtypes")
804 public Dictionary getLabelTable() {
805 /*
806 if ( labelTable == null && getMajorTickSpacing() > 0 ) {
807 setLabelTable( createStandardLabels( getMajorTickSpacing() ) );
808 }
809 */
810 return labelTable;
811 }
812
813
814 /**
815 * Used to specify what label will be drawn at any given value.
816 * The key-value pairs are of this format:
817 * {@code { Integer value, java.swing.JComponent label }}.
818 * <p>
819 * An easy way to generate a standard table of value labels is by using the
820 * {@code createStandardLabels} method.
821 * <p>
822 * Once the labels have been set, this method calls {@link #updateLabelUIs}.
823 * Note that the labels are only painted if the {@code paintLabels}
824 * property is {@code true}.
825 *
826 * @param labels new {@code Dictionary} of labels, or {@code null} to
827 * remove all labels
828 * @see #createStandardLabels(int)
829 * @see #getLabelTable
830 * @see #setPaintLabels
831 * @beaninfo
832 * hidden: true
833 * bound: true
834 * attribute: visualUpdate true
835 * description: Specifies what labels will be drawn for any given value.
836 */
837 @SuppressWarnings("rawtypes")
870 component.setSize(component.getPreferredSize());
871 }
872 }
873
874 private void updateLabelSizes() {
875 @SuppressWarnings("rawtypes")
876 Dictionary labelTable = getLabelTable();
877 if (labelTable != null) {
878 Enumeration<?> labels = labelTable.elements();
879 while (labels.hasMoreElements()) {
880 JComponent component = (JComponent) labels.nextElement();
881 component.setSize(component.getPreferredSize());
882 }
883 }
884 }
885
886
887 /**
888 * Creates a {@code Hashtable} of numerical text labels, starting at the
889 * slider minimum, and using the increment specified.
890 * For example, if you call {@code createStandardLabels( 10 )}
891 * and the slider minimum is zero,
892 * then labels will be created for the values 0, 10, 20, 30, and so on.
893 * <p>
894 * For the labels to be drawn on the slider, the returned {@code Hashtable}
895 * must be passed into {@code setLabelTable}, and {@code setPaintLabels}
896 * must be set to {@code true}.
897 * <p>
898 * For further details on the makeup of the returned {@code Hashtable}, see
899 * the {@code setLabelTable} documentation.
900 *
901 * @param increment distance between labels in the generated hashtable
902 * @return a new {@code Hashtable} of labels
903 * @see #setLabelTable
904 * @see #setPaintLabels
905 * @throws IllegalArgumentException if {@code increment} is less than or
906 * equal to zero
907 */
908 public Hashtable<Integer, JComponent> createStandardLabels( int increment ) {
909 return createStandardLabels( increment, getMinimum() );
910 }
911
912
913 /**
914 * Creates a {@code Hashtable} of numerical text labels, starting at the
915 * starting point specified, and using the increment specified.
916 * For example, if you call
917 * {@code createStandardLabels( 10, 2 )},
918 * then labels will be created for the values 2, 12, 22, 32, and so on.
919 * <p>
920 * For the labels to be drawn on the slider, the returned {@code Hashtable}
921 * must be passed into {@code setLabelTable}, and {@code setPaintLabels}
922 * must be set to {@code true}.
923 * <p>
924 * For further details on the makeup of the returned {@code Hashtable}, see
925 * the {@code setLabelTable} documentation.
926 *
927 * @param increment distance between labels in the generated hashtable
928 * @param start value at which the labels will begin
929 * @return a new {@code Hashtable} of labels
930 * @see #setLabelTable
931 * @see #setPaintLabels
932 * @exception IllegalArgumentException if {@code start} is
933 * out of range, or if {@code increment} is less than or equal
934 * to zero
935 */
936 public Hashtable<Integer, JComponent> createStandardLabels( int increment, int start ) {
937 if ( start > getMaximum() || start < getMinimum() ) {
1034 addPropertyChangeListener( table );
1035
1036 return table;
1037 }
1038
1039
1040 /**
1041 * Returns true if the value-range shown for the slider is reversed,
1042 *
1043 * @return true if the slider values are reversed from their normal order
1044 * @see #setInverted
1045 */
1046 public boolean getInverted() {
1047 return isInverted;
1048 }
1049
1050
1051 /**
1052 * Specify true to reverse the value-range shown for the slider and false to
1053 * put the value range in the normal order. The order depends on the
1054 * slider's {@code ComponentOrientation} property. Normal (non-inverted)
1055 * horizontal sliders with a {@code ComponentOrientation} value of
1056 * {@code LEFT_TO_RIGHT} have their maximum on the right.
1057 * Normal horizontal sliders with a {@code ComponentOrientation} value of
1058 * {@code RIGHT_TO_LEFT} have their maximum on the left. Normal vertical
1059 * sliders have their maximum on the top. These labels are reversed when the
1060 * slider is inverted.
1061 * <p>
1062 * By default, the value of this property is {@code false}.
1063 *
1064 * @param b true to reverse the slider values from their normal order
1065 * @beaninfo
1066 * bound: true
1067 * attribute: visualUpdate true
1068 * description: If true reverses the slider values from their normal order
1069 *
1070 */
1071 public void setInverted( boolean b ) {
1072 boolean oldValue = isInverted;
1073 isInverted = b;
1074 firePropertyChange("inverted", oldValue, isInverted);
1075 if (b != oldValue) {
1076 repaint();
1077 }
1078 }
1359 * See readObject() and writeObject() in JComponent for more
1360 * information about serialization in Swing.
1361 */
1362 private void writeObject(ObjectOutputStream s) throws IOException {
1363 s.defaultWriteObject();
1364 if (getUIClassID().equals(uiClassID)) {
1365 byte count = JComponent.getWriteObjCounter(this);
1366 JComponent.setWriteObjCounter(this, --count);
1367 if (count == 0 && ui != null) {
1368 ui.installUI(this);
1369 }
1370 }
1371 }
1372
1373
1374 /**
1375 * Returns a string representation of this JSlider. This method
1376 * is intended to be used only for debugging purposes, and the
1377 * content and format of the returned string may vary between
1378 * implementations. The returned string may be empty but may not
1379 * be {@code null}.
1380 *
1381 * @return a string representation of this JSlider.
1382 */
1383 protected String paramString() {
1384 String paintTicksString = (paintTicks ?
1385 "true" : "false");
1386 String paintTrackString = (paintTrack ?
1387 "true" : "false");
1388 String paintLabelsString = (paintLabels ?
1389 "true" : "false");
1390 String isInvertedString = (isInverted ?
1391 "true" : "false");
1392 String snapToTicksString = (snapToTicks ?
1393 "true" : "false");
1394 String snapToValueString = (snapToValue ?
1395 "true" : "false");
1396 String orientationString = (orientation == HORIZONTAL ?
1397 "HORIZONTAL" : "VERTICAL");
1398
1399 return super.paramString() +
1414 ////////////////
1415
1416 /**
1417 * Gets the AccessibleContext associated with this JSlider.
1418 * For sliders, the AccessibleContext takes the form of an
1419 * AccessibleJSlider.
1420 * A new AccessibleJSlider instance is created if necessary.
1421 *
1422 * @return an AccessibleJSlider that serves as the
1423 * AccessibleContext of this JSlider
1424 */
1425 public AccessibleContext getAccessibleContext() {
1426 if (accessibleContext == null) {
1427 accessibleContext = new AccessibleJSlider();
1428 }
1429 return accessibleContext;
1430 }
1431
1432 /**
1433 * This class implements accessibility support for the
1434 * {@code JSlider} class. It provides an implementation of the
1435 * Java Accessibility API appropriate to slider user-interface elements.
1436 * <p>
1437 * <strong>Warning:</strong>
1438 * Serialized objects of this class will not be compatible with
1439 * future Swing releases. The current serialization support is
1440 * appropriate for short term storage or RMI between applications running
1441 * the same version of Swing. As of 1.4, support for long term storage
1442 * of all JavaBeans™
1443 * has been added to the {@code java.beans} package.
1444 * Please see {@link java.beans.XMLEncoder}.
1445 */
1446 @SuppressWarnings("serial") // Same-version serialization only
1447 protected class AccessibleJSlider extends AccessibleJComponent
1448 implements AccessibleValue {
1449
1450 /**
1451 * Get the state set of this object.
1452 *
1453 * @return an instance of AccessibleState containing the current state
1454 * of the object
1455 * @see AccessibleState
1456 */
1457 public AccessibleStateSet getAccessibleStateSet() {
1458 AccessibleStateSet states = super.getAccessibleStateSet();
1459 if (getValueIsAdjusting()) {
1460 states.add(AccessibleState.BUSY);
1461 }
1462 if (getOrientation() == VERTICAL) {
1463 states.add(AccessibleState.VERTICAL);
|