30 import java.beans.*;
31 import java.util.Dictionary;
32 import java.util.Enumeration;
33
34 import javax.swing.*;
35 import javax.swing.event.*;
36 import javax.swing.plaf.*;
37 import sun.swing.DefaultLookup;
38 import sun.swing.UIAction;
39
40
41 /**
42 * A Basic L&F implementation of SliderUI.
43 *
44 * @author Tom Santos
45 */
46 public class BasicSliderUI extends SliderUI{
47 // Old actions forward to an instance of this.
48 private static final Actions SHARED_ACTION = new Actions();
49
50 public static final int POSITIVE_SCROLL = +1;
51 public static final int NEGATIVE_SCROLL = -1;
52 public static final int MIN_SCROLL = -2;
53 public static final int MAX_SCROLL = +2;
54
55 protected Timer scrollTimer;
56 protected JSlider slider;
57
58 protected Insets focusInsets = null;
59 protected Insets insetCache = null;
60 protected boolean leftToRightCache = true;
61 protected Rectangle focusRect = null;
62 protected Rectangle contentRect = null;
63 protected Rectangle labelRect = null;
64 protected Rectangle tickRect = null;
65 protected Rectangle trackRect = null;
66 protected Rectangle thumbRect = null;
67
68 protected int trackBuffer = 0; // The distance that the track is from the side of the control
69
70 private transient boolean isDragging;
71
72 protected TrackListener trackListener;
73 protected ChangeListener changeListener;
74 protected ComponentListener componentListener;
75 protected FocusListener focusListener;
76 protected ScrollListener scrollListener;
77 protected PropertyChangeListener propertyChangeListener;
78 private Handler handler;
79 private int lastValue;
80
81 // Colors
82 private Color shadowColor;
83 private Color highlightColor;
84 private Color focusColor;
85
86 /**
87 * Whther or not sameLabelBaselines is up to date.
88 */
89 private boolean checkedLabelBaselines;
90 /**
91 * Whether or not all the entries in the labeltable have the same
92 * baseline.
93 */
94 private boolean sameLabelBaselines;
95
96
97 protected Color getShadowColor() {
98 return shadowColor;
99 }
100
101 protected Color getHighlightColor() {
102 return highlightColor;
103 }
104
105 protected Color getFocusColor() {
106 return focusColor;
107 }
108
109 /**
110 * Returns true if the user is dragging the slider.
111 *
112 * @return true if the user is dragging the slider
113 * @since 1.5
114 */
115 protected boolean isDragging() {
116 return isDragging;
117 }
118
119 /////////////////////////////////////////////////////////////////////////////
120 // ComponentUI Interface Implementation methods
121 /////////////////////////////////////////////////////////////////////////////
122 public static ComponentUI createUI(JComponent b) {
123 return new BasicSliderUI((JSlider)b);
124 }
125
126 public BasicSliderUI(JSlider b) {
127 }
128
129 public void installUI(JComponent c) {
130 slider = (JSlider) c;
131
132 checkedLabelBaselines = false;
133
134 slider.setEnabled(slider.isEnabled());
135 LookAndFeel.installProperty(slider, "opaque", Boolean.TRUE);
136
137 isDragging = false;
138 trackListener = createTrackListener( slider );
139 changeListener = createChangeListener( slider );
140 componentListener = createComponentListener( slider );
141 focusListener = createFocusListener( slider );
142 scrollListener = createScrollListener( slider );
143 propertyChangeListener = createPropertyChangeListener( slider );
144
145 installDefaults( slider );
146 installListeners( slider );
147 installKeyboardActions( slider );
148
149 scrollTimer = new Timer( 100, scrollListener );
150 scrollTimer.setInitialDelay( 300 );
151
152 insetCache = slider.getInsets();
153 leftToRightCache = BasicGraphicsUtils.isLeftToRight(slider);
154 focusRect = new Rectangle();
155 contentRect = new Rectangle();
156 labelRect = new Rectangle();
157 tickRect = new Rectangle();
158 trackRect = new Rectangle();
159 thumbRect = new Rectangle();
160 lastValue = slider.getValue();
161
162 calculateGeometry(); // This figures out where the labels, ticks, track, and thumb are.
163 }
164
165 public void uninstallUI(JComponent c) {
166 if ( c != slider )
167 throw new IllegalComponentStateException(
168 this + " was asked to deinstall() "
169 + c + " when it only knows about "
170 + slider + ".");
171
172 scrollTimer.stop();
173 scrollTimer = null;
174
175 uninstallDefaults(slider);
176 uninstallListeners( slider );
177 uninstallKeyboardActions(slider);
178
179 insetCache = null;
180 leftToRightCache = true;
181 focusRect = null;
182 contentRect = null;
183 labelRect = null;
184 tickRect = null;
185 trackRect = null;
186 thumbRect = null;
187 trackListener = null;
188 changeListener = null;
189 componentListener = null;
190 focusListener = null;
191 scrollListener = null;
192 propertyChangeListener = null;
193 slider = null;
194 }
195
196 protected void installDefaults( JSlider slider ) {
197 LookAndFeel.installBorder(slider, "Slider.border");
198 LookAndFeel.installColorsAndFont(slider, "Slider.background",
199 "Slider.foreground", "Slider.font");
200 highlightColor = UIManager.getColor("Slider.highlight");
201
202 shadowColor = UIManager.getColor("Slider.shadow");
203 focusColor = UIManager.getColor("Slider.focus");
204
205 focusInsets = (Insets)UIManager.get( "Slider.focusInsets" );
206 // use default if missing so that BasicSliderUI can be used in other
207 // LAFs like Nimbus
208 if (focusInsets == null) focusInsets = new InsetsUIResource(2,2,2,2);
209 }
210
211 protected void uninstallDefaults(JSlider slider) {
212 LookAndFeel.uninstallBorder(slider);
213
214 focusInsets = null;
215 }
216
217 protected TrackListener createTrackListener(JSlider slider) {
218 return new TrackListener();
219 }
220
221 protected ChangeListener createChangeListener(JSlider slider) {
222 return getHandler();
223 }
224
225 protected ComponentListener createComponentListener(JSlider slider) {
226 return getHandler();
227 }
228
229 protected FocusListener createFocusListener(JSlider slider) {
230 return getHandler();
231 }
232
233 protected ScrollListener createScrollListener( JSlider slider ) {
234 return new ScrollListener();
235 }
236
237 protected PropertyChangeListener createPropertyChangeListener(
238 JSlider slider) {
239 return getHandler();
240 }
241
242 private Handler getHandler() {
243 if (handler == null) {
244 handler = new Handler();
245 }
246 return handler;
247 }
248
249 protected void installListeners( JSlider slider ) {
250 slider.addMouseListener(trackListener);
251 slider.addMouseMotionListener(trackListener);
252 slider.addFocusListener(focusListener);
253 slider.addComponentListener(componentListener);
254 slider.addPropertyChangeListener( propertyChangeListener );
255 slider.getModel().addChangeListener(changeListener);
256 }
257
258 protected void uninstallListeners( JSlider slider ) {
259 slider.removeMouseListener(trackListener);
260 slider.removeMouseMotionListener(trackListener);
261 slider.removeFocusListener(focusListener);
262 slider.removeComponentListener(componentListener);
263 slider.removePropertyChangeListener( propertyChangeListener );
264 slider.getModel().removeChangeListener(changeListener);
265 handler = null;
266 }
267
268 protected void installKeyboardActions( JSlider slider ) {
269 InputMap km = getInputMap(JComponent.WHEN_FOCUSED, slider);
270 SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, km);
271 LazyActionMap.installLazyActionMap(slider, BasicSliderUI.class,
272 "Slider.actionMap");
273 }
274
275 InputMap getInputMap(int condition, JSlider slider) {
276 if (condition == JComponent.WHEN_FOCUSED) {
277 InputMap keyMap = (InputMap)DefaultLookup.get(slider, this,
278 "Slider.focusInputMap");
279 InputMap rtlKeyMap;
280
281 if (slider.getComponentOrientation().isLeftToRight() ||
282 ((rtlKeyMap = (InputMap)DefaultLookup.get(slider, this,
283 "Slider.focusInputMap.RightToLeft")) == null)) {
284 return keyMap;
285 } else {
286 rtlKeyMap.setParent(keyMap);
287 return rtlKeyMap;
288 }
289 }
290 return null;
291 }
292
293 /**
294 * Populates ComboBox's actions.
295 */
296 static void loadActionMap(LazyActionMap map) {
297 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
298 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
299 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
300 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
301 map.put(new Actions(Actions.MIN_SCROLL_INCREMENT));
302 map.put(new Actions(Actions.MAX_SCROLL_INCREMENT));
303 }
304
305 protected void uninstallKeyboardActions( JSlider slider ) {
306 SwingUtilities.replaceUIActionMap(slider, null);
307 SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED,
308 null);
309 }
310
311
312 /**
313 * Returns the baseline.
314 *
315 * @throws NullPointerException {@inheritDoc}
316 * @throws IllegalArgumentException {@inheritDoc}
317 * @see javax.swing.JComponent#getBaseline(int, int)
318 * @since 1.6
319 */
320 public int getBaseline(JComponent c, int width, int height) {
321 super.getBaseline(c, width, height);
322 if (slider.getPaintLabels() && labelsHaveSameBaselines()) {
323 FontMetrics metrics = slider.getFontMetrics(slider.getFont());
324 Insets insets = slider.getInsets();
413 baseline = labelBaseline;
414 }
415 else if (baseline != labelBaseline) {
416 sameLabelBaselines = false;
417 break;
418 }
419 }
420 else {
421 sameLabelBaselines = false;
422 break;
423 }
424 }
425 }
426 else {
427 sameLabelBaselines = false;
428 }
429 }
430 return sameLabelBaselines;
431 }
432
433 public Dimension getPreferredHorizontalSize() {
434 Dimension horizDim = (Dimension)DefaultLookup.get(slider,
435 this, "Slider.horizontalSize");
436 if (horizDim == null) {
437 horizDim = new Dimension(200, 21);
438 }
439 return horizDim;
440 }
441
442 public Dimension getPreferredVerticalSize() {
443 Dimension vertDim = (Dimension)DefaultLookup.get(slider,
444 this, "Slider.verticalSize");
445 if (vertDim == null) {
446 vertDim = new Dimension(21, 200);
447 }
448 return vertDim;
449 }
450
451 public Dimension getMinimumHorizontalSize() {
452 Dimension minHorizDim = (Dimension)DefaultLookup.get(slider,
453 this, "Slider.minimumHorizontalSize");
454 if (minHorizDim == null) {
455 minHorizDim = new Dimension(36, 21);
456 }
457 return minHorizDim;
458 }
459
460 public Dimension getMinimumVerticalSize() {
461 Dimension minVertDim = (Dimension)DefaultLookup.get(slider,
462 this, "Slider.minimumVerticalSize");
463 if (minVertDim == null) {
464 minVertDim = new Dimension(21, 36);
465 }
466 return minVertDim;
467 }
468
469 public Dimension getPreferredSize(JComponent c) {
470 recalculateIfInsetsChanged();
471 Dimension d;
472 if ( slider.getOrientation() == JSlider.VERTICAL ) {
473 d = new Dimension(getPreferredVerticalSize());
474 d.width = insetCache.left + insetCache.right;
475 d.width += focusInsets.left + focusInsets.right;
476 d.width += trackRect.width + tickRect.width + labelRect.width;
477 }
478 else {
479 d = new Dimension(getPreferredHorizontalSize());
480 d.height = insetCache.top + insetCache.bottom;
481 d.height += focusInsets.top + focusInsets.bottom;
482 d.height += trackRect.height + tickRect.height + labelRect.height;
483 }
484
485 return d;
486 }
487
488 public Dimension getMinimumSize(JComponent c) {
489 recalculateIfInsetsChanged();
490 Dimension d;
491
492 if ( slider.getOrientation() == JSlider.VERTICAL ) {
493 d = new Dimension(getMinimumVerticalSize());
494 d.width = insetCache.left + insetCache.right;
495 d.width += focusInsets.left + focusInsets.right;
496 d.width += trackRect.width + tickRect.width + labelRect.width;
497 }
498 else {
499 d = new Dimension(getMinimumHorizontalSize());
500 d.height = insetCache.top + insetCache.bottom;
501 d.height += focusInsets.top + focusInsets.bottom;
502 d.height += trackRect.height + tickRect.height + labelRect.height;
503 }
504
505 return d;
506 }
507
508 public Dimension getMaximumSize(JComponent c) {
509 Dimension d = getPreferredSize(c);
510 if ( slider.getOrientation() == JSlider.VERTICAL ) {
511 d.height = Short.MAX_VALUE;
512 }
513 else {
514 d.width = Short.MAX_VALUE;
515 }
516
517 return d;
518 }
519
520 protected void calculateGeometry() {
521 calculateFocusRect();
522 calculateContentRect();
523 calculateThumbSize();
524 calculateTrackBuffer();
525 calculateTrackRect();
526 calculateTickRect();
527 calculateLabelRect();
528 calculateThumbLocation();
529 }
530
531 protected void calculateFocusRect() {
532 focusRect.x = insetCache.left;
533 focusRect.y = insetCache.top;
534 focusRect.width = slider.getWidth() - (insetCache.left + insetCache.right);
535 focusRect.height = slider.getHeight() - (insetCache.top + insetCache.bottom);
536 }
537
538 protected void calculateThumbSize() {
539 Dimension size = getThumbSize();
540 thumbRect.setSize( size.width, size.height );
541 }
542
543 protected void calculateContentRect() {
544 contentRect.x = focusRect.x + focusInsets.left;
545 contentRect.y = focusRect.y + focusInsets.top;
546 contentRect.width = focusRect.width - (focusInsets.left + focusInsets.right);
547 contentRect.height = focusRect.height - (focusInsets.top + focusInsets.bottom);
548 }
549
550 private int getTickSpacing() {
551 int majorTickSpacing = slider.getMajorTickSpacing();
552 int minorTickSpacing = slider.getMinorTickSpacing();
553
554 int result;
555
556 if (minorTickSpacing > 0) {
557 result = minorTickSpacing;
558 } else if (majorTickSpacing > 0) {
559 result = majorTickSpacing;
560 } else {
561 result = 0;
562 }
563
564 return result;
565 }
566
567 protected void calculateThumbLocation() {
568 if ( slider.getSnapToTicks() ) {
569 int sliderValue = slider.getValue();
570 int snappedValue = sliderValue;
571 int tickSpacing = getTickSpacing();
572
573 if ( tickSpacing != 0 ) {
574 // If it's not on a tick, change the value
575 if ( (sliderValue - slider.getMinimum()) % tickSpacing != 0 ) {
576 float temp = (float)(sliderValue - slider.getMinimum()) / (float)tickSpacing;
577 int whichTick = Math.round( temp );
578
579 // This is the fix for the bug #6401380
580 if (temp - (int)temp == .5 && sliderValue < lastValue) {
581 whichTick --;
582 }
583 snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
584 }
585
586 if( snappedValue != sliderValue ) {
587 slider.setValue( snappedValue );
588 }
589 }
590 }
591
592 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
593 int valuePosition = xPositionForValue(slider.getValue());
594
595 thumbRect.x = valuePosition - (thumbRect.width / 2);
596 thumbRect.y = trackRect.y;
597 }
598 else {
599 int valuePosition = yPositionForValue(slider.getValue());
600
601 thumbRect.x = trackRect.x;
602 thumbRect.y = valuePosition - (thumbRect.height / 2);
603 }
604 }
605
606 protected void calculateTrackBuffer() {
607 if ( slider.getPaintLabels() && slider.getLabelTable() != null ) {
608 Component highLabel = getHighestValueLabel();
609 Component lowLabel = getLowestValueLabel();
610
611 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
612 trackBuffer = Math.max( highLabel.getBounds().width, lowLabel.getBounds().width ) / 2;
613 trackBuffer = Math.max( trackBuffer, thumbRect.width / 2 );
614 }
615 else {
616 trackBuffer = Math.max( highLabel.getBounds().height, lowLabel.getBounds().height ) / 2;
617 trackBuffer = Math.max( trackBuffer, thumbRect.height / 2 );
618 }
619 }
620 else {
621 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
622 trackBuffer = thumbRect.width / 2;
623 }
624 else {
625 trackBuffer = thumbRect.height / 2;
626 }
627 }
628 }
629
630
631 protected void calculateTrackRect() {
632 int centerSpacing; // used to center sliders added using BorderLayout.CENTER (bug 4275631)
633 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
634 centerSpacing = thumbRect.height;
635 if ( slider.getPaintTicks() ) centerSpacing += getTickLength();
636 if ( slider.getPaintLabels() ) centerSpacing += getHeightOfTallestLabel();
637 trackRect.x = contentRect.x + trackBuffer;
638 trackRect.y = contentRect.y + (contentRect.height - centerSpacing - 1)/2;
639 trackRect.width = contentRect.width - (trackBuffer * 2);
640 trackRect.height = thumbRect.height;
641 }
642 else {
643 centerSpacing = thumbRect.width;
644 if (BasicGraphicsUtils.isLeftToRight(slider)) {
645 if ( slider.getPaintTicks() ) centerSpacing += getTickLength();
646 if ( slider.getPaintLabels() ) centerSpacing += getWidthOfWidestLabel();
647 } else {
648 if ( slider.getPaintTicks() ) centerSpacing -= getTickLength();
649 if ( slider.getPaintLabels() ) centerSpacing -= getWidthOfWidestLabel();
650 }
654 trackRect.height = contentRect.height - (trackBuffer * 2);
655 }
656
657 }
658
659 /**
660 * Gets the height of the tick area for horizontal sliders and the width of
661 * the tick area for vertical sliders. BasicSliderUI uses the returned value
662 * to determine the tick area rectangle. If you want to give your ticks some
663 * room, make this larger than you need and paint your ticks away from the
664 * sides in paintTicks().
665 *
666 * @return an integer representing the height of the tick area for
667 * horizontal sliders, and the width of the tick area for the vertical
668 * sliders
669 */
670 protected int getTickLength() {
671 return 8;
672 }
673
674 protected void calculateTickRect() {
675 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
676 tickRect.x = trackRect.x;
677 tickRect.y = trackRect.y + trackRect.height;
678 tickRect.width = trackRect.width;
679 tickRect.height = (slider.getPaintTicks()) ? getTickLength() : 0;
680 }
681 else {
682 tickRect.width = (slider.getPaintTicks()) ? getTickLength() : 0;
683 if(BasicGraphicsUtils.isLeftToRight(slider)) {
684 tickRect.x = trackRect.x + trackRect.width;
685 }
686 else {
687 tickRect.x = trackRect.x - tickRect.width;
688 }
689 tickRect.y = trackRect.y;
690 tickRect.height = trackRect.height;
691 }
692 }
693
694 protected void calculateLabelRect() {
695 if ( slider.getPaintLabels() ) {
696 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
697 labelRect.x = tickRect.x - trackBuffer;
698 labelRect.y = tickRect.y + tickRect.height;
699 labelRect.width = tickRect.width + (trackBuffer * 2);
700 labelRect.height = getHeightOfTallestLabel();
701 }
702 else {
703 if(BasicGraphicsUtils.isLeftToRight(slider)) {
704 labelRect.x = tickRect.x + tickRect.width;
705 labelRect.width = getWidthOfWidestLabel();
706 }
707 else {
708 labelRect.width = getWidthOfWidestLabel();
709 labelRect.x = tickRect.x - labelRect.width;
710 }
711 labelRect.y = tickRect.y - trackBuffer;
712 labelRect.height = tickRect.height + (trackBuffer * 2);
713 }
716 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
717 labelRect.x = tickRect.x;
718 labelRect.y = tickRect.y + tickRect.height;
719 labelRect.width = tickRect.width;
720 labelRect.height = 0;
721 }
722 else {
723 if(BasicGraphicsUtils.isLeftToRight(slider)) {
724 labelRect.x = tickRect.x + tickRect.width;
725 }
726 else {
727 labelRect.x = tickRect.x;
728 }
729 labelRect.y = tickRect.y;
730 labelRect.width = 0;
731 labelRect.height = tickRect.height;
732 }
733 }
734 }
735
736 protected Dimension getThumbSize() {
737 Dimension size = new Dimension();
738
739 if ( slider.getOrientation() == JSlider.VERTICAL ) {
740 size.width = 20;
741 size.height = 11;
742 }
743 else {
744 size.width = 11;
745 size.height = 20;
746 }
747
748 return size;
749 }
750
751 public class PropertyChangeHandler implements PropertyChangeListener {
752 // NOTE: This class exists only for backward compatibility. All
753 // its functionality has been moved into Handler. If you need to add
754 // new functionality add it to the Handler, but make sure this
755 // class calls into the Handler.
756 public void propertyChange( PropertyChangeEvent e ) {
757 getHandler().propertyChange(e);
758 }
759 }
760
761 protected int getWidthOfWidestLabel() {
762 @SuppressWarnings("rawtypes")
763 Dictionary dictionary = slider.getLabelTable();
764 int widest = 0;
765 if ( dictionary != null ) {
766 Enumeration<?> keys = dictionary.keys();
767 while ( keys.hasMoreElements() ) {
768 JComponent label = (JComponent) dictionary.get(keys.nextElement());
769 widest = Math.max( label.getPreferredSize().width, widest );
770 }
771 }
772 return widest;
773 }
774
775 protected int getHeightOfTallestLabel() {
776 @SuppressWarnings("rawtypes")
777 Dictionary dictionary = slider.getLabelTable();
778 int tallest = 0;
779 if ( dictionary != null ) {
780 Enumeration<?> keys = dictionary.keys();
781 while ( keys.hasMoreElements() ) {
782 JComponent label = (JComponent) dictionary.get(keys.nextElement());
783 tallest = Math.max( label.getPreferredSize().height, tallest );
784 }
785 }
786 return tallest;
787 }
788
789 protected int getWidthOfHighValueLabel() {
790 Component label = getHighestValueLabel();
791 int width = 0;
792
793 if ( label != null ) {
794 width = label.getPreferredSize().width;
795 }
796
797 return width;
798 }
799
800 protected int getWidthOfLowValueLabel() {
801 Component label = getLowestValueLabel();
802 int width = 0;
803
804 if ( label != null ) {
805 width = label.getPreferredSize().width;
806 }
807
808 return width;
809 }
810
811 protected int getHeightOfHighValueLabel() {
812 Component label = getHighestValueLabel();
813 int height = 0;
814
815 if ( label != null ) {
816 height = label.getPreferredSize().height;
817 }
818
819 return height;
820 }
821
822 protected int getHeightOfLowValueLabel() {
823 Component label = getLowestValueLabel();
824 int height = 0;
825
826 if ( label != null ) {
827 height = label.getPreferredSize().height;
828 }
829
830 return height;
831 }
832
833 protected boolean drawInverted() {
834 if (slider.getOrientation()==JSlider.HORIZONTAL) {
835 if(BasicGraphicsUtils.isLeftToRight(slider)) {
836 return slider.getInverted();
837 } else {
838 return !slider.getInverted();
839 }
840 } else {
841 return slider.getInverted();
842 }
843 }
844
845 /**
846 * Returns the biggest value that has an entry in the label table.
847 *
848 * @return biggest value that has an entry in the label table, or
849 * null.
850 * @since 1.6
851 */
852 protected Integer getHighestValue() {
943 if ( !clip.intersects(trackRect) && slider.getPaintTrack())
944 calculateGeometry();
945
946 if ( slider.getPaintTrack() && clip.intersects( trackRect ) ) {
947 paintTrack( g );
948 }
949 if ( slider.getPaintTicks() && clip.intersects( tickRect ) ) {
950 paintTicks( g );
951 }
952 if ( slider.getPaintLabels() && clip.intersects( labelRect ) ) {
953 paintLabels( g );
954 }
955 if ( slider.hasFocus() && clip.intersects( focusRect ) ) {
956 paintFocus( g );
957 }
958 if ( clip.intersects( thumbRect ) ) {
959 paintThumb( g );
960 }
961 }
962
963 protected void recalculateIfInsetsChanged() {
964 Insets newInsets = slider.getInsets();
965 if ( !newInsets.equals( insetCache ) ) {
966 insetCache = newInsets;
967 calculateGeometry();
968 }
969 }
970
971 protected void recalculateIfOrientationChanged() {
972 boolean ltr = BasicGraphicsUtils.isLeftToRight(slider);
973 if ( ltr!=leftToRightCache ) {
974 leftToRightCache = ltr;
975 calculateGeometry();
976 }
977 }
978
979 public void paintFocus(Graphics g) {
980 g.setColor( getFocusColor() );
981
982 BasicGraphicsUtils.drawDashedRect( g, focusRect.x, focusRect.y,
983 focusRect.width, focusRect.height );
984 }
985
986 public void paintTrack(Graphics g) {
987
988 Rectangle trackBounds = trackRect;
989
990 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
991 int cy = (trackBounds.height / 2) - 2;
992 int cw = trackBounds.width;
993
994 g.translate(trackBounds.x, trackBounds.y + cy);
995
996 g.setColor(getShadowColor());
997 g.drawLine(0, 0, cw - 1, 0);
998 g.drawLine(0, 1, 0, 2);
999 g.setColor(getHighlightColor());
1000 g.drawLine(0, 3, cw, 3);
1001 g.drawLine(cw, 0, cw, 3);
1002 g.setColor(Color.black);
1003 g.drawLine(1, 1, cw-2, 1);
1004
1005 g.translate(-trackBounds.x, -(trackBounds.y + cy));
1006 }
1007 else {
1008 int cx = (trackBounds.width / 2) - 2;
1009 int ch = trackBounds.height;
1010
1011 g.translate(trackBounds.x + cx, trackBounds.y);
1012
1013 g.setColor(getShadowColor());
1014 g.drawLine(0, 0, 0, ch - 1);
1015 g.drawLine(1, 0, 2, 0);
1016 g.setColor(getHighlightColor());
1017 g.drawLine(3, 0, 3, ch);
1018 g.drawLine(0, ch, 3, ch);
1019 g.setColor(Color.black);
1020 g.drawLine(1, 1, 1, ch-2);
1021
1022 g.translate(-(trackBounds.x + cx), -trackBounds.y);
1023 }
1024 }
1025
1026 public void paintTicks(Graphics g) {
1027 Rectangle tickBounds = tickRect;
1028
1029 g.setColor(DefaultLookup.getColor(slider, this, "Slider.tickColor", Color.black));
1030
1031 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
1032 g.translate(0, tickBounds.y);
1033
1034 if (slider.getMinorTickSpacing() > 0) {
1035 int value = slider.getMinimum();
1036
1037 while ( value <= slider.getMaximum() ) {
1038 int xPos = xPositionForValue(value);
1039 paintMinorTickForHorizSlider( g, tickBounds, xPos );
1040
1041 // Overflow checking
1042 if (Integer.MAX_VALUE - slider.getMinorTickSpacing() < value) {
1043 break;
1044 }
1045
1103 while (value <= slider.getMaximum()) {
1104 int yPos = yPositionForValue(value);
1105 paintMajorTickForVertSlider( g, tickBounds, yPos );
1106
1107 // Overflow checking
1108 if (Integer.MAX_VALUE - slider.getMajorTickSpacing() < value) {
1109 break;
1110 }
1111
1112 value += slider.getMajorTickSpacing();
1113 }
1114
1115 if(!BasicGraphicsUtils.isLeftToRight(slider)) {
1116 g.translate(-2, 0);
1117 }
1118 }
1119 g.translate(-tickBounds.x, 0);
1120 }
1121 }
1122
1123 protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
1124 g.drawLine( x, 0, x, tickBounds.height / 2 - 1 );
1125 }
1126
1127 protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
1128 g.drawLine( x, 0, x, tickBounds.height - 2 );
1129 }
1130
1131 protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
1132 g.drawLine( 0, y, tickBounds.width / 2 - 1, y );
1133 }
1134
1135 protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
1136 g.drawLine( 0, y, tickBounds.width - 2, y );
1137 }
1138
1139 public void paintLabels( Graphics g ) {
1140 Rectangle labelBounds = labelRect;
1141
1142 @SuppressWarnings("rawtypes")
1143 Dictionary dictionary = slider.getLabelTable();
1144 if ( dictionary != null ) {
1145 Enumeration<?> keys = dictionary.keys();
1146 int minValue = slider.getMinimum();
1147 int maxValue = slider.getMaximum();
1148 boolean enabled = slider.isEnabled();
1149 while ( keys.hasMoreElements() ) {
1150 Integer key = (Integer)keys.nextElement();
1151 int value = key.intValue();
1152 if (value >= minValue && value <= maxValue) {
1153 JComponent label = (JComponent) dictionary.get(key);
1154 label.setEnabled(enabled);
1155
1156 if (label instanceof JLabel) {
1157 Icon icon = label.isEnabled() ? ((JLabel) label).getIcon() : ((JLabel) label).getDisabledIcon();
1158
1205
1206 /**
1207 * Called for every label in the label table. Used to draw the labels for
1208 * vertical sliders. The graphics have been translated to labelRect.x
1209 * already.
1210 *
1211 * @param g the graphics context in which to paint
1212 * @param value the value of the slider
1213 * @param label the component label in the label table that needs to be
1214 * painted
1215 * @see JSlider#setLabelTable
1216 */
1217 protected void paintVerticalLabel( Graphics g, int value, Component label ) {
1218 int labelCenter = yPositionForValue( value );
1219 int labelTop = labelCenter - (label.getPreferredSize().height / 2);
1220 g.translate( 0, labelTop );
1221 label.paint( g );
1222 g.translate( 0, -labelTop );
1223 }
1224
1225 public void paintThumb(Graphics g) {
1226 Rectangle knobBounds = thumbRect;
1227 int w = knobBounds.width;
1228 int h = knobBounds.height;
1229
1230 g.translate(knobBounds.x, knobBounds.y);
1231
1232 if ( slider.isEnabled() ) {
1233 g.setColor(slider.getBackground());
1234 }
1235 else {
1236 g.setColor(slider.getBackground().darker());
1237 }
1238
1239 Boolean paintThumbArrowShape =
1240 (Boolean)slider.getClientProperty("Slider.paintThumbArrowShape");
1241
1242 if ((!slider.getPaintTicks() && paintThumbArrowShape == null) ||
1243 paintThumbArrowShape == Boolean.FALSE) {
1244
1313 g.setColor(highlightColor);
1314 g.drawLine(cw-1, 0, w-2, 0); // top
1315 g.drawLine(0, cw, cw, 0); // top slant
1316
1317 g.setColor(Color.black);
1318 g.drawLine(0, h-1-cw, cw, h-1 ); // bottom slant
1319 g.drawLine(cw, h-1, w-1, h-1); // bottom
1320
1321 g.setColor(shadowColor);
1322 g.drawLine(cw, h-2, w-2, h-2 ); // bottom
1323 g.drawLine(w-1, 1, w-1, h-2 ); // right
1324 }
1325 }
1326
1327 g.translate(-knobBounds.x, -knobBounds.y);
1328 }
1329
1330 // Used exclusively by setThumbLocation()
1331 private static Rectangle unionRect = new Rectangle();
1332
1333 public void setThumbLocation(int x, int y) {
1334 unionRect.setBounds( thumbRect );
1335
1336 thumbRect.setLocation( x, y );
1337
1338 SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, unionRect );
1339 slider.repaint( unionRect.x, unionRect.y, unionRect.width, unionRect.height );
1340 }
1341
1342 public void scrollByBlock(int direction) {
1343 synchronized(slider) {
1344 int blockIncrement =
1345 (slider.getMaximum() - slider.getMinimum()) / 10;
1346 if (blockIncrement == 0) {
1347 blockIncrement = 1;
1348 }
1349
1350 if (slider.getSnapToTicks()) {
1351 int tickSpacing = getTickSpacing();
1352
1353 if (blockIncrement < tickSpacing) {
1354 blockIncrement = tickSpacing;
1355 }
1356 }
1357
1358 int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
1359 slider.setValue(slider.getValue() + delta);
1360 }
1361 }
1362
1363 public void scrollByUnit(int direction) {
1364 synchronized(slider) {
1365 int delta = ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
1366
1367 if (slider.getSnapToTicks()) {
1368 delta *= getTickSpacing();
1369 }
1370
1371 slider.setValue(slider.getValue() + delta);
1372 }
1373 }
1374
1375 /**
1376 * This function is called when a mousePressed was detected in the track,
1377 * not in the thumb. The default behavior is to scroll by block. You can
1378 * override this method to stop it from scrolling or to add additional
1379 * behavior.
1380 *
1381 * @param dir the direction and number of blocks to scroll
1382 */
1383 protected void scrollDueToClickInTrack( int dir ) {
1384 scrollByBlock( dir );
1385 }
1386
1387 protected int xPositionForValue( int value ) {
1388 int min = slider.getMinimum();
1389 int max = slider.getMaximum();
1390 int trackLength = trackRect.width;
1391 double valueRange = (double)max - (double)min;
1392 double pixelsPerValue = (double)trackLength / valueRange;
1393 int trackLeft = trackRect.x;
1394 int trackRight = trackRect.x + (trackRect.width - 1);
1395 int xPosition;
1396
1397 if ( !drawInverted() ) {
1398 xPosition = trackLeft;
1399 xPosition += Math.round( pixelsPerValue * ((double)value - min) );
1400 }
1401 else {
1402 xPosition = trackRight;
1403 xPosition -= Math.round( pixelsPerValue * ((double)value - min) );
1404 }
1405
1406 xPosition = Math.max( trackLeft, xPosition );
1407 xPosition = Math.min( trackRight, xPosition );
1408
1409 return xPosition;
1410 }
1411
1412 protected int yPositionForValue( int value ) {
1413 return yPositionForValue(value, trackRect.y, trackRect.height);
1414 }
1415
1416 /**
1417 * Returns the y location for the specified value. No checking is
1418 * done on the arguments. In particular if <code>trackHeight</code> is
1419 * negative undefined results may occur.
1420 *
1421 * @param value the slider value to get the location for
1422 * @param trackY y-origin of the track
1423 * @param trackHeight the height of the track
1424 * @return the y location for the specified value of the slider
1425 * @since 1.6
1426 */
1427 protected int yPositionForValue(int value, int trackY, int trackHeight) {
1428 int min = slider.getMinimum();
1429 int max = slider.getMaximum();
1430 double valueRange = (double)max - (double)min;
1431 double pixelsPerValue = (double)trackHeight / valueRange;
1588 public class ChangeHandler implements ChangeListener {
1589 // NOTE: This class exists only for backward compatibility. All
1590 // its functionality has been moved into Handler. If you need to add
1591 // new functionality add it to the Handler, but make sure this
1592 // class calls into the Handler.
1593 public void stateChanged(ChangeEvent e) {
1594 getHandler().stateChanged(e);
1595 }
1596 }
1597
1598 /////////////////////////////////////////////////////////////////////////
1599 /// Track Listener Class
1600 /////////////////////////////////////////////////////////////////////////
1601 /**
1602 * Track mouse movements.
1603 *
1604 * This class should be treated as a "protected" inner class.
1605 * Instantiate it only within subclasses of <code>Foo</code>.
1606 */
1607 public class TrackListener extends MouseInputAdapter {
1608 protected transient int offset;
1609 protected transient int currentMouseX, currentMouseY;
1610
1611 public void mouseReleased(MouseEvent e) {
1612 if (!slider.isEnabled()) {
1613 return;
1614 }
1615
1616 offset = 0;
1617 scrollTimer.stop();
1618
1619 isDragging = false;
1620 slider.setValueIsAdjusting(false);
1621 slider.repaint();
1622 }
1623
1624 /**
1625 * If the mouse is pressed above the "thumb" component
1626 * then reduce the scrollbars value by one page ("page up"),
1627 * otherwise increase it by one page. If there is no
1628 * thumb then page up if the mouse is in the upper half
1629 * of the track.
1630 */
1719 NEGATIVE_SCROLL : POSITIVE_SCROLL;
1720 }
1721 else {
1722 direction = (currentMouseX < thumbX) ?
1723 POSITIVE_SCROLL : NEGATIVE_SCROLL;
1724 }
1725 }
1726 break;
1727 }
1728
1729 if (shouldScroll(direction)) {
1730 scrollDueToClickInTrack(direction);
1731 }
1732 if (shouldScroll(direction)) {
1733 scrollTimer.stop();
1734 scrollListener.setDirection(direction);
1735 scrollTimer.start();
1736 }
1737 }
1738
1739 public boolean shouldScroll(int direction) {
1740 Rectangle r = thumbRect;
1741 if (slider.getOrientation() == JSlider.VERTICAL) {
1742 if (drawInverted() ? direction < 0 : direction > 0) {
1743 if (r.y <= currentMouseY) {
1744 return false;
1745 }
1746 }
1747 else if (r.y + r.height >= currentMouseY) {
1748 return false;
1749 }
1750 }
1751 else {
1752 if (drawInverted() ? direction < 0 : direction > 0) {
1753 if (r.x + r.width >= currentMouseX) {
1754 return false;
1755 }
1756 }
1757 else if (r.x <= currentMouseX) {
1758 return false;
1822 int hMax = xPositionForValue(slider.getMaximum() -
1823 slider.getExtent());
1824
1825 if (drawInverted()) {
1826 trackLeft = hMax;
1827 }
1828 else {
1829 trackRight = hMax;
1830 }
1831 thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
1832 thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
1833
1834 setThumbLocation(thumbLeft, thumbRect.y);
1835
1836 thumbMiddle = thumbLeft + halfThumbWidth;
1837 slider.setValue(valueForXPosition(thumbMiddle));
1838 break;
1839 }
1840 }
1841
1842 public void mouseMoved(MouseEvent e) { }
1843 }
1844
1845 /**
1846 * Scroll-event listener.
1847 *
1848 * This class should be treated as a "protected" inner class.
1849 * Instantiate it only within subclasses of <code>Foo</code>.
1850 */
1851 public class ScrollListener implements ActionListener {
1852 // changed this class to public to avoid bogus IllegalAccessException
1853 // bug in InternetExplorer browser. It was protected. Work around
1854 // for 4109432
1855 int direction = POSITIVE_SCROLL;
1856 boolean useBlockIncrement;
1857
1858 public ScrollListener() {
1859 direction = POSITIVE_SCROLL;
1860 useBlockIncrement = true;
1861 }
1862
1863 public ScrollListener(int dir, boolean block) {
1864 direction = dir;
1865 useBlockIncrement = block;
1866 }
1867
1868 public void setDirection(int direction) {
1869 this.direction = direction;
1870 }
1871
1872 public void setScrollByBlock(boolean block) {
1873 this.useBlockIncrement = block;
1874 }
1875
1876 public void actionPerformed(ActionEvent e) {
1877 if (useBlockIncrement) {
1878 scrollByBlock(direction);
1879 }
1880 else {
1881 scrollByUnit(direction);
1882 }
1883 if (!trackListener.shouldScroll(direction)) {
1884 ((Timer)e.getSource()).stop();
1885 }
1886 }
1887 }
1888
1889 /**
1890 * Listener for resizing events.
1891 * <p>
1892 * This class should be treated as a "protected" inner class.
1893 * Instantiate it only within subclasses of <code>Foo</code>.
1894 */
1895 public class ComponentHandler extends ComponentAdapter {
1928 * combination of an <code>ActionMap</code>, to contain the action,
1929 * and an <code>InputMap</code> to contain the mapping from KeyStroke
1930 * to action description. The InputMap is usually described in the
1931 * LookAndFeel tables.
1932 * <p>
1933 * Please refer to the key bindings specification for further details.
1934 * <p>
1935 * This class should be treated as a "protected" inner class.
1936 * Instantiate it only within subclasses of <code>Foo</code>.
1937 */
1938 @SuppressWarnings("serial") // Superclass is not serializable across versions
1939 public class ActionScroller extends AbstractAction {
1940 // NOTE: This class exists only for backward compatibility. All
1941 // its functionality has been moved into Actions. If you need to add
1942 // new functionality add it to the Actions, but make sure this
1943 // class calls into the Actions.
1944 int dir;
1945 boolean block;
1946 JSlider slider;
1947
1948 public ActionScroller( JSlider slider, int dir, boolean block) {
1949 this.dir = dir;
1950 this.block = block;
1951 this.slider = slider;
1952 }
1953
1954 public void actionPerformed(ActionEvent e) {
1955 SHARED_ACTION.scroll(slider, BasicSliderUI.this, dir, block);
1956 }
1957
1958 public boolean isEnabled() {
1959 boolean b = true;
1960 if (slider != null) {
1961 b = slider.isEnabled();
1962 }
1963 return b;
1964 }
1965
1966 }
1967
1968
1969 /**
1970 * A static version of the above.
1971 */
1972 @SuppressWarnings("serial") // Superclass is not serializable across versions
1973 static class SharedActionScroller extends AbstractAction {
1974 // NOTE: This class exists only for backward compatibility. All
1975 // its functionality has been moved into Actions. If you need to add
1976 // new functionality add it to the Actions, but make sure this
1977 // class calls into the Actions.
|
30 import java.beans.*;
31 import java.util.Dictionary;
32 import java.util.Enumeration;
33
34 import javax.swing.*;
35 import javax.swing.event.*;
36 import javax.swing.plaf.*;
37 import sun.swing.DefaultLookup;
38 import sun.swing.UIAction;
39
40
41 /**
42 * A Basic L&F implementation of SliderUI.
43 *
44 * @author Tom Santos
45 */
46 public class BasicSliderUI extends SliderUI{
47 // Old actions forward to an instance of this.
48 private static final Actions SHARED_ACTION = new Actions();
49
50 /** Positive scroll */
51 public static final int POSITIVE_SCROLL = +1;
52 /** Negative scroll */
53 public static final int NEGATIVE_SCROLL = -1;
54 /** Minimum scroll */
55 public static final int MIN_SCROLL = -2;
56 /** Maximum scroll */
57 public static final int MAX_SCROLL = +2;
58
59 /** Scroll timer */
60 protected Timer scrollTimer;
61 /** Slider */
62 protected JSlider slider;
63
64 /** Focus insets */
65 protected Insets focusInsets = null;
66 /** Inset cache */
67 protected Insets insetCache = null;
68 /** Left-to-right cache */
69 protected boolean leftToRightCache = true;
70 /** Focus rectangle */
71 protected Rectangle focusRect = null;
72 /** Content rectangle */
73 protected Rectangle contentRect = null;
74 /** Label rectangle */
75 protected Rectangle labelRect = null;
76 /** Tick rectangle */
77 protected Rectangle tickRect = null;
78 /** Track rectangle */
79 protected Rectangle trackRect = null;
80 /** Thumb rectangle */
81 protected Rectangle thumbRect = null;
82
83 /** The distance that the track is from the side of the control */
84 protected int trackBuffer = 0;
85
86 private transient boolean isDragging;
87
88 /** Track listener */
89 protected TrackListener trackListener;
90 /** Change listener */
91 protected ChangeListener changeListener;
92 /** Component listener */
93 protected ComponentListener componentListener;
94 /** Focus listener */
95 protected FocusListener focusListener;
96 /** Scroll listener */
97 protected ScrollListener scrollListener;
98 /** Property chane listener */
99 protected PropertyChangeListener propertyChangeListener;
100 private Handler handler;
101 private int lastValue;
102
103 // Colors
104 private Color shadowColor;
105 private Color highlightColor;
106 private Color focusColor;
107
108 /**
109 * Whther or not sameLabelBaselines is up to date.
110 */
111 private boolean checkedLabelBaselines;
112 /**
113 * Whether or not all the entries in the labeltable have the same
114 * baseline.
115 */
116 private boolean sameLabelBaselines;
117
118 /**
119 * Returns the shadow color.
120 * @return the shadow color
121 */
122 protected Color getShadowColor() {
123 return shadowColor;
124 }
125
126 /**
127 * Returns the highlight color.
128 * @return the highlight color
129 */
130 protected Color getHighlightColor() {
131 return highlightColor;
132 }
133
134 /**
135 * Returns the focus color.
136 * @return the focus color
137 */
138 protected Color getFocusColor() {
139 return focusColor;
140 }
141
142 /**
143 * Returns true if the user is dragging the slider.
144 *
145 * @return true if the user is dragging the slider
146 * @since 1.5
147 */
148 protected boolean isDragging() {
149 return isDragging;
150 }
151
152 /////////////////////////////////////////////////////////////////////////////
153 // ComponentUI Interface Implementation methods
154 /////////////////////////////////////////////////////////////////////////////
155 /**
156 * Creates a UI.
157 * @param b a component
158 * @return a UI
159 */
160 public static ComponentUI createUI(JComponent b) {
161 return new BasicSliderUI((JSlider)b);
162 }
163
164 /**
165 * Constructs a {@code BasicSliderUI}.
166 * @param b a slider
167 */
168 public BasicSliderUI(JSlider b) {
169 }
170
171 /**
172 * Installs a UI.
173 * @param c a component
174 */
175 public void installUI(JComponent c) {
176 slider = (JSlider) c;
177
178 checkedLabelBaselines = false;
179
180 slider.setEnabled(slider.isEnabled());
181 LookAndFeel.installProperty(slider, "opaque", Boolean.TRUE);
182
183 isDragging = false;
184 trackListener = createTrackListener( slider );
185 changeListener = createChangeListener( slider );
186 componentListener = createComponentListener( slider );
187 focusListener = createFocusListener( slider );
188 scrollListener = createScrollListener( slider );
189 propertyChangeListener = createPropertyChangeListener( slider );
190
191 installDefaults( slider );
192 installListeners( slider );
193 installKeyboardActions( slider );
194
195 scrollTimer = new Timer( 100, scrollListener );
196 scrollTimer.setInitialDelay( 300 );
197
198 insetCache = slider.getInsets();
199 leftToRightCache = BasicGraphicsUtils.isLeftToRight(slider);
200 focusRect = new Rectangle();
201 contentRect = new Rectangle();
202 labelRect = new Rectangle();
203 tickRect = new Rectangle();
204 trackRect = new Rectangle();
205 thumbRect = new Rectangle();
206 lastValue = slider.getValue();
207
208 calculateGeometry(); // This figures out where the labels, ticks, track, and thumb are.
209 }
210
211 /**
212 * Uninstalls a UI.
213 * @param c a component
214 */
215 public void uninstallUI(JComponent c) {
216 if ( c != slider )
217 throw new IllegalComponentStateException(
218 this + " was asked to deinstall() "
219 + c + " when it only knows about "
220 + slider + ".");
221
222 scrollTimer.stop();
223 scrollTimer = null;
224
225 uninstallDefaults(slider);
226 uninstallListeners( slider );
227 uninstallKeyboardActions(slider);
228
229 insetCache = null;
230 leftToRightCache = true;
231 focusRect = null;
232 contentRect = null;
233 labelRect = null;
234 tickRect = null;
235 trackRect = null;
236 thumbRect = null;
237 trackListener = null;
238 changeListener = null;
239 componentListener = null;
240 focusListener = null;
241 scrollListener = null;
242 propertyChangeListener = null;
243 slider = null;
244 }
245
246 /**
247 * Installs the defaults.
248 * @param slider a slider
249 */
250 protected void installDefaults( JSlider slider ) {
251 LookAndFeel.installBorder(slider, "Slider.border");
252 LookAndFeel.installColorsAndFont(slider, "Slider.background",
253 "Slider.foreground", "Slider.font");
254 highlightColor = UIManager.getColor("Slider.highlight");
255
256 shadowColor = UIManager.getColor("Slider.shadow");
257 focusColor = UIManager.getColor("Slider.focus");
258
259 focusInsets = (Insets)UIManager.get( "Slider.focusInsets" );
260 // use default if missing so that BasicSliderUI can be used in other
261 // LAFs like Nimbus
262 if (focusInsets == null) focusInsets = new InsetsUIResource(2,2,2,2);
263 }
264
265 /**
266 * Uninstalls the defaults.
267 * @param slider a slider
268 */
269 protected void uninstallDefaults(JSlider slider) {
270 LookAndFeel.uninstallBorder(slider);
271
272 focusInsets = null;
273 }
274
275 /**
276 * Creates a track listener.
277 * @return a track listener
278 * @param slider a slider
279 */
280 protected TrackListener createTrackListener(JSlider slider) {
281 return new TrackListener();
282 }
283
284 /**
285 * Creates a change listener.
286 * @return a change listener
287 * @param slider a slider
288 */
289 protected ChangeListener createChangeListener(JSlider slider) {
290 return getHandler();
291 }
292
293 /**
294 * Creates a composite listener.
295 * @return a composite listener
296 * @param slider a slider
297 */
298 protected ComponentListener createComponentListener(JSlider slider) {
299 return getHandler();
300 }
301
302 /**
303 * Creates a focus listener.
304 * @return a focus listener
305 * @param slider a slider
306 */
307 protected FocusListener createFocusListener(JSlider slider) {
308 return getHandler();
309 }
310
311 /**
312 * Creates a scroll listener.
313 * @return a scroll listener
314 * @param slider a slider
315 */
316 protected ScrollListener createScrollListener( JSlider slider ) {
317 return new ScrollListener();
318 }
319
320 /**
321 * Creates a property change listener.
322 * @return a property change listener
323 * @param slider a slider
324 */
325 protected PropertyChangeListener createPropertyChangeListener(
326 JSlider slider) {
327 return getHandler();
328 }
329
330 private Handler getHandler() {
331 if (handler == null) {
332 handler = new Handler();
333 }
334 return handler;
335 }
336
337 /**
338 * Installs listeners.
339 * @param slider a slider
340 */
341 protected void installListeners( JSlider slider ) {
342 slider.addMouseListener(trackListener);
343 slider.addMouseMotionListener(trackListener);
344 slider.addFocusListener(focusListener);
345 slider.addComponentListener(componentListener);
346 slider.addPropertyChangeListener( propertyChangeListener );
347 slider.getModel().addChangeListener(changeListener);
348 }
349
350 /**
351 * Uninstalls listeners.
352 * @param slider a slider
353 */
354 protected void uninstallListeners( JSlider slider ) {
355 slider.removeMouseListener(trackListener);
356 slider.removeMouseMotionListener(trackListener);
357 slider.removeFocusListener(focusListener);
358 slider.removeComponentListener(componentListener);
359 slider.removePropertyChangeListener( propertyChangeListener );
360 slider.getModel().removeChangeListener(changeListener);
361 handler = null;
362 }
363
364 /**
365 * Installs keyboard actions.
366 * @param slider a slider
367 */
368 protected void installKeyboardActions( JSlider slider ) {
369 InputMap km = getInputMap(JComponent.WHEN_FOCUSED, slider);
370 SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, km);
371 LazyActionMap.installLazyActionMap(slider, BasicSliderUI.class,
372 "Slider.actionMap");
373 }
374
375 InputMap getInputMap(int condition, JSlider slider) {
376 if (condition == JComponent.WHEN_FOCUSED) {
377 InputMap keyMap = (InputMap)DefaultLookup.get(slider, this,
378 "Slider.focusInputMap");
379 InputMap rtlKeyMap;
380
381 if (slider.getComponentOrientation().isLeftToRight() ||
382 ((rtlKeyMap = (InputMap)DefaultLookup.get(slider, this,
383 "Slider.focusInputMap.RightToLeft")) == null)) {
384 return keyMap;
385 } else {
386 rtlKeyMap.setParent(keyMap);
387 return rtlKeyMap;
388 }
389 }
390 return null;
391 }
392
393 /**
394 * Populates ComboBox's actions.
395 */
396 static void loadActionMap(LazyActionMap map) {
397 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
398 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
399 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
400 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
401 map.put(new Actions(Actions.MIN_SCROLL_INCREMENT));
402 map.put(new Actions(Actions.MAX_SCROLL_INCREMENT));
403 }
404
405 /**
406 * Uninstalls keyboard actions.
407 * @param slider a slider
408 */
409 protected void uninstallKeyboardActions( JSlider slider ) {
410 SwingUtilities.replaceUIActionMap(slider, null);
411 SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED,
412 null);
413 }
414
415
416 /**
417 * Returns the baseline.
418 *
419 * @throws NullPointerException {@inheritDoc}
420 * @throws IllegalArgumentException {@inheritDoc}
421 * @see javax.swing.JComponent#getBaseline(int, int)
422 * @since 1.6
423 */
424 public int getBaseline(JComponent c, int width, int height) {
425 super.getBaseline(c, width, height);
426 if (slider.getPaintLabels() && labelsHaveSameBaselines()) {
427 FontMetrics metrics = slider.getFontMetrics(slider.getFont());
428 Insets insets = slider.getInsets();
517 baseline = labelBaseline;
518 }
519 else if (baseline != labelBaseline) {
520 sameLabelBaselines = false;
521 break;
522 }
523 }
524 else {
525 sameLabelBaselines = false;
526 break;
527 }
528 }
529 }
530 else {
531 sameLabelBaselines = false;
532 }
533 }
534 return sameLabelBaselines;
535 }
536
537 /**
538 * Returns the preferred horizontal size.
539 * @return the preferred horizontal size
540 */
541 public Dimension getPreferredHorizontalSize() {
542 Dimension horizDim = (Dimension)DefaultLookup.get(slider,
543 this, "Slider.horizontalSize");
544 if (horizDim == null) {
545 horizDim = new Dimension(200, 21);
546 }
547 return horizDim;
548 }
549
550 /**
551 * Returns the preferred vertical size.
552 * @return the preferred vertical size
553 */
554 public Dimension getPreferredVerticalSize() {
555 Dimension vertDim = (Dimension)DefaultLookup.get(slider,
556 this, "Slider.verticalSize");
557 if (vertDim == null) {
558 vertDim = new Dimension(21, 200);
559 }
560 return vertDim;
561 }
562
563 /**
564 * Returns the minimum horizontal size.
565 * @return the minimum horizontal size
566 */
567 public Dimension getMinimumHorizontalSize() {
568 Dimension minHorizDim = (Dimension)DefaultLookup.get(slider,
569 this, "Slider.minimumHorizontalSize");
570 if (minHorizDim == null) {
571 minHorizDim = new Dimension(36, 21);
572 }
573 return minHorizDim;
574 }
575
576 /**
577 * Returns the minimum vertical size.
578 * @return the minimum vertical size
579 */
580 public Dimension getMinimumVerticalSize() {
581 Dimension minVertDim = (Dimension)DefaultLookup.get(slider,
582 this, "Slider.minimumVerticalSize");
583 if (minVertDim == null) {
584 minVertDim = new Dimension(21, 36);
585 }
586 return minVertDim;
587 }
588
589 /**
590 * Returns the preferred size.
591 * @param c a component
592 * @return the preferred size
593 */
594 public Dimension getPreferredSize(JComponent c) {
595 recalculateIfInsetsChanged();
596 Dimension d;
597 if ( slider.getOrientation() == JSlider.VERTICAL ) {
598 d = new Dimension(getPreferredVerticalSize());
599 d.width = insetCache.left + insetCache.right;
600 d.width += focusInsets.left + focusInsets.right;
601 d.width += trackRect.width + tickRect.width + labelRect.width;
602 }
603 else {
604 d = new Dimension(getPreferredHorizontalSize());
605 d.height = insetCache.top + insetCache.bottom;
606 d.height += focusInsets.top + focusInsets.bottom;
607 d.height += trackRect.height + tickRect.height + labelRect.height;
608 }
609
610 return d;
611 }
612
613 /**
614 * Returns the minimum size.
615 * @param c a component
616 * @return the minimum size
617 */
618 public Dimension getMinimumSize(JComponent c) {
619 recalculateIfInsetsChanged();
620 Dimension d;
621
622 if ( slider.getOrientation() == JSlider.VERTICAL ) {
623 d = new Dimension(getMinimumVerticalSize());
624 d.width = insetCache.left + insetCache.right;
625 d.width += focusInsets.left + focusInsets.right;
626 d.width += trackRect.width + tickRect.width + labelRect.width;
627 }
628 else {
629 d = new Dimension(getMinimumHorizontalSize());
630 d.height = insetCache.top + insetCache.bottom;
631 d.height += focusInsets.top + focusInsets.bottom;
632 d.height += trackRect.height + tickRect.height + labelRect.height;
633 }
634
635 return d;
636 }
637
638 /**
639 * Returns the maximum size.
640 * @param c a component
641 * @return the maximum size
642 */
643 public Dimension getMaximumSize(JComponent c) {
644 Dimension d = getPreferredSize(c);
645 if ( slider.getOrientation() == JSlider.VERTICAL ) {
646 d.height = Short.MAX_VALUE;
647 }
648 else {
649 d.width = Short.MAX_VALUE;
650 }
651
652 return d;
653 }
654
655 /**
656 * Calculates the geometry.
657 */
658 protected void calculateGeometry() {
659 calculateFocusRect();
660 calculateContentRect();
661 calculateThumbSize();
662 calculateTrackBuffer();
663 calculateTrackRect();
664 calculateTickRect();
665 calculateLabelRect();
666 calculateThumbLocation();
667 }
668
669 /**
670 * Calculates the focus rectangle.
671 */
672 protected void calculateFocusRect() {
673 focusRect.x = insetCache.left;
674 focusRect.y = insetCache.top;
675 focusRect.width = slider.getWidth() - (insetCache.left + insetCache.right);
676 focusRect.height = slider.getHeight() - (insetCache.top + insetCache.bottom);
677 }
678
679 /**
680 * Calculates the thumb size rectangle.
681 */
682 protected void calculateThumbSize() {
683 Dimension size = getThumbSize();
684 thumbRect.setSize( size.width, size.height );
685 }
686
687 /**
688 * Calculates the content rectangle.
689 */
690 protected void calculateContentRect() {
691 contentRect.x = focusRect.x + focusInsets.left;
692 contentRect.y = focusRect.y + focusInsets.top;
693 contentRect.width = focusRect.width - (focusInsets.left + focusInsets.right);
694 contentRect.height = focusRect.height - (focusInsets.top + focusInsets.bottom);
695 }
696
697 private int getTickSpacing() {
698 int majorTickSpacing = slider.getMajorTickSpacing();
699 int minorTickSpacing = slider.getMinorTickSpacing();
700
701 int result;
702
703 if (minorTickSpacing > 0) {
704 result = minorTickSpacing;
705 } else if (majorTickSpacing > 0) {
706 result = majorTickSpacing;
707 } else {
708 result = 0;
709 }
710
711 return result;
712 }
713
714 /**
715 * Calculates the thumb location.
716 */
717 protected void calculateThumbLocation() {
718 if ( slider.getSnapToTicks() ) {
719 int sliderValue = slider.getValue();
720 int snappedValue = sliderValue;
721 int tickSpacing = getTickSpacing();
722
723 if ( tickSpacing != 0 ) {
724 // If it's not on a tick, change the value
725 if ( (sliderValue - slider.getMinimum()) % tickSpacing != 0 ) {
726 float temp = (float)(sliderValue - slider.getMinimum()) / (float)tickSpacing;
727 int whichTick = Math.round( temp );
728
729 // This is the fix for the bug #6401380
730 if (temp - (int)temp == .5 && sliderValue < lastValue) {
731 whichTick --;
732 }
733 snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
734 }
735
736 if( snappedValue != sliderValue ) {
737 slider.setValue( snappedValue );
738 }
739 }
740 }
741
742 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
743 int valuePosition = xPositionForValue(slider.getValue());
744
745 thumbRect.x = valuePosition - (thumbRect.width / 2);
746 thumbRect.y = trackRect.y;
747 }
748 else {
749 int valuePosition = yPositionForValue(slider.getValue());
750
751 thumbRect.x = trackRect.x;
752 thumbRect.y = valuePosition - (thumbRect.height / 2);
753 }
754 }
755
756 /**
757 * Calculates the track buffer.
758 */
759 protected void calculateTrackBuffer() {
760 if ( slider.getPaintLabels() && slider.getLabelTable() != null ) {
761 Component highLabel = getHighestValueLabel();
762 Component lowLabel = getLowestValueLabel();
763
764 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
765 trackBuffer = Math.max( highLabel.getBounds().width, lowLabel.getBounds().width ) / 2;
766 trackBuffer = Math.max( trackBuffer, thumbRect.width / 2 );
767 }
768 else {
769 trackBuffer = Math.max( highLabel.getBounds().height, lowLabel.getBounds().height ) / 2;
770 trackBuffer = Math.max( trackBuffer, thumbRect.height / 2 );
771 }
772 }
773 else {
774 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
775 trackBuffer = thumbRect.width / 2;
776 }
777 else {
778 trackBuffer = thumbRect.height / 2;
779 }
780 }
781 }
782
783 /**
784 * Calculates the track rectangle.
785 */
786 protected void calculateTrackRect() {
787 int centerSpacing; // used to center sliders added using BorderLayout.CENTER (bug 4275631)
788 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
789 centerSpacing = thumbRect.height;
790 if ( slider.getPaintTicks() ) centerSpacing += getTickLength();
791 if ( slider.getPaintLabels() ) centerSpacing += getHeightOfTallestLabel();
792 trackRect.x = contentRect.x + trackBuffer;
793 trackRect.y = contentRect.y + (contentRect.height - centerSpacing - 1)/2;
794 trackRect.width = contentRect.width - (trackBuffer * 2);
795 trackRect.height = thumbRect.height;
796 }
797 else {
798 centerSpacing = thumbRect.width;
799 if (BasicGraphicsUtils.isLeftToRight(slider)) {
800 if ( slider.getPaintTicks() ) centerSpacing += getTickLength();
801 if ( slider.getPaintLabels() ) centerSpacing += getWidthOfWidestLabel();
802 } else {
803 if ( slider.getPaintTicks() ) centerSpacing -= getTickLength();
804 if ( slider.getPaintLabels() ) centerSpacing -= getWidthOfWidestLabel();
805 }
809 trackRect.height = contentRect.height - (trackBuffer * 2);
810 }
811
812 }
813
814 /**
815 * Gets the height of the tick area for horizontal sliders and the width of
816 * the tick area for vertical sliders. BasicSliderUI uses the returned value
817 * to determine the tick area rectangle. If you want to give your ticks some
818 * room, make this larger than you need and paint your ticks away from the
819 * sides in paintTicks().
820 *
821 * @return an integer representing the height of the tick area for
822 * horizontal sliders, and the width of the tick area for the vertical
823 * sliders
824 */
825 protected int getTickLength() {
826 return 8;
827 }
828
829 /**
830 * Calculates the tick rectangle.
831 */
832 protected void calculateTickRect() {
833 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
834 tickRect.x = trackRect.x;
835 tickRect.y = trackRect.y + trackRect.height;
836 tickRect.width = trackRect.width;
837 tickRect.height = (slider.getPaintTicks()) ? getTickLength() : 0;
838 }
839 else {
840 tickRect.width = (slider.getPaintTicks()) ? getTickLength() : 0;
841 if(BasicGraphicsUtils.isLeftToRight(slider)) {
842 tickRect.x = trackRect.x + trackRect.width;
843 }
844 else {
845 tickRect.x = trackRect.x - tickRect.width;
846 }
847 tickRect.y = trackRect.y;
848 tickRect.height = trackRect.height;
849 }
850 }
851
852 /**
853 * Calculates the label rectangle.
854 */
855 protected void calculateLabelRect() {
856 if ( slider.getPaintLabels() ) {
857 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
858 labelRect.x = tickRect.x - trackBuffer;
859 labelRect.y = tickRect.y + tickRect.height;
860 labelRect.width = tickRect.width + (trackBuffer * 2);
861 labelRect.height = getHeightOfTallestLabel();
862 }
863 else {
864 if(BasicGraphicsUtils.isLeftToRight(slider)) {
865 labelRect.x = tickRect.x + tickRect.width;
866 labelRect.width = getWidthOfWidestLabel();
867 }
868 else {
869 labelRect.width = getWidthOfWidestLabel();
870 labelRect.x = tickRect.x - labelRect.width;
871 }
872 labelRect.y = tickRect.y - trackBuffer;
873 labelRect.height = tickRect.height + (trackBuffer * 2);
874 }
877 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
878 labelRect.x = tickRect.x;
879 labelRect.y = tickRect.y + tickRect.height;
880 labelRect.width = tickRect.width;
881 labelRect.height = 0;
882 }
883 else {
884 if(BasicGraphicsUtils.isLeftToRight(slider)) {
885 labelRect.x = tickRect.x + tickRect.width;
886 }
887 else {
888 labelRect.x = tickRect.x;
889 }
890 labelRect.y = tickRect.y;
891 labelRect.width = 0;
892 labelRect.height = tickRect.height;
893 }
894 }
895 }
896
897 /**
898 * Returns the thumb size.
899 * @return the thumb size
900 */
901 protected Dimension getThumbSize() {
902 Dimension size = new Dimension();
903
904 if ( slider.getOrientation() == JSlider.VERTICAL ) {
905 size.width = 20;
906 size.height = 11;
907 }
908 else {
909 size.width = 11;
910 size.height = 20;
911 }
912
913 return size;
914 }
915
916 /**
917 * A property change handler.
918 */
919 public class PropertyChangeHandler implements PropertyChangeListener {
920 // NOTE: This class exists only for backward compatibility. All
921 // its functionality has been moved into Handler. If you need to add
922 // new functionality add it to the Handler, but make sure this
923 // class calls into the Handler.
924 /** {@inheritDoc} */
925 public void propertyChange( PropertyChangeEvent e ) {
926 getHandler().propertyChange(e);
927 }
928 }
929
930 /**
931 * Returns the width of the widest label.
932 * @return the width of the widest label
933 */
934 protected int getWidthOfWidestLabel() {
935 @SuppressWarnings("rawtypes")
936 Dictionary dictionary = slider.getLabelTable();
937 int widest = 0;
938 if ( dictionary != null ) {
939 Enumeration<?> keys = dictionary.keys();
940 while ( keys.hasMoreElements() ) {
941 JComponent label = (JComponent) dictionary.get(keys.nextElement());
942 widest = Math.max( label.getPreferredSize().width, widest );
943 }
944 }
945 return widest;
946 }
947
948 /**
949 * Returns the height of the tallest label.
950 * @return the height of the tallest label
951 */
952 protected int getHeightOfTallestLabel() {
953 @SuppressWarnings("rawtypes")
954 Dictionary dictionary = slider.getLabelTable();
955 int tallest = 0;
956 if ( dictionary != null ) {
957 Enumeration<?> keys = dictionary.keys();
958 while ( keys.hasMoreElements() ) {
959 JComponent label = (JComponent) dictionary.get(keys.nextElement());
960 tallest = Math.max( label.getPreferredSize().height, tallest );
961 }
962 }
963 return tallest;
964 }
965
966 /**
967 * Returns the width of the highest value label.
968 * @return the width of the highest value label
969 */
970 protected int getWidthOfHighValueLabel() {
971 Component label = getHighestValueLabel();
972 int width = 0;
973
974 if ( label != null ) {
975 width = label.getPreferredSize().width;
976 }
977
978 return width;
979 }
980
981 /**
982 * Returns the width of the lowest value label.
983 * @return the width of the lowest value label
984 */
985 protected int getWidthOfLowValueLabel() {
986 Component label = getLowestValueLabel();
987 int width = 0;
988
989 if ( label != null ) {
990 width = label.getPreferredSize().width;
991 }
992
993 return width;
994 }
995
996 /**
997 * Returns the height of the highest value label.
998 * @return the height of the highest value label
999 */
1000 protected int getHeightOfHighValueLabel() {
1001 Component label = getHighestValueLabel();
1002 int height = 0;
1003
1004 if ( label != null ) {
1005 height = label.getPreferredSize().height;
1006 }
1007
1008 return height;
1009 }
1010
1011 /**
1012 * Returns the height of the lowest value label.
1013 * @return the height of the lowest value label
1014 */
1015 protected int getHeightOfLowValueLabel() {
1016 Component label = getLowestValueLabel();
1017 int height = 0;
1018
1019 if ( label != null ) {
1020 height = label.getPreferredSize().height;
1021 }
1022
1023 return height;
1024 }
1025
1026 /**
1027 * Draws inverted.
1028 * @return the inverted-ness
1029 */
1030 protected boolean drawInverted() {
1031 if (slider.getOrientation()==JSlider.HORIZONTAL) {
1032 if(BasicGraphicsUtils.isLeftToRight(slider)) {
1033 return slider.getInverted();
1034 } else {
1035 return !slider.getInverted();
1036 }
1037 } else {
1038 return slider.getInverted();
1039 }
1040 }
1041
1042 /**
1043 * Returns the biggest value that has an entry in the label table.
1044 *
1045 * @return biggest value that has an entry in the label table, or
1046 * null.
1047 * @since 1.6
1048 */
1049 protected Integer getHighestValue() {
1140 if ( !clip.intersects(trackRect) && slider.getPaintTrack())
1141 calculateGeometry();
1142
1143 if ( slider.getPaintTrack() && clip.intersects( trackRect ) ) {
1144 paintTrack( g );
1145 }
1146 if ( slider.getPaintTicks() && clip.intersects( tickRect ) ) {
1147 paintTicks( g );
1148 }
1149 if ( slider.getPaintLabels() && clip.intersects( labelRect ) ) {
1150 paintLabels( g );
1151 }
1152 if ( slider.hasFocus() && clip.intersects( focusRect ) ) {
1153 paintFocus( g );
1154 }
1155 if ( clip.intersects( thumbRect ) ) {
1156 paintThumb( g );
1157 }
1158 }
1159
1160 /**
1161 * Recalculates if the insets have changed.
1162 */
1163 protected void recalculateIfInsetsChanged() {
1164 Insets newInsets = slider.getInsets();
1165 if ( !newInsets.equals( insetCache ) ) {
1166 insetCache = newInsets;
1167 calculateGeometry();
1168 }
1169 }
1170
1171 /**
1172 * Recalculates if the orientation has changed.
1173 */
1174 protected void recalculateIfOrientationChanged() {
1175 boolean ltr = BasicGraphicsUtils.isLeftToRight(slider);
1176 if ( ltr!=leftToRightCache ) {
1177 leftToRightCache = ltr;
1178 calculateGeometry();
1179 }
1180 }
1181
1182 /**
1183 * Paints focus.
1184 * @param g the graphics
1185 */
1186 public void paintFocus(Graphics g) {
1187 g.setColor( getFocusColor() );
1188
1189 BasicGraphicsUtils.drawDashedRect( g, focusRect.x, focusRect.y,
1190 focusRect.width, focusRect.height );
1191 }
1192
1193 /**
1194 * Paints track.
1195 * @param g the graphics
1196 */
1197 public void paintTrack(Graphics g) {
1198
1199 Rectangle trackBounds = trackRect;
1200
1201 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
1202 int cy = (trackBounds.height / 2) - 2;
1203 int cw = trackBounds.width;
1204
1205 g.translate(trackBounds.x, trackBounds.y + cy);
1206
1207 g.setColor(getShadowColor());
1208 g.drawLine(0, 0, cw - 1, 0);
1209 g.drawLine(0, 1, 0, 2);
1210 g.setColor(getHighlightColor());
1211 g.drawLine(0, 3, cw, 3);
1212 g.drawLine(cw, 0, cw, 3);
1213 g.setColor(Color.black);
1214 g.drawLine(1, 1, cw-2, 1);
1215
1216 g.translate(-trackBounds.x, -(trackBounds.y + cy));
1217 }
1218 else {
1219 int cx = (trackBounds.width / 2) - 2;
1220 int ch = trackBounds.height;
1221
1222 g.translate(trackBounds.x + cx, trackBounds.y);
1223
1224 g.setColor(getShadowColor());
1225 g.drawLine(0, 0, 0, ch - 1);
1226 g.drawLine(1, 0, 2, 0);
1227 g.setColor(getHighlightColor());
1228 g.drawLine(3, 0, 3, ch);
1229 g.drawLine(0, ch, 3, ch);
1230 g.setColor(Color.black);
1231 g.drawLine(1, 1, 1, ch-2);
1232
1233 g.translate(-(trackBounds.x + cx), -trackBounds.y);
1234 }
1235 }
1236
1237 /**
1238 * Paints ticks.
1239 * @param g the graphics
1240 */
1241 public void paintTicks(Graphics g) {
1242 Rectangle tickBounds = tickRect;
1243
1244 g.setColor(DefaultLookup.getColor(slider, this, "Slider.tickColor", Color.black));
1245
1246 if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
1247 g.translate(0, tickBounds.y);
1248
1249 if (slider.getMinorTickSpacing() > 0) {
1250 int value = slider.getMinimum();
1251
1252 while ( value <= slider.getMaximum() ) {
1253 int xPos = xPositionForValue(value);
1254 paintMinorTickForHorizSlider( g, tickBounds, xPos );
1255
1256 // Overflow checking
1257 if (Integer.MAX_VALUE - slider.getMinorTickSpacing() < value) {
1258 break;
1259 }
1260
1318 while (value <= slider.getMaximum()) {
1319 int yPos = yPositionForValue(value);
1320 paintMajorTickForVertSlider( g, tickBounds, yPos );
1321
1322 // Overflow checking
1323 if (Integer.MAX_VALUE - slider.getMajorTickSpacing() < value) {
1324 break;
1325 }
1326
1327 value += slider.getMajorTickSpacing();
1328 }
1329
1330 if(!BasicGraphicsUtils.isLeftToRight(slider)) {
1331 g.translate(-2, 0);
1332 }
1333 }
1334 g.translate(-tickBounds.x, 0);
1335 }
1336 }
1337
1338 /**
1339 * Paints minor tick for horizontal slider.
1340 * @param g the graphics
1341 * @param tickBounds the tick bounds
1342 * @param x the x coordinate
1343 */
1344 protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
1345 g.drawLine( x, 0, x, tickBounds.height / 2 - 1 );
1346 }
1347
1348 /**
1349 * Paints major tick for horizontal slider.
1350 * @param g the graphics
1351 * @param tickBounds the tick bounds
1352 * @param x the x coordinate
1353 */
1354 protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
1355 g.drawLine( x, 0, x, tickBounds.height - 2 );
1356 }
1357
1358 /**
1359 * Paints minor tick for vertical slider.
1360 * @param g the graphics
1361 * @param tickBounds the tick bounds
1362 * @param y the y coordinate
1363 */
1364 protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
1365 g.drawLine( 0, y, tickBounds.width / 2 - 1, y );
1366 }
1367
1368 /**
1369 * Paints major tick for vertical slider.
1370 * @param g the graphics
1371 * @param tickBounds the tick bounds
1372 * @param y the y coordinate
1373 */
1374 protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
1375 g.drawLine( 0, y, tickBounds.width - 2, y );
1376 }
1377
1378 /**
1379 * Paints the labels.
1380 * @param g the graphics
1381 */
1382 public void paintLabels( Graphics g ) {
1383 Rectangle labelBounds = labelRect;
1384
1385 @SuppressWarnings("rawtypes")
1386 Dictionary dictionary = slider.getLabelTable();
1387 if ( dictionary != null ) {
1388 Enumeration<?> keys = dictionary.keys();
1389 int minValue = slider.getMinimum();
1390 int maxValue = slider.getMaximum();
1391 boolean enabled = slider.isEnabled();
1392 while ( keys.hasMoreElements() ) {
1393 Integer key = (Integer)keys.nextElement();
1394 int value = key.intValue();
1395 if (value >= minValue && value <= maxValue) {
1396 JComponent label = (JComponent) dictionary.get(key);
1397 label.setEnabled(enabled);
1398
1399 if (label instanceof JLabel) {
1400 Icon icon = label.isEnabled() ? ((JLabel) label).getIcon() : ((JLabel) label).getDisabledIcon();
1401
1448
1449 /**
1450 * Called for every label in the label table. Used to draw the labels for
1451 * vertical sliders. The graphics have been translated to labelRect.x
1452 * already.
1453 *
1454 * @param g the graphics context in which to paint
1455 * @param value the value of the slider
1456 * @param label the component label in the label table that needs to be
1457 * painted
1458 * @see JSlider#setLabelTable
1459 */
1460 protected void paintVerticalLabel( Graphics g, int value, Component label ) {
1461 int labelCenter = yPositionForValue( value );
1462 int labelTop = labelCenter - (label.getPreferredSize().height / 2);
1463 g.translate( 0, labelTop );
1464 label.paint( g );
1465 g.translate( 0, -labelTop );
1466 }
1467
1468 /**
1469 * Paints the thumb.
1470 * @param g the graphics
1471 */
1472 public void paintThumb(Graphics g) {
1473 Rectangle knobBounds = thumbRect;
1474 int w = knobBounds.width;
1475 int h = knobBounds.height;
1476
1477 g.translate(knobBounds.x, knobBounds.y);
1478
1479 if ( slider.isEnabled() ) {
1480 g.setColor(slider.getBackground());
1481 }
1482 else {
1483 g.setColor(slider.getBackground().darker());
1484 }
1485
1486 Boolean paintThumbArrowShape =
1487 (Boolean)slider.getClientProperty("Slider.paintThumbArrowShape");
1488
1489 if ((!slider.getPaintTicks() && paintThumbArrowShape == null) ||
1490 paintThumbArrowShape == Boolean.FALSE) {
1491
1560 g.setColor(highlightColor);
1561 g.drawLine(cw-1, 0, w-2, 0); // top
1562 g.drawLine(0, cw, cw, 0); // top slant
1563
1564 g.setColor(Color.black);
1565 g.drawLine(0, h-1-cw, cw, h-1 ); // bottom slant
1566 g.drawLine(cw, h-1, w-1, h-1); // bottom
1567
1568 g.setColor(shadowColor);
1569 g.drawLine(cw, h-2, w-2, h-2 ); // bottom
1570 g.drawLine(w-1, 1, w-1, h-2 ); // right
1571 }
1572 }
1573
1574 g.translate(-knobBounds.x, -knobBounds.y);
1575 }
1576
1577 // Used exclusively by setThumbLocation()
1578 private static Rectangle unionRect = new Rectangle();
1579
1580 /**
1581 * Sets the thumb location.
1582 * @param x the x coordinate
1583 * @param y the y coordinate
1584 */
1585 public void setThumbLocation(int x, int y) {
1586 unionRect.setBounds( thumbRect );
1587
1588 thumbRect.setLocation( x, y );
1589
1590 SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, unionRect );
1591 slider.repaint( unionRect.x, unionRect.y, unionRect.width, unionRect.height );
1592 }
1593
1594 /**
1595 * Scrolls by block.
1596 * @param direction the direction
1597 */
1598 public void scrollByBlock(int direction) {
1599 synchronized(slider) {
1600 int blockIncrement =
1601 (slider.getMaximum() - slider.getMinimum()) / 10;
1602 if (blockIncrement == 0) {
1603 blockIncrement = 1;
1604 }
1605
1606 if (slider.getSnapToTicks()) {
1607 int tickSpacing = getTickSpacing();
1608
1609 if (blockIncrement < tickSpacing) {
1610 blockIncrement = tickSpacing;
1611 }
1612 }
1613
1614 int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
1615 slider.setValue(slider.getValue() + delta);
1616 }
1617 }
1618
1619 /**
1620 * Scrolls by unit.
1621 * @param direction the direction
1622 */
1623 public void scrollByUnit(int direction) {
1624 synchronized(slider) {
1625 int delta = ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
1626
1627 if (slider.getSnapToTicks()) {
1628 delta *= getTickSpacing();
1629 }
1630
1631 slider.setValue(slider.getValue() + delta);
1632 }
1633 }
1634
1635 /**
1636 * This function is called when a mousePressed was detected in the track,
1637 * not in the thumb. The default behavior is to scroll by block. You can
1638 * override this method to stop it from scrolling or to add additional
1639 * behavior.
1640 *
1641 * @param dir the direction and number of blocks to scroll
1642 */
1643 protected void scrollDueToClickInTrack( int dir ) {
1644 scrollByBlock( dir );
1645 }
1646
1647 /**
1648 * Returns the x position for a value.
1649 * @param value the value
1650 * @return the x position for a value
1651 */
1652 protected int xPositionForValue( int value ) {
1653 int min = slider.getMinimum();
1654 int max = slider.getMaximum();
1655 int trackLength = trackRect.width;
1656 double valueRange = (double)max - (double)min;
1657 double pixelsPerValue = (double)trackLength / valueRange;
1658 int trackLeft = trackRect.x;
1659 int trackRight = trackRect.x + (trackRect.width - 1);
1660 int xPosition;
1661
1662 if ( !drawInverted() ) {
1663 xPosition = trackLeft;
1664 xPosition += Math.round( pixelsPerValue * ((double)value - min) );
1665 }
1666 else {
1667 xPosition = trackRight;
1668 xPosition -= Math.round( pixelsPerValue * ((double)value - min) );
1669 }
1670
1671 xPosition = Math.max( trackLeft, xPosition );
1672 xPosition = Math.min( trackRight, xPosition );
1673
1674 return xPosition;
1675 }
1676
1677 /**
1678 * Returns the y position for a value.
1679 * @param value the value
1680 * @return the y position for a value
1681 */
1682 protected int yPositionForValue( int value ) {
1683 return yPositionForValue(value, trackRect.y, trackRect.height);
1684 }
1685
1686 /**
1687 * Returns the y location for the specified value. No checking is
1688 * done on the arguments. In particular if <code>trackHeight</code> is
1689 * negative undefined results may occur.
1690 *
1691 * @param value the slider value to get the location for
1692 * @param trackY y-origin of the track
1693 * @param trackHeight the height of the track
1694 * @return the y location for the specified value of the slider
1695 * @since 1.6
1696 */
1697 protected int yPositionForValue(int value, int trackY, int trackHeight) {
1698 int min = slider.getMinimum();
1699 int max = slider.getMaximum();
1700 double valueRange = (double)max - (double)min;
1701 double pixelsPerValue = (double)trackHeight / valueRange;
1858 public class ChangeHandler implements ChangeListener {
1859 // NOTE: This class exists only for backward compatibility. All
1860 // its functionality has been moved into Handler. If you need to add
1861 // new functionality add it to the Handler, but make sure this
1862 // class calls into the Handler.
1863 public void stateChanged(ChangeEvent e) {
1864 getHandler().stateChanged(e);
1865 }
1866 }
1867
1868 /////////////////////////////////////////////////////////////////////////
1869 /// Track Listener Class
1870 /////////////////////////////////////////////////////////////////////////
1871 /**
1872 * Track mouse movements.
1873 *
1874 * This class should be treated as a "protected" inner class.
1875 * Instantiate it only within subclasses of <code>Foo</code>.
1876 */
1877 public class TrackListener extends MouseInputAdapter {
1878 /** The offset */
1879 protected transient int offset;
1880 /** Current mouse x. */
1881 protected transient int currentMouseX;
1882 /** Current mouse y. */
1883 protected transient int currentMouseY;
1884
1885 /**
1886 * {@inheritDoc}
1887 */
1888 public void mouseReleased(MouseEvent e) {
1889 if (!slider.isEnabled()) {
1890 return;
1891 }
1892
1893 offset = 0;
1894 scrollTimer.stop();
1895
1896 isDragging = false;
1897 slider.setValueIsAdjusting(false);
1898 slider.repaint();
1899 }
1900
1901 /**
1902 * If the mouse is pressed above the "thumb" component
1903 * then reduce the scrollbars value by one page ("page up"),
1904 * otherwise increase it by one page. If there is no
1905 * thumb then page up if the mouse is in the upper half
1906 * of the track.
1907 */
1996 NEGATIVE_SCROLL : POSITIVE_SCROLL;
1997 }
1998 else {
1999 direction = (currentMouseX < thumbX) ?
2000 POSITIVE_SCROLL : NEGATIVE_SCROLL;
2001 }
2002 }
2003 break;
2004 }
2005
2006 if (shouldScroll(direction)) {
2007 scrollDueToClickInTrack(direction);
2008 }
2009 if (shouldScroll(direction)) {
2010 scrollTimer.stop();
2011 scrollListener.setDirection(direction);
2012 scrollTimer.start();
2013 }
2014 }
2015
2016 /**
2017 * Returns if scrolling should occur
2018 * @param direction the direction.
2019 * @return if scrolling should occur
2020 */
2021 public boolean shouldScroll(int direction) {
2022 Rectangle r = thumbRect;
2023 if (slider.getOrientation() == JSlider.VERTICAL) {
2024 if (drawInverted() ? direction < 0 : direction > 0) {
2025 if (r.y <= currentMouseY) {
2026 return false;
2027 }
2028 }
2029 else if (r.y + r.height >= currentMouseY) {
2030 return false;
2031 }
2032 }
2033 else {
2034 if (drawInverted() ? direction < 0 : direction > 0) {
2035 if (r.x + r.width >= currentMouseX) {
2036 return false;
2037 }
2038 }
2039 else if (r.x <= currentMouseX) {
2040 return false;
2104 int hMax = xPositionForValue(slider.getMaximum() -
2105 slider.getExtent());
2106
2107 if (drawInverted()) {
2108 trackLeft = hMax;
2109 }
2110 else {
2111 trackRight = hMax;
2112 }
2113 thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
2114 thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
2115
2116 setThumbLocation(thumbLeft, thumbRect.y);
2117
2118 thumbMiddle = thumbLeft + halfThumbWidth;
2119 slider.setValue(valueForXPosition(thumbMiddle));
2120 break;
2121 }
2122 }
2123
2124 /** {@inheritDoc} */
2125 public void mouseMoved(MouseEvent e) { }
2126 }
2127
2128 /**
2129 * Scroll-event listener.
2130 *
2131 * This class should be treated as a "protected" inner class.
2132 * Instantiate it only within subclasses of <code>Foo</code>.
2133 */
2134 public class ScrollListener implements ActionListener {
2135 // changed this class to public to avoid bogus IllegalAccessException
2136 // bug in InternetExplorer browser. It was protected. Work around
2137 // for 4109432
2138 int direction = POSITIVE_SCROLL;
2139 boolean useBlockIncrement;
2140
2141 /**
2142 * Constructs a {@code ScrollListener}
2143 */
2144 public ScrollListener() {
2145 direction = POSITIVE_SCROLL;
2146 useBlockIncrement = true;
2147 }
2148
2149 /**
2150 * Constructs a {@code ScrollListener}
2151 * @param dir the direction
2152 * @param block whether or not to scroll by block
2153 */
2154 public ScrollListener(int dir, boolean block) {
2155 direction = dir;
2156 useBlockIncrement = block;
2157 }
2158
2159 /**
2160 * Sets the direction.
2161 * @param direction the new direction
2162 */
2163 public void setDirection(int direction) {
2164 this.direction = direction;
2165 }
2166
2167 /**
2168 * Sets scrolling by block
2169 * @param block the new scroll by block value
2170 */
2171 public void setScrollByBlock(boolean block) {
2172 this.useBlockIncrement = block;
2173 }
2174
2175 /** {@inheritDoc} */
2176 public void actionPerformed(ActionEvent e) {
2177 if (useBlockIncrement) {
2178 scrollByBlock(direction);
2179 }
2180 else {
2181 scrollByUnit(direction);
2182 }
2183 if (!trackListener.shouldScroll(direction)) {
2184 ((Timer)e.getSource()).stop();
2185 }
2186 }
2187 }
2188
2189 /**
2190 * Listener for resizing events.
2191 * <p>
2192 * This class should be treated as a "protected" inner class.
2193 * Instantiate it only within subclasses of <code>Foo</code>.
2194 */
2195 public class ComponentHandler extends ComponentAdapter {
2228 * combination of an <code>ActionMap</code>, to contain the action,
2229 * and an <code>InputMap</code> to contain the mapping from KeyStroke
2230 * to action description. The InputMap is usually described in the
2231 * LookAndFeel tables.
2232 * <p>
2233 * Please refer to the key bindings specification for further details.
2234 * <p>
2235 * This class should be treated as a "protected" inner class.
2236 * Instantiate it only within subclasses of <code>Foo</code>.
2237 */
2238 @SuppressWarnings("serial") // Superclass is not serializable across versions
2239 public class ActionScroller extends AbstractAction {
2240 // NOTE: This class exists only for backward compatibility. All
2241 // its functionality has been moved into Actions. If you need to add
2242 // new functionality add it to the Actions, but make sure this
2243 // class calls into the Actions.
2244 int dir;
2245 boolean block;
2246 JSlider slider;
2247
2248 /**
2249 * Constructs an {@code ActionScroller}.
2250 * @param slider a slider
2251 * @param dir the direction
2252 * @param block block scrolling or not
2253 */
2254 public ActionScroller( JSlider slider, int dir, boolean block) {
2255 this.dir = dir;
2256 this.block = block;
2257 this.slider = slider;
2258 }
2259
2260 /** {@inheritDoc} */
2261 public void actionPerformed(ActionEvent e) {
2262 SHARED_ACTION.scroll(slider, BasicSliderUI.this, dir, block);
2263 }
2264
2265 /** {@inheritDoc} */
2266 public boolean isEnabled() {
2267 boolean b = true;
2268 if (slider != null) {
2269 b = slider.isEnabled();
2270 }
2271 return b;
2272 }
2273
2274 }
2275
2276
2277 /**
2278 * A static version of the above.
2279 */
2280 @SuppressWarnings("serial") // Superclass is not serializable across versions
2281 static class SharedActionScroller extends AbstractAction {
2282 // NOTE: This class exists only for backward compatibility. All
2283 // its functionality has been moved into Actions. If you need to add
2284 // new functionality add it to the Actions, but make sure this
2285 // class calls into the Actions.
|