0001 /*
0002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.swing.plaf.basic;
0027
0028 import java.awt.Component;
0029 import java.awt.Container;
0030 import java.awt.Adjustable;
0031 import java.awt.event.*;
0032 import java.awt.FontMetrics;
0033 import java.awt.Graphics;
0034 import java.awt.Dimension;
0035 import java.awt.Rectangle;
0036 import java.awt.Point;
0037 import java.awt.Insets;
0038 import java.awt.Color;
0039 import java.awt.IllegalComponentStateException;
0040 import java.awt.Polygon;
0041 import java.beans.*;
0042 import java.util.Dictionary;
0043 import java.util.Enumeration;
0044
0045 import javax.swing.border.AbstractBorder;
0046
0047 import javax.swing.*;
0048 import javax.swing.event.*;
0049 import javax.swing.plaf.*;
0050 import sun.swing.DefaultLookup;
0051 import sun.swing.UIAction;
0052
0053 /**
0054 * A Basic L&F implementation of SliderUI.
0055 *
0056 * @version 1.113 05/05/07
0057 * @author Tom Santos
0058 */
0059 public class BasicSliderUI extends SliderUI {
0060 // Old actions forward to an instance of this.
0061 private static final Actions SHARED_ACTION = new Actions();
0062
0063 public static final int POSITIVE_SCROLL = +1;
0064 public static final int NEGATIVE_SCROLL = -1;
0065 public static final int MIN_SCROLL = -2;
0066 public static final int MAX_SCROLL = +2;
0067
0068 protected Timer scrollTimer;
0069 protected JSlider slider;
0070
0071 protected Insets focusInsets = null;
0072 protected Insets insetCache = null;
0073 protected boolean leftToRightCache = true;
0074 protected Rectangle focusRect = null;
0075 protected Rectangle contentRect = null;
0076 protected Rectangle labelRect = null;
0077 protected Rectangle tickRect = null;
0078 protected Rectangle trackRect = null;
0079 protected Rectangle thumbRect = null;
0080
0081 protected int trackBuffer = 0; // The distance that the track is from the side of the control
0082
0083 private transient boolean isDragging;
0084
0085 protected TrackListener trackListener;
0086 protected ChangeListener changeListener;
0087 protected ComponentListener componentListener;
0088 protected FocusListener focusListener;
0089 protected ScrollListener scrollListener;
0090 protected PropertyChangeListener propertyChangeListener;
0091 private Handler handler;
0092 private int lastValue;
0093
0094 // Colors
0095 private Color shadowColor;
0096 private Color highlightColor;
0097 private Color focusColor;
0098
0099 /**
0100 * Whther or not sameLabelBaselines is up to date.
0101 */
0102 private boolean checkedLabelBaselines;
0103 /**
0104 * Whether or not all the entries in the labeltable have the same
0105 * baseline.
0106 */
0107 private boolean sameLabelBaselines;
0108
0109 protected Color getShadowColor() {
0110 return shadowColor;
0111 }
0112
0113 protected Color getHighlightColor() {
0114 return highlightColor;
0115 }
0116
0117 protected Color getFocusColor() {
0118 return focusColor;
0119 }
0120
0121 /**
0122 * Returns true if the user is dragging the slider.
0123 *
0124 * @return true if the user is dragging the slider
0125 * @since 1.5
0126 */
0127 protected boolean isDragging() {
0128 return isDragging;
0129 }
0130
0131 /////////////////////////////////////////////////////////////////////////////
0132 // ComponentUI Interface Implementation methods
0133 /////////////////////////////////////////////////////////////////////////////
0134 public static ComponentUI createUI(JComponent b) {
0135 return new BasicSliderUI((JSlider) b);
0136 }
0137
0138 public BasicSliderUI(JSlider b) {
0139 }
0140
0141 public void installUI(JComponent c) {
0142 slider = (JSlider) c;
0143
0144 checkedLabelBaselines = false;
0145
0146 slider.setEnabled(slider.isEnabled());
0147 LookAndFeel.installProperty(slider, "opaque", Boolean.TRUE);
0148
0149 isDragging = false;
0150 trackListener = createTrackListener(slider);
0151 changeListener = createChangeListener(slider);
0152 componentListener = createComponentListener(slider);
0153 focusListener = createFocusListener(slider);
0154 scrollListener = createScrollListener(slider);
0155 propertyChangeListener = createPropertyChangeListener(slider);
0156
0157 installDefaults(slider);
0158 installListeners(slider);
0159 installKeyboardActions(slider);
0160
0161 scrollTimer = new Timer(100, scrollListener);
0162 scrollTimer.setInitialDelay(300);
0163
0164 insetCache = slider.getInsets();
0165 leftToRightCache = BasicGraphicsUtils.isLeftToRight(slider);
0166 focusRect = new Rectangle();
0167 contentRect = new Rectangle();
0168 labelRect = new Rectangle();
0169 tickRect = new Rectangle();
0170 trackRect = new Rectangle();
0171 thumbRect = new Rectangle();
0172 lastValue = slider.getValue();
0173
0174 calculateGeometry(); // This figures out where the labels, ticks, track, and thumb are.
0175 }
0176
0177 public void uninstallUI(JComponent c) {
0178 if (c != slider)
0179 throw new IllegalComponentStateException(this
0180 + " was asked to deinstall() " + c
0181 + " when it only knows about " + slider + ".");
0182
0183 LookAndFeel.uninstallBorder(slider);
0184
0185 scrollTimer.stop();
0186 scrollTimer = null;
0187
0188 uninstallListeners(slider);
0189 uninstallKeyboardActions(slider);
0190
0191 focusInsets = null;
0192 insetCache = null;
0193 leftToRightCache = true;
0194 focusRect = null;
0195 contentRect = null;
0196 labelRect = null;
0197 tickRect = null;
0198 trackRect = null;
0199 thumbRect = null;
0200 trackListener = null;
0201 changeListener = null;
0202 componentListener = null;
0203 focusListener = null;
0204 scrollListener = null;
0205 propertyChangeListener = null;
0206 slider = null;
0207 }
0208
0209 protected void installDefaults(JSlider slider) {
0210 LookAndFeel.installBorder(slider, "Slider.border");
0211 LookAndFeel.installColorsAndFont(slider, "Slider.background",
0212 "Slider.foreground", "Slider.font");
0213 highlightColor = UIManager.getColor("Slider.highlight");
0214
0215 shadowColor = UIManager.getColor("Slider.shadow");
0216 focusColor = UIManager.getColor("Slider.focus");
0217
0218 focusInsets = (Insets) UIManager.get("Slider.focusInsets");
0219 }
0220
0221 protected TrackListener createTrackListener(JSlider slider) {
0222 return new TrackListener();
0223 }
0224
0225 protected ChangeListener createChangeListener(JSlider slider) {
0226 return getHandler();
0227 }
0228
0229 protected ComponentListener createComponentListener(JSlider slider) {
0230 return getHandler();
0231 }
0232
0233 protected FocusListener createFocusListener(JSlider slider) {
0234 return getHandler();
0235 }
0236
0237 protected ScrollListener createScrollListener(JSlider slider) {
0238 return new ScrollListener();
0239 }
0240
0241 protected PropertyChangeListener createPropertyChangeListener(
0242 JSlider slider) {
0243 return getHandler();
0244 }
0245
0246 private Handler getHandler() {
0247 if (handler == null) {
0248 handler = new Handler();
0249 }
0250 return handler;
0251 }
0252
0253 protected void installListeners(JSlider slider) {
0254 slider.addMouseListener(trackListener);
0255 slider.addMouseMotionListener(trackListener);
0256 slider.addFocusListener(focusListener);
0257 slider.addComponentListener(componentListener);
0258 slider.addPropertyChangeListener(propertyChangeListener);
0259 slider.getModel().addChangeListener(changeListener);
0260 }
0261
0262 protected void uninstallListeners(JSlider slider) {
0263 slider.removeMouseListener(trackListener);
0264 slider.removeMouseMotionListener(trackListener);
0265 slider.removeFocusListener(focusListener);
0266 slider.removeComponentListener(componentListener);
0267 slider.removePropertyChangeListener(propertyChangeListener);
0268 slider.getModel().removeChangeListener(changeListener);
0269 handler = null;
0270 }
0271
0272 protected void installKeyboardActions(JSlider slider) {
0273 InputMap km = getInputMap(JComponent.WHEN_FOCUSED, slider);
0274 SwingUtilities.replaceUIInputMap(slider,
0275 JComponent.WHEN_FOCUSED, km);
0276 LazyActionMap.installLazyActionMap(slider, BasicSliderUI.class,
0277 "Slider.actionMap");
0278 }
0279
0280 InputMap getInputMap(int condition, JSlider slider) {
0281 if (condition == JComponent.WHEN_FOCUSED) {
0282 InputMap keyMap = (InputMap) DefaultLookup.get(slider,
0283 this , "Slider.focusInputMap");
0284 InputMap rtlKeyMap;
0285
0286 if (slider.getComponentOrientation().isLeftToRight()
0287 || ((rtlKeyMap = (InputMap) DefaultLookup.get(
0288 slider, this ,
0289 "Slider.focusInputMap.RightToLeft")) == null)) {
0290 return keyMap;
0291 } else {
0292 rtlKeyMap.setParent(keyMap);
0293 return rtlKeyMap;
0294 }
0295 }
0296 return null;
0297 }
0298
0299 /**
0300 * Populates ComboBox's actions.
0301 */
0302 static void loadActionMap(LazyActionMap map) {
0303 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
0304 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
0305 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
0306 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
0307 map.put(new Actions(Actions.MIN_SCROLL_INCREMENT));
0308 map.put(new Actions(Actions.MAX_SCROLL_INCREMENT));
0309 }
0310
0311 protected void uninstallKeyboardActions(JSlider slider) {
0312 SwingUtilities.replaceUIActionMap(slider, null);
0313 SwingUtilities.replaceUIInputMap(slider,
0314 JComponent.WHEN_FOCUSED, null);
0315 }
0316
0317 /**
0318 * Returns the baseline.
0319 *
0320 * @throws NullPointerException {@inheritDoc}
0321 * @throws IllegalArgumentException {@inheritDoc}
0322 * @see javax.swing.JComponent#getBaseline(int, int)
0323 * @since 1.6
0324 */
0325 public int getBaseline(JComponent c, int width, int height) {
0326 super .getBaseline(c, width, height);
0327 if (slider.getPaintLabels() && labelsHaveSameBaselines()) {
0328 FontMetrics metrics = slider.getFontMetrics(slider
0329 .getFont());
0330 Insets insets = slider.getInsets();
0331 Dimension thumbSize = getThumbSize();
0332 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0333 int tickLength = getTickLength();
0334 int contentHeight = height - insets.top - insets.bottom
0335 - focusInsets.top - focusInsets.bottom;
0336 int thumbHeight = thumbSize.height;
0337 int centerSpacing = thumbHeight;
0338 if (slider.getPaintTicks()) {
0339 centerSpacing += tickLength;
0340 }
0341 // Assume uniform labels.
0342 centerSpacing += getHeightOfTallestLabel();
0343 int trackY = insets.top + focusInsets.top
0344 + (contentHeight - centerSpacing - 1) / 2;
0345 int trackHeight = thumbHeight;
0346 int tickY = trackY + trackHeight;
0347 int tickHeight = tickLength;
0348 if (!slider.getPaintTicks()) {
0349 tickHeight = 0;
0350 }
0351 int labelY = tickY + tickHeight;
0352 return labelY + metrics.getAscent();
0353 } else { // vertical
0354 boolean inverted = slider.getInverted();
0355 Integer value = inverted ? getLowestValue()
0356 : getHighestValue();
0357 if (value != null) {
0358 int thumbHeight = thumbSize.height;
0359 int trackBuffer = Math.max(metrics.getHeight() / 2,
0360 thumbHeight / 2);
0361 int contentY = focusInsets.top + insets.top;
0362 int trackY = contentY + trackBuffer;
0363 int trackHeight = height - focusInsets.top
0364 - focusInsets.bottom - insets.top
0365 - insets.bottom - trackBuffer - trackBuffer;
0366 int yPosition = yPositionForValue(value, trackY,
0367 trackHeight);
0368 return yPosition - metrics.getHeight() / 2
0369 + metrics.getAscent();
0370 }
0371 }
0372 }
0373 return 0;
0374 }
0375
0376 /**
0377 * Returns an enum indicating how the baseline of the component
0378 * changes as the size changes.
0379 *
0380 * @throws NullPointerException {@inheritDoc}
0381 * @see javax.swing.JComponent#getBaseline(int, int)
0382 * @since 1.6
0383 */
0384 public Component.BaselineResizeBehavior getBaselineResizeBehavior(
0385 JComponent c) {
0386 super .getBaselineResizeBehavior(c);
0387 // NOTE: BasicSpinner really provides for CENTER_OFFSET, but
0388 // the default min/pref size is smaller than it should be
0389 // so that getBaseline() doesn't implement the contract
0390 // for CENTER_OFFSET as defined in Component.
0391 return Component.BaselineResizeBehavior.OTHER;
0392 }
0393
0394 /**
0395 * Returns true if all the labels from the label table have the same
0396 * baseline.
0397 *
0398 * @return true if all the labels from the label table have the
0399 * same baseline
0400 * @since 1.6
0401 */
0402 protected boolean labelsHaveSameBaselines() {
0403 if (!checkedLabelBaselines) {
0404 checkedLabelBaselines = true;
0405 Dictionary dictionary = slider.getLabelTable();
0406 if (dictionary != null) {
0407 sameLabelBaselines = true;
0408 Enumeration elements = dictionary.elements();
0409 int baseline = -1;
0410 while (elements.hasMoreElements()) {
0411 Component label = (Component) elements
0412 .nextElement();
0413 Dimension pref = label.getPreferredSize();
0414 int labelBaseline = label.getBaseline(pref.width,
0415 pref.height);
0416 if (labelBaseline >= 0) {
0417 if (baseline == -1) {
0418 baseline = labelBaseline;
0419 } else if (baseline != labelBaseline) {
0420 sameLabelBaselines = false;
0421 break;
0422 }
0423 } else {
0424 sameLabelBaselines = false;
0425 break;
0426 }
0427 }
0428 } else {
0429 sameLabelBaselines = false;
0430 }
0431 }
0432 return sameLabelBaselines;
0433 }
0434
0435 public Dimension getPreferredHorizontalSize() {
0436 Dimension horizDim = (Dimension) DefaultLookup.get(slider,
0437 this , "Slider.horizontalSize");
0438 if (horizDim == null) {
0439 horizDim = new Dimension(200, 21);
0440 }
0441 return horizDim;
0442 }
0443
0444 public Dimension getPreferredVerticalSize() {
0445 Dimension vertDim = (Dimension) DefaultLookup.get(slider, this ,
0446 "Slider.verticalSize");
0447 if (vertDim == null) {
0448 vertDim = new Dimension(21, 200);
0449 }
0450 return vertDim;
0451 }
0452
0453 public Dimension getMinimumHorizontalSize() {
0454 Dimension minHorizDim = (Dimension) DefaultLookup.get(slider,
0455 this , "Slider.minimumHorizontalSize");
0456 if (minHorizDim == null) {
0457 minHorizDim = new Dimension(36, 21);
0458 }
0459 return minHorizDim;
0460 }
0461
0462 public Dimension getMinimumVerticalSize() {
0463 Dimension minVertDim = (Dimension) DefaultLookup.get(slider,
0464 this , "Slider.minimumVerticalSize");
0465 if (minVertDim == null) {
0466 minVertDim = new Dimension(21, 36);
0467 }
0468 return minVertDim;
0469 }
0470
0471 public Dimension getPreferredSize(JComponent c) {
0472 recalculateIfInsetsChanged();
0473 Dimension d;
0474 if (slider.getOrientation() == JSlider.VERTICAL) {
0475 d = new Dimension(getPreferredVerticalSize());
0476 d.width = insetCache.left + insetCache.right;
0477 d.width += focusInsets.left + focusInsets.right;
0478 d.width += trackRect.width + tickRect.width
0479 + labelRect.width;
0480 } else {
0481 d = new Dimension(getPreferredHorizontalSize());
0482 d.height = insetCache.top + insetCache.bottom;
0483 d.height += focusInsets.top + focusInsets.bottom;
0484 d.height += trackRect.height + tickRect.height
0485 + labelRect.height;
0486 }
0487
0488 return d;
0489 }
0490
0491 public Dimension getMinimumSize(JComponent c) {
0492 recalculateIfInsetsChanged();
0493 Dimension d;
0494
0495 if (slider.getOrientation() == JSlider.VERTICAL) {
0496 d = new Dimension(getMinimumVerticalSize());
0497 d.width = insetCache.left + insetCache.right;
0498 d.width += focusInsets.left + focusInsets.right;
0499 d.width += trackRect.width + tickRect.width
0500 + labelRect.width;
0501 } else {
0502 d = new Dimension(getMinimumHorizontalSize());
0503 d.height = insetCache.top + insetCache.bottom;
0504 d.height += focusInsets.top + focusInsets.bottom;
0505 d.height += trackRect.height + tickRect.height
0506 + labelRect.height;
0507 }
0508
0509 return d;
0510 }
0511
0512 public Dimension getMaximumSize(JComponent c) {
0513 Dimension d = getPreferredSize(c);
0514 if (slider.getOrientation() == JSlider.VERTICAL) {
0515 d.height = Short.MAX_VALUE;
0516 } else {
0517 d.width = Short.MAX_VALUE;
0518 }
0519
0520 return d;
0521 }
0522
0523 protected void calculateGeometry() {
0524 calculateFocusRect();
0525 calculateContentRect();
0526 calculateThumbSize();
0527 calculateTrackBuffer();
0528 calculateTrackRect();
0529 calculateTickRect();
0530 calculateLabelRect();
0531 calculateThumbLocation();
0532 }
0533
0534 protected void calculateFocusRect() {
0535 focusRect.x = insetCache.left;
0536 focusRect.y = insetCache.top;
0537 focusRect.width = slider.getWidth()
0538 - (insetCache.left + insetCache.right);
0539 focusRect.height = slider.getHeight()
0540 - (insetCache.top + insetCache.bottom);
0541 }
0542
0543 protected void calculateThumbSize() {
0544 Dimension size = getThumbSize();
0545 thumbRect.setSize(size.width, size.height);
0546 }
0547
0548 protected void calculateContentRect() {
0549 contentRect.x = focusRect.x + focusInsets.left;
0550 contentRect.y = focusRect.y + focusInsets.top;
0551 contentRect.width = focusRect.width
0552 - (focusInsets.left + focusInsets.right);
0553 contentRect.height = focusRect.height
0554 - (focusInsets.top + focusInsets.bottom);
0555 }
0556
0557 protected void calculateThumbLocation() {
0558 if (slider.getSnapToTicks()) {
0559 int sliderValue = slider.getValue();
0560 int snappedValue = sliderValue;
0561 int majorTickSpacing = slider.getMajorTickSpacing();
0562 int minorTickSpacing = slider.getMinorTickSpacing();
0563 int tickSpacing = 0;
0564
0565 if (minorTickSpacing > 0) {
0566 tickSpacing = minorTickSpacing;
0567 } else if (majorTickSpacing > 0) {
0568 tickSpacing = majorTickSpacing;
0569 }
0570
0571 if (tickSpacing != 0) {
0572 // If it's not on a tick, change the value
0573 if ((sliderValue - slider.getMinimum()) % tickSpacing != 0) {
0574 float temp = (float) (sliderValue - slider
0575 .getMinimum())
0576 / (float) tickSpacing;
0577 int whichTick = Math.round(temp);
0578
0579 // This is the fix for the bug #6401380
0580 if (temp - (int) temp == .5
0581 && sliderValue < lastValue) {
0582 whichTick--;
0583 }
0584 snappedValue = slider.getMinimum()
0585 + (whichTick * tickSpacing);
0586 }
0587
0588 if (snappedValue != sliderValue) {
0589 slider.setValue(snappedValue);
0590 }
0591 }
0592 }
0593
0594 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0595 int valuePosition = xPositionForValue(slider.getValue());
0596
0597 thumbRect.x = valuePosition - (thumbRect.width / 2);
0598 thumbRect.y = trackRect.y;
0599 } else {
0600 int valuePosition = yPositionForValue(slider.getValue());
0601
0602 thumbRect.x = trackRect.x;
0603 thumbRect.y = valuePosition - (thumbRect.height / 2);
0604 }
0605 }
0606
0607 protected void calculateTrackBuffer() {
0608 if (slider.getPaintLabels() && slider.getLabelTable() != null) {
0609 Component highLabel = getHighestValueLabel();
0610 Component lowLabel = getLowestValueLabel();
0611
0612 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0613 trackBuffer = Math.max(highLabel.getBounds().width,
0614 lowLabel.getBounds().width) / 2;
0615 trackBuffer = Math
0616 .max(trackBuffer, thumbRect.width / 2);
0617 } else {
0618 trackBuffer = Math.max(highLabel.getBounds().height,
0619 lowLabel.getBounds().height) / 2;
0620 trackBuffer = Math.max(trackBuffer,
0621 thumbRect.height / 2);
0622 }
0623 } else {
0624 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0625 trackBuffer = thumbRect.width / 2;
0626 } else {
0627 trackBuffer = thumbRect.height / 2;
0628 }
0629 }
0630 }
0631
0632 protected void calculateTrackRect() {
0633 int centerSpacing = 0; // used to center sliders added using BorderLayout.CENTER (bug 4275631)
0634 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0635 centerSpacing = thumbRect.height;
0636 if (slider.getPaintTicks())
0637 centerSpacing += getTickLength();
0638 if (slider.getPaintLabels())
0639 centerSpacing += getHeightOfTallestLabel();
0640 trackRect.x = contentRect.x + trackBuffer;
0641 trackRect.y = contentRect.y
0642 + (contentRect.height - centerSpacing - 1) / 2;
0643 trackRect.width = contentRect.width - (trackBuffer * 2);
0644 trackRect.height = thumbRect.height;
0645 } else {
0646 centerSpacing = thumbRect.width;
0647 if (BasicGraphicsUtils.isLeftToRight(slider)) {
0648 if (slider.getPaintTicks())
0649 centerSpacing += getTickLength();
0650 if (slider.getPaintLabels())
0651 centerSpacing += getWidthOfWidestLabel();
0652 } else {
0653 if (slider.getPaintTicks())
0654 centerSpacing -= getTickLength();
0655 if (slider.getPaintLabels())
0656 centerSpacing -= getWidthOfWidestLabel();
0657 }
0658 trackRect.x = contentRect.x
0659 + (contentRect.width - centerSpacing - 1) / 2;
0660 trackRect.y = contentRect.y + trackBuffer;
0661 trackRect.width = thumbRect.width;
0662 trackRect.height = contentRect.height - (trackBuffer * 2);
0663 }
0664
0665 }
0666
0667 /**
0668 * Gets the height of the tick area for horizontal sliders and the width of the
0669 * tick area for vertical sliders. BasicSliderUI uses the returned value to
0670 * determine the tick area rectangle. If you want to give your ticks some room,
0671 * make this larger than you need and paint your ticks away from the sides in paintTicks().
0672 */
0673 protected int getTickLength() {
0674 return 8;
0675 }
0676
0677 protected void calculateTickRect() {
0678 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0679 tickRect.x = trackRect.x;
0680 tickRect.y = trackRect.y + trackRect.height;
0681 tickRect.width = trackRect.width;
0682 tickRect.height = (slider.getPaintTicks()) ? getTickLength()
0683 : 0;
0684 } else {
0685 tickRect.width = (slider.getPaintTicks()) ? getTickLength()
0686 : 0;
0687 if (BasicGraphicsUtils.isLeftToRight(slider)) {
0688 tickRect.x = trackRect.x + trackRect.width;
0689 } else {
0690 tickRect.x = trackRect.x - tickRect.width;
0691 }
0692 tickRect.y = trackRect.y;
0693 tickRect.height = trackRect.height;
0694 }
0695 }
0696
0697 protected void calculateLabelRect() {
0698 if (slider.getPaintLabels()) {
0699 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0700 labelRect.x = tickRect.x - trackBuffer;
0701 labelRect.y = tickRect.y + tickRect.height;
0702 labelRect.width = tickRect.width + (trackBuffer * 2);
0703 labelRect.height = getHeightOfTallestLabel();
0704 } else {
0705 if (BasicGraphicsUtils.isLeftToRight(slider)) {
0706 labelRect.x = tickRect.x + tickRect.width;
0707 labelRect.width = getWidthOfWidestLabel();
0708 } else {
0709 labelRect.width = getWidthOfWidestLabel();
0710 labelRect.x = tickRect.x - labelRect.width;
0711 }
0712 labelRect.y = tickRect.y - trackBuffer;
0713 labelRect.height = tickRect.height + (trackBuffer * 2);
0714 }
0715 } else {
0716 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0717 labelRect.x = tickRect.x;
0718 labelRect.y = tickRect.y + tickRect.height;
0719 labelRect.width = tickRect.width;
0720 labelRect.height = 0;
0721 } else {
0722 if (BasicGraphicsUtils.isLeftToRight(slider)) {
0723 labelRect.x = tickRect.x + tickRect.width;
0724 } else {
0725 labelRect.x = tickRect.x;
0726 }
0727 labelRect.y = tickRect.y;
0728 labelRect.width = 0;
0729 labelRect.height = tickRect.height;
0730 }
0731 }
0732 }
0733
0734 protected Dimension getThumbSize() {
0735 Dimension size = new Dimension();
0736
0737 if (slider.getOrientation() == JSlider.VERTICAL) {
0738 size.width = 20;
0739 size.height = 11;
0740 } else {
0741 size.width = 11;
0742 size.height = 20;
0743 }
0744
0745 return size;
0746 }
0747
0748 public class PropertyChangeHandler implements
0749 PropertyChangeListener {
0750 // NOTE: This class exists only for backward compatability. All
0751 // its functionality has been moved into Handler. If you need to add
0752 // new functionality add it to the Handler, but make sure this
0753 // class calls into the Handler.
0754 public void propertyChange(PropertyChangeEvent e) {
0755 getHandler().propertyChange(e);
0756 }
0757 }
0758
0759 protected int getWidthOfWidestLabel() {
0760 Dictionary dictionary = slider.getLabelTable();
0761 int widest = 0;
0762 if (dictionary != null) {
0763 Enumeration keys = dictionary.keys();
0764 while (keys.hasMoreElements()) {
0765 Component label = (Component) dictionary.get(keys
0766 .nextElement());
0767 widest = Math.max(label.getPreferredSize().width,
0768 widest);
0769 }
0770 }
0771 return widest;
0772 }
0773
0774 protected int getHeightOfTallestLabel() {
0775 Dictionary dictionary = slider.getLabelTable();
0776 int tallest = 0;
0777 if (dictionary != null) {
0778 Enumeration keys = dictionary.keys();
0779 while (keys.hasMoreElements()) {
0780 Component label = (Component) dictionary.get(keys
0781 .nextElement());
0782 tallest = Math.max(label.getPreferredSize().height,
0783 tallest);
0784 }
0785 }
0786 return tallest;
0787 }
0788
0789 protected int getWidthOfHighValueLabel() {
0790 Component label = getHighestValueLabel();
0791 int width = 0;
0792
0793 if (label != null) {
0794 width = label.getPreferredSize().width;
0795 }
0796
0797 return width;
0798 }
0799
0800 protected int getWidthOfLowValueLabel() {
0801 Component label = getLowestValueLabel();
0802 int width = 0;
0803
0804 if (label != null) {
0805 width = label.getPreferredSize().width;
0806 }
0807
0808 return width;
0809 }
0810
0811 protected int getHeightOfHighValueLabel() {
0812 Component label = getHighestValueLabel();
0813 int height = 0;
0814
0815 if (label != null) {
0816 height = label.getPreferredSize().height;
0817 }
0818
0819 return height;
0820 }
0821
0822 protected int getHeightOfLowValueLabel() {
0823 Component label = getLowestValueLabel();
0824 int height = 0;
0825
0826 if (label != null) {
0827 height = label.getPreferredSize().height;
0828 }
0829
0830 return height;
0831 }
0832
0833 protected boolean drawInverted() {
0834 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0835 if (BasicGraphicsUtils.isLeftToRight(slider)) {
0836 return slider.getInverted();
0837 } else {
0838 return !slider.getInverted();
0839 }
0840 } else {
0841 return slider.getInverted();
0842 }
0843 }
0844
0845 /**
0846 * Returns the biggest value that has an entry in the label table.
0847 *
0848 * @return biggest value that has an entry in the label table, or
0849 * null.
0850 * @since 1.6
0851 */
0852 protected Integer getHighestValue() {
0853 Dictionary dictionary = slider.getLabelTable();
0854 if (dictionary != null) {
0855 Enumeration keys = dictionary.keys();
0856 int max = slider.getMinimum() - 1;
0857 while (keys.hasMoreElements()) {
0858 max = Math.max(max, ((Integer) keys.nextElement())
0859 .intValue());
0860 }
0861 if (max == slider.getMinimum() - 1) {
0862 return null;
0863 }
0864 return max;
0865 }
0866 return null;
0867 }
0868
0869 /**
0870 * Returns the smallest value that has an entry in the label table.
0871 *
0872 * @return smallest value that has an entry in the label table, or
0873 * null.
0874 * @since 1.6
0875 */
0876 protected Integer getLowestValue() {
0877 Dictionary dictionary = slider.getLabelTable();
0878 if (dictionary != null) {
0879 Enumeration keys = dictionary.keys();
0880 int min = slider.getMaximum() + 1;
0881 while (keys.hasMoreElements()) {
0882 min = Math.min(min, ((Integer) keys.nextElement())
0883 .intValue());
0884 }
0885 if (min == slider.getMaximum() + 1) {
0886 return null;
0887 }
0888 return min;
0889 }
0890 return null;
0891 }
0892
0893 /**
0894 * Returns the label that corresponds to the highest slider value in the label table.
0895 * @see JSlider#setLabelTable
0896 */
0897 protected Component getLowestValueLabel() {
0898 Integer min = getLowestValue();
0899 if (min != null) {
0900 return (Component) slider.getLabelTable().get(min);
0901 }
0902 return null;
0903 }
0904
0905 /**
0906 * Returns the label that corresponds to the lowest slider value in the label table.
0907 * @see JSlider#setLabelTable
0908 */
0909 protected Component getHighestValueLabel() {
0910 Integer max = getHighestValue();
0911 if (max != null) {
0912 return (Component) slider.getLabelTable().get(max);
0913 }
0914 return null;
0915 }
0916
0917 public void paint(Graphics g, JComponent c) {
0918 recalculateIfInsetsChanged();
0919 recalculateIfOrientationChanged();
0920 Rectangle clip = g.getClipBounds();
0921
0922 if (!clip.intersects(trackRect) && slider.getPaintTrack())
0923 calculateGeometry();
0924
0925 if (slider.getPaintTrack() && clip.intersects(trackRect)) {
0926 paintTrack(g);
0927 }
0928 if (slider.getPaintTicks() && clip.intersects(tickRect)) {
0929 paintTicks(g);
0930 }
0931 if (slider.getPaintLabels() && clip.intersects(labelRect)) {
0932 paintLabels(g);
0933 }
0934 if (slider.hasFocus() && clip.intersects(focusRect)) {
0935 paintFocus(g);
0936 }
0937 if (clip.intersects(thumbRect)) {
0938 paintThumb(g);
0939 }
0940 }
0941
0942 protected void recalculateIfInsetsChanged() {
0943 Insets newInsets = slider.getInsets();
0944 if (!newInsets.equals(insetCache)) {
0945 insetCache = newInsets;
0946 calculateGeometry();
0947 }
0948 }
0949
0950 protected void recalculateIfOrientationChanged() {
0951 boolean ltr = BasicGraphicsUtils.isLeftToRight(slider);
0952 if (ltr != leftToRightCache) {
0953 leftToRightCache = ltr;
0954 calculateGeometry();
0955 }
0956 }
0957
0958 public void paintFocus(Graphics g) {
0959 g.setColor(getFocusColor());
0960
0961 BasicGraphicsUtils.drawDashedRect(g, focusRect.x, focusRect.y,
0962 focusRect.width, focusRect.height);
0963 }
0964
0965 public void paintTrack(Graphics g) {
0966
0967 Rectangle trackBounds = trackRect;
0968
0969 if (slider.getOrientation() == JSlider.HORIZONTAL) {
0970 int cy = (trackBounds.height / 2) - 2;
0971 int cw = trackBounds.width;
0972
0973 g.translate(trackBounds.x, trackBounds.y + cy);
0974
0975 g.setColor(getShadowColor());
0976 g.drawLine(0, 0, cw - 1, 0);
0977 g.drawLine(0, 1, 0, 2);
0978 g.setColor(getHighlightColor());
0979 g.drawLine(0, 3, cw, 3);
0980 g.drawLine(cw, 0, cw, 3);
0981 g.setColor(Color.black);
0982 g.drawLine(1, 1, cw - 2, 1);
0983
0984 g.translate(-trackBounds.x, -(trackBounds.y + cy));
0985 } else {
0986 int cx = (trackBounds.width / 2) - 2;
0987 int ch = trackBounds.height;
0988
0989 g.translate(trackBounds.x + cx, trackBounds.y);
0990
0991 g.setColor(getShadowColor());
0992 g.drawLine(0, 0, 0, ch - 1);
0993 g.drawLine(1, 0, 2, 0);
0994 g.setColor(getHighlightColor());
0995 g.drawLine(3, 0, 3, ch);
0996 g.drawLine(0, ch, 3, ch);
0997 g.setColor(Color.black);
0998 g.drawLine(1, 1, 1, ch - 2);
0999
1000 g.translate(-(trackBounds.x + cx), -trackBounds.y);
1001 }
1002 }
1003
1004 public void paintTicks(Graphics g) {
1005 Rectangle tickBounds = tickRect;
1006 int i;
1007 int maj, min, max;
1008 int w = tickBounds.width;
1009 int h = tickBounds.height;
1010 int centerEffect, tickHeight;
1011
1012 g.setColor(DefaultLookup.getColor(slider, this ,
1013 "Slider.tickColor", Color.black));
1014
1015 maj = slider.getMajorTickSpacing();
1016 min = slider.getMinorTickSpacing();
1017
1018 if (slider.getOrientation() == JSlider.HORIZONTAL) {
1019 g.translate(0, tickBounds.y);
1020
1021 int value = slider.getMinimum();
1022 int xPos = 0;
1023
1024 if (slider.getMinorTickSpacing() > 0) {
1025 while (value <= slider.getMaximum()) {
1026 xPos = xPositionForValue(value);
1027 paintMinorTickForHorizSlider(g, tickBounds, xPos);
1028 value += slider.getMinorTickSpacing();
1029 }
1030 }
1031
1032 if (slider.getMajorTickSpacing() > 0) {
1033 value = slider.getMinimum();
1034
1035 while (value <= slider.getMaximum()) {
1036 xPos = xPositionForValue(value);
1037 paintMajorTickForHorizSlider(g, tickBounds, xPos);
1038 value += slider.getMajorTickSpacing();
1039 }
1040 }
1041
1042 g.translate(0, -tickBounds.y);
1043 } else {
1044 g.translate(tickBounds.x, 0);
1045
1046 int value = slider.getMinimum();
1047 int yPos = 0;
1048
1049 if (slider.getMinorTickSpacing() > 0) {
1050 int offset = 0;
1051 if (!BasicGraphicsUtils.isLeftToRight(slider)) {
1052 offset = tickBounds.width - tickBounds.width / 2;
1053 g.translate(offset, 0);
1054 }
1055
1056 while (value <= slider.getMaximum()) {
1057 yPos = yPositionForValue(value);
1058 paintMinorTickForVertSlider(g, tickBounds, yPos);
1059 value += slider.getMinorTickSpacing();
1060 }
1061
1062 if (!BasicGraphicsUtils.isLeftToRight(slider)) {
1063 g.translate(-offset, 0);
1064 }
1065 }
1066
1067 if (slider.getMajorTickSpacing() > 0) {
1068 value = slider.getMinimum();
1069 if (!BasicGraphicsUtils.isLeftToRight(slider)) {
1070 g.translate(2, 0);
1071 }
1072
1073 while (value <= slider.getMaximum()) {
1074 yPos = yPositionForValue(value);
1075 paintMajorTickForVertSlider(g, tickBounds, yPos);
1076 value += slider.getMajorTickSpacing();
1077 }
1078
1079 if (!BasicGraphicsUtils.isLeftToRight(slider)) {
1080 g.translate(-2, 0);
1081 }
1082 }
1083 g.translate(-tickBounds.x, 0);
1084 }
1085 }
1086
1087 protected void paintMinorTickForHorizSlider(Graphics g,
1088 Rectangle tickBounds, int x) {
1089 g.drawLine(x, 0, x, tickBounds.height / 2 - 1);
1090 }
1091
1092 protected void paintMajorTickForHorizSlider(Graphics g,
1093 Rectangle tickBounds, int x) {
1094 g.drawLine(x, 0, x, tickBounds.height - 2);
1095 }
1096
1097 protected void paintMinorTickForVertSlider(Graphics g,
1098 Rectangle tickBounds, int y) {
1099 g.drawLine(0, y, tickBounds.width / 2 - 1, y);
1100 }
1101
1102 protected void paintMajorTickForVertSlider(Graphics g,
1103 Rectangle tickBounds, int y) {
1104 g.drawLine(0, y, tickBounds.width - 2, y);
1105 }
1106
1107 public void paintLabels(Graphics g) {
1108 Rectangle labelBounds = labelRect;
1109
1110 Dictionary dictionary = slider.getLabelTable();
1111 if (dictionary != null) {
1112 Enumeration keys = dictionary.keys();
1113 int minValue = slider.getMinimum();
1114 int maxValue = slider.getMaximum();
1115 boolean enabled = slider.isEnabled();
1116 while (keys.hasMoreElements()) {
1117 Integer key = (Integer) keys.nextElement();
1118 int value = key.intValue();
1119 if (value >= minValue && value <= maxValue) {
1120 Component label = (Component) dictionary.get(key);
1121 if (label instanceof JComponent) {
1122 ((JComponent) label).setEnabled(enabled);
1123 }
1124 if (slider.getOrientation() == JSlider.HORIZONTAL) {
1125 g.translate(0, labelBounds.y);
1126 paintHorizontalLabel(g, value, label);
1127 g.translate(0, -labelBounds.y);
1128 } else {
1129 int offset = 0;
1130 if (!BasicGraphicsUtils.isLeftToRight(slider)) {
1131 offset = labelBounds.width
1132 - label.getPreferredSize().width;
1133 }
1134 g.translate(labelBounds.x + offset, 0);
1135 paintVerticalLabel(g, value, label);
1136 g.translate(-labelBounds.x - offset, 0);
1137 }
1138 }
1139 }
1140 }
1141
1142 }
1143
1144 /**
1145 * Called for every label in the label table. Used to draw the labels for horizontal sliders.
1146 * The graphics have been translated to labelRect.y already.
1147 * @see JSlider#setLabelTable
1148 */
1149 protected void paintHorizontalLabel(Graphics g, int value,
1150 Component label) {
1151 int labelCenter = xPositionForValue(value);
1152 int labelLeft = labelCenter
1153 - (label.getPreferredSize().width / 2);
1154 g.translate(labelLeft, 0);
1155 label.paint(g);
1156 g.translate(-labelLeft, 0);
1157 }
1158
1159 /**
1160 * Called for every label in the label table. Used to draw the labels for vertical sliders.
1161 * The graphics have been translated to labelRect.x already.
1162 * @see JSlider#setLabelTable
1163 */
1164 protected void paintVerticalLabel(Graphics g, int value,
1165 Component label) {
1166 int labelCenter = yPositionForValue(value);
1167 int labelTop = labelCenter
1168 - (label.getPreferredSize().height / 2);
1169 g.translate(0, labelTop);
1170 label.paint(g);
1171 g.translate(0, -labelTop);
1172 }
1173
1174 public void paintThumb(Graphics g) {
1175 Rectangle knobBounds = thumbRect;
1176 int w = knobBounds.width;
1177 int h = knobBounds.height;
1178
1179 g.translate(knobBounds.x, knobBounds.y);
1180
1181 if (slider.isEnabled()) {
1182 g.setColor(slider.getBackground());
1183 } else {
1184 g.setColor(slider.getBackground().darker());
1185 }
1186
1187 Boolean paintThumbArrowShape = (Boolean) slider
1188 .getClientProperty("Slider.paintThumbArrowShape");
1189
1190 if ((!slider.getPaintTicks() && paintThumbArrowShape == null)
1191 || paintThumbArrowShape == Boolean.FALSE) {
1192
1193 // "plain" version
1194 g.fillRect(0, 0, w, h);
1195
1196 g.setColor(Color.black);
1197 g.drawLine(0, h - 1, w - 1, h - 1);
1198 g.drawLine(w - 1, 0, w - 1, h - 1);
1199
1200 g.setColor(highlightColor);
1201 g.drawLine(0, 0, 0, h - 2);
1202 g.drawLine(1, 0, w - 2, 0);
1203
1204 g.setColor(shadowColor);
1205 g.drawLine(1, h - 2, w - 2, h - 2);
1206 g.drawLine(w - 2, 1, w - 2, h - 3);
1207 } else if (slider.getOrientation() == JSlider.HORIZONTAL) {
1208 int cw = w / 2;
1209 g.fillRect(1, 1, w - 3, h - 1 - cw);
1210 Polygon p = new Polygon();
1211 p.addPoint(1, h - cw);
1212 p.addPoint(cw - 1, h - 1);
1213 p.addPoint(w - 2, h - 1 - cw);
1214 g.fillPolygon(p);
1215
1216 g.setColor(highlightColor);
1217 g.drawLine(0, 0, w - 2, 0);
1218 g.drawLine(0, 1, 0, h - 1 - cw);
1219 g.drawLine(0, h - cw, cw - 1, h - 1);
1220
1221 g.setColor(Color.black);
1222 g.drawLine(w - 1, 0, w - 1, h - 2 - cw);
1223 g.drawLine(w - 1, h - 1 - cw, w - 1 - cw, h - 1);
1224
1225 g.setColor(shadowColor);
1226 g.drawLine(w - 2, 1, w - 2, h - 2 - cw);
1227 g.drawLine(w - 2, h - 1 - cw, w - 1 - cw, h - 2);
1228 } else { // vertical
1229 int cw = h / 2;
1230 if (BasicGraphicsUtils.isLeftToRight(slider)) {
1231 g.fillRect(1, 1, w - 1 - cw, h - 3);
1232 Polygon p = new Polygon();
1233 p.addPoint(w - cw - 1, 0);
1234 p.addPoint(w - 1, cw);
1235 p.addPoint(w - 1 - cw, h - 2);
1236 g.fillPolygon(p);
1237
1238 g.setColor(highlightColor);
1239 g.drawLine(0, 0, 0, h - 2); // left
1240 g.drawLine(1, 0, w - 1 - cw, 0); // top
1241 g.drawLine(w - cw - 1, 0, w - 1, cw); // top slant
1242
1243 g.setColor(Color.black);
1244 g.drawLine(0, h - 1, w - 2 - cw, h - 1); // bottom
1245 g.drawLine(w - 1 - cw, h - 1, w - 1, h - 1 - cw); // bottom slant
1246
1247 g.setColor(shadowColor);
1248 g.drawLine(1, h - 2, w - 2 - cw, h - 2); // bottom
1249 g.drawLine(w - 1 - cw, h - 2, w - 2, h - cw - 1); // bottom slant
1250 } else {
1251 g.fillRect(5, 1, w - 1 - cw, h - 3);
1252 Polygon p = new Polygon();
1253 p.addPoint(cw, 0);
1254 p.addPoint(0, cw);
1255 p.addPoint(cw, h - 2);
1256 g.fillPolygon(p);
1257
1258 g.setColor(highlightColor);
1259 g.drawLine(cw - 1, 0, w - 2, 0); // top
1260 g.drawLine(0, cw, cw, 0); // top slant
1261
1262 g.setColor(Color.black);
1263 g.drawLine(0, h - 1 - cw, cw, h - 1); // bottom slant
1264 g.drawLine(cw, h - 1, w - 1, h - 1); // bottom
1265
1266 g.setColor(shadowColor);
1267 g.drawLine(cw, h - 2, w - 2, h - 2); // bottom
1268 g.drawLine(w - 1, 1, w - 1, h - 2); // right
1269 }
1270 }
1271
1272 g.translate(-knobBounds.x, -knobBounds.y);
1273 }
1274
1275 // Used exclusively by setThumbLocation()
1276 private static Rectangle unionRect = new Rectangle();
1277
1278 public void setThumbLocation(int x, int y) {
1279 unionRect.setBounds(thumbRect);
1280
1281 thumbRect.setLocation(x, y);
1282
1283 SwingUtilities.computeUnion(thumbRect.x, thumbRect.y,
1284 thumbRect.width, thumbRect.height, unionRect);
1285 slider.repaint(unionRect.x, unionRect.y, unionRect.width,
1286 unionRect.height);
1287 }
1288
1289 public void scrollByBlock(int direction) {
1290 synchronized (slider) {
1291
1292 int oldValue = slider.getValue();
1293 int blockIncrement = (slider.getMaximum() - slider
1294 .getMinimum()) / 10;
1295 if (blockIncrement <= 0
1296 && slider.getMaximum() > slider.getMinimum()) {
1297
1298 blockIncrement = 1;
1299 }
1300
1301 int delta = blockIncrement
1302 * ((direction > 0) ? POSITIVE_SCROLL
1303 : NEGATIVE_SCROLL);
1304 slider.setValue(oldValue + delta);
1305 }
1306 }
1307
1308 public void scrollByUnit(int direction) {
1309 synchronized (slider) {
1310
1311 int oldValue = slider.getValue();
1312 int delta = 1 * ((direction > 0) ? POSITIVE_SCROLL
1313 : NEGATIVE_SCROLL);
1314
1315 slider.setValue(oldValue + delta);
1316 }
1317 }
1318
1319 /**
1320 * This function is called when a mousePressed was detected in the track, not
1321 * in the thumb. The default behavior is to scroll by block. You can
1322 * override this method to stop it from scrolling or to add additional behavior.
1323 */
1324 protected void scrollDueToClickInTrack(int dir) {
1325 scrollByBlock(dir);
1326 }
1327
1328 protected int xPositionForValue(int value) {
1329 int min = slider.getMinimum();
1330 int max = slider.getMaximum();
1331 int trackLength = trackRect.width;
1332 double valueRange = (double) max - (double) min;
1333 double pixelsPerValue = (double) trackLength / valueRange;
1334 int trackLeft = trackRect.x;
1335 int trackRight = trackRect.x + (trackRect.width - 1);
1336 int xPosition;
1337
1338 if (!drawInverted()) {
1339 xPosition = trackLeft;
1340 xPosition += Math.round(pixelsPerValue
1341 * ((double) value - min));
1342 } else {
1343 xPosition = trackRight;
1344 xPosition -= Math.round(pixelsPerValue
1345 * ((double) value - min));
1346 }
1347
1348 xPosition = Math.max(trackLeft, xPosition);
1349 xPosition = Math.min(trackRight, xPosition);
1350
1351 return xPosition;
1352 }
1353
1354 protected int yPositionForValue(int value) {
1355 return yPositionForValue(value, trackRect.y, trackRect.height);
1356 }
1357
1358 /**
1359 * Returns the y location for the specified value. No checking is
1360 * done on the arguments. In particular if <code>trackHeight</code> is
1361 * negative undefined results may occur.
1362 *
1363 * @param value the slider value to get the location for
1364 * @param trackY y-origin of the track
1365 * @param trackHeight the height of the track
1366 * @since 1.6
1367 */
1368 protected int yPositionForValue(int value, int trackY,
1369 int trackHeight) {
1370 int min = slider.getMinimum();
1371 int max = slider.getMaximum();
1372 double valueRange = (double) max - (double) min;
1373 double pixelsPerValue = (double) trackHeight
1374 / (double) valueRange;
1375 int trackBottom = trackY + (trackHeight - 1);
1376 int yPosition;
1377
1378 if (!drawInverted()) {
1379 yPosition = trackY;
1380 yPosition += Math.round(pixelsPerValue
1381 * ((double) max - value));
1382 } else {
1383 yPosition = trackY;
1384 yPosition += Math.round(pixelsPerValue
1385 * ((double) value - min));
1386 }
1387
1388 yPosition = Math.max(trackY, yPosition);
1389 yPosition = Math.min(trackBottom, yPosition);
1390
1391 return yPosition;
1392 }
1393
1394 /**
1395 * Returns a value give a y position. If yPos is past the track at the top or the
1396 * bottom it will set the value to the min or max of the slider, depending if the
1397 * slider is inverted or not.
1398 */
1399 public int valueForYPosition(int yPos) {
1400 int value;
1401 final int minValue = slider.getMinimum();
1402 final int maxValue = slider.getMaximum();
1403 final int trackLength = trackRect.height;
1404 final int trackTop = trackRect.y;
1405 final int trackBottom = trackRect.y + (trackRect.height - 1);
1406
1407 if (yPos <= trackTop) {
1408 value = drawInverted() ? minValue : maxValue;
1409 } else if (yPos >= trackBottom) {
1410 value = drawInverted() ? maxValue : minValue;
1411 } else {
1412 int distanceFromTrackTop = yPos - trackTop;
1413 double valueRange = (double) maxValue - (double) minValue;
1414 double valuePerPixel = valueRange / (double) trackLength;
1415 int valueFromTrackTop = (int) Math
1416 .round(distanceFromTrackTop * valuePerPixel);
1417
1418 value = drawInverted() ? minValue + valueFromTrackTop
1419 : maxValue - valueFromTrackTop;
1420 }
1421
1422 return value;
1423 }
1424
1425 /**
1426 * Returns a value give an x position. If xPos is past the track at the left or the
1427 * right it will set the value to the min or max of the slider, depending if the
1428 * slider is inverted or not.
1429 */
1430 public int valueForXPosition(int xPos) {
1431 int value;
1432 final int minValue = slider.getMinimum();
1433 final int maxValue = slider.getMaximum();
1434 final int trackLength = trackRect.width;
1435 final int trackLeft = trackRect.x;
1436 final int trackRight = trackRect.x + (trackRect.width - 1);
1437
1438 if (xPos <= trackLeft) {
1439 value = drawInverted() ? maxValue : minValue;
1440 } else if (xPos >= trackRight) {
1441 value = drawInverted() ? minValue : maxValue;
1442 } else {
1443 int distanceFromTrackLeft = xPos - trackLeft;
1444 double valueRange = (double) maxValue - (double) minValue;
1445 double valuePerPixel = valueRange / (double) trackLength;
1446 int valueFromTrackLeft = (int) Math
1447 .round(distanceFromTrackLeft * valuePerPixel);
1448
1449 value = drawInverted() ? maxValue - valueFromTrackLeft
1450 : minValue + valueFromTrackLeft;
1451 }
1452
1453 return value;
1454 }
1455
1456 private class Handler implements ChangeListener, ComponentListener,
1457 FocusListener, PropertyChangeListener {
1458 // Change Handler
1459 public void stateChanged(ChangeEvent e) {
1460 if (!isDragging) {
1461 calculateThumbLocation();
1462 slider.repaint();
1463 }
1464 lastValue = slider.getValue();
1465 }
1466
1467 // Component Handler
1468 public void componentHidden(ComponentEvent e) {
1469 }
1470
1471 public void componentMoved(ComponentEvent e) {
1472 }
1473
1474 public void componentResized(ComponentEvent e) {
1475 calculateGeometry();
1476 slider.repaint();
1477 }
1478
1479 public void componentShown(ComponentEvent e) {
1480 }
1481
1482 // Focus Handler
1483 public void focusGained(FocusEvent e) {
1484 slider.repaint();
1485 }
1486
1487 public void focusLost(FocusEvent e) {
1488 slider.repaint();
1489 }
1490
1491 // Property Change Handler
1492 public void propertyChange(PropertyChangeEvent e) {
1493 String propertyName = e.getPropertyName();
1494 if (propertyName == "orientation"
1495 || propertyName == "inverted"
1496 || propertyName == "labelTable"
1497 || propertyName == "majorTickSpacing"
1498 || propertyName == "minorTickSpacing"
1499 || propertyName == "paintTicks"
1500 || propertyName == "paintTrack"
1501 || propertyName == "font"
1502 || propertyName == "paintLabels") {
1503 checkedLabelBaselines = false;
1504 calculateGeometry();
1505 slider.repaint();
1506 } else if (propertyName == "componentOrientation") {
1507 calculateGeometry();
1508 slider.repaint();
1509 InputMap km = getInputMap(JComponent.WHEN_FOCUSED,
1510 slider);
1511 SwingUtilities.replaceUIInputMap(slider,
1512 JComponent.WHEN_FOCUSED, km);
1513 } else if (propertyName == "model") {
1514 ((BoundedRangeModel) e.getOldValue())
1515 .removeChangeListener(changeListener);
1516 ((BoundedRangeModel) e.getNewValue())
1517 .addChangeListener(changeListener);
1518 calculateThumbLocation();
1519 slider.repaint();
1520 }
1521 }
1522 }
1523
1524 /////////////////////////////////////////////////////////////////////////
1525 /// Model Listener Class
1526 /////////////////////////////////////////////////////////////////////////
1527 /**
1528 * Data model listener.
1529 *
1530 * This class should be treated as a "protected" inner class.
1531 * Instantiate it only within subclasses of <Foo>.
1532 */
1533 public class ChangeHandler implements ChangeListener {
1534 // NOTE: This class exists only for backward compatability. All
1535 // its functionality has been moved into Handler. If you need to add
1536 // new functionality add it to the Handler, but make sure this
1537 // class calls into the Handler.
1538 public void stateChanged(ChangeEvent e) {
1539 getHandler().stateChanged(e);
1540 }
1541 }
1542
1543 /////////////////////////////////////////////////////////////////////////
1544 /// Track Listener Class
1545 /////////////////////////////////////////////////////////////////////////
1546 /**
1547 * Track mouse movements.
1548 *
1549 * This class should be treated as a "protected" inner class.
1550 * Instantiate it only within subclasses of <Foo>.
1551 */
1552 public class TrackListener extends MouseInputAdapter {
1553 protected transient int offset;
1554 protected transient int currentMouseX, currentMouseY;
1555
1556 public void mouseReleased(MouseEvent e) {
1557 if (!slider.isEnabled()) {
1558 return;
1559 }
1560
1561 offset = 0;
1562 scrollTimer.stop();
1563
1564 // This is the way we have to determine snap-to-ticks. It's
1565 // hard to explain but since ChangeEvents don't give us any
1566 // idea what has changed we don't have a way to stop the thumb
1567 // bounds from being recalculated. Recalculating the thumb
1568 // bounds moves the thumb over the current value (i.e., snapping
1569 // to the ticks).
1570 if (slider.getSnapToTicks() /*|| slider.getSnapToValue()*/) {
1571 isDragging = false;
1572 slider.setValueIsAdjusting(false);
1573 } else {
1574 slider.setValueIsAdjusting(false);
1575 isDragging = false;
1576 }
1577 slider.repaint();
1578 }
1579
1580 /**
1581 * If the mouse is pressed above the "thumb" component
1582 * then reduce the scrollbars value by one page ("page up"),
1583 * otherwise increase it by one page. If there is no
1584 * thumb then page up if the mouse is in the upper half
1585 * of the track.
1586 */
1587 public void mousePressed(MouseEvent e) {
1588 if (!slider.isEnabled()) {
1589 return;
1590 }
1591
1592 // We should recalculate geometry just before
1593 // calculation of the thumb movement direction.
1594 // It is important for the case, when JSlider
1595 // is a cell editor in JTable. See 6348946.
1596 calculateGeometry();
1597
1598 currentMouseX = e.getX();
1599 currentMouseY = e.getY();
1600
1601 if (slider.isRequestFocusEnabled()) {
1602 slider.requestFocus();
1603 }
1604
1605 // Clicked in the Thumb area?
1606 if (thumbRect.contains(currentMouseX, currentMouseY)) {
1607 switch (slider.getOrientation()) {
1608 case JSlider.VERTICAL:
1609 offset = currentMouseY - thumbRect.y;
1610 break;
1611 case JSlider.HORIZONTAL:
1612 offset = currentMouseX - thumbRect.x;
1613 break;
1614 }
1615 isDragging = true;
1616 return;
1617 }
1618 isDragging = false;
1619 slider.setValueIsAdjusting(true);
1620
1621 Dimension sbSize = slider.getSize();
1622 int direction = POSITIVE_SCROLL;
1623
1624 switch (slider.getOrientation()) {
1625 case JSlider.VERTICAL:
1626 if (thumbRect.isEmpty()) {
1627 int scrollbarCenter = sbSize.height / 2;
1628 if (!drawInverted()) {
1629 direction = (currentMouseY < scrollbarCenter) ? POSITIVE_SCROLL
1630 : NEGATIVE_SCROLL;
1631 } else {
1632 direction = (currentMouseY < scrollbarCenter) ? NEGATIVE_SCROLL
1633 : POSITIVE_SCROLL;
1634 }
1635 } else {
1636 int thumbY = thumbRect.y;
1637 if (!drawInverted()) {
1638 direction = (currentMouseY < thumbY) ? POSITIVE_SCROLL
1639 : NEGATIVE_SCROLL;
1640 } else {
1641 direction = (currentMouseY < thumbY) ? NEGATIVE_SCROLL
1642 : POSITIVE_SCROLL;
1643 }
1644 }
1645 break;
1646 case JSlider.HORIZONTAL:
1647 if (thumbRect.isEmpty()) {
1648 int scrollbarCenter = sbSize.width / 2;
1649 if (!drawInverted()) {
1650 direction = (currentMouseX < scrollbarCenter) ? NEGATIVE_SCROLL
1651 : POSITIVE_SCROLL;
1652 } else {
1653 direction = (currentMouseX < scrollbarCenter) ? POSITIVE_SCROLL
1654 : NEGATIVE_SCROLL;
1655 }
1656 } else {
1657 int thumbX = thumbRect.x;
1658 if (!drawInverted()) {
1659 direction = (currentMouseX < thumbX) ? NEGATIVE_SCROLL
1660 : POSITIVE_SCROLL;
1661 } else {
1662 direction = (currentMouseX < thumbX) ? POSITIVE_SCROLL
1663 : NEGATIVE_SCROLL;
1664 }
1665 }
1666 break;
1667 }
1668
1669 if (shouldScroll(direction)) {
1670 scrollDueToClickInTrack(direction);
1671 }
1672 if (shouldScroll(direction)) {
1673 scrollTimer.stop();
1674 scrollListener.setDirection(direction);
1675 scrollTimer.start();
1676 }
1677 }
1678
1679 public boolean shouldScroll(int direction) {
1680 Rectangle r = thumbRect;
1681 if (slider.getOrientation() == JSlider.VERTICAL) {
1682 if (drawInverted() ? direction < 0 : direction > 0) {
1683 if (r.y <= currentMouseY) {
1684 return false;
1685 }
1686 } else if (r.y + r.height >= currentMouseY) {
1687 return false;
1688 }
1689 } else {
1690 if (drawInverted() ? direction < 0 : direction > 0) {
1691 if (r.x + r.width >= currentMouseX) {
1692 return false;
1693 }
1694 } else if (r.x <= currentMouseX) {
1695 return false;
1696 }
1697 }
1698
1699 if (direction > 0
1700 && slider.getValue() + slider.getExtent() >= slider
1701 .getMaximum()) {
1702 return false;
1703 } else if (direction < 0
1704 && slider.getValue() <= slider.getMinimum()) {
1705 return false;
1706 }
1707
1708 return true;
1709 }
1710
1711 /**
1712 * Set the models value to the position of the top/left
1713 * of the thumb relative to the origin of the track.
1714 */
1715 public void mouseDragged(MouseEvent e) {
1716 int thumbMiddle = 0;
1717
1718 if (!slider.isEnabled()) {
1719 return;
1720 }
1721
1722 currentMouseX = e.getX();
1723 currentMouseY = e.getY();
1724
1725 if (!isDragging) {
1726 return;
1727 }
1728
1729 slider.setValueIsAdjusting(true);
1730
1731 switch (slider.getOrientation()) {
1732 case JSlider.VERTICAL:
1733 int halfThumbHeight = thumbRect.height / 2;
1734 int thumbTop = e.getY() - offset;
1735 int trackTop = trackRect.y;
1736 int trackBottom = trackRect.y + (trackRect.height - 1);
1737 int vMax = yPositionForValue(slider.getMaximum()
1738 - slider.getExtent());
1739
1740 if (drawInverted()) {
1741 trackBottom = vMax;
1742 } else {
1743 trackTop = vMax;
1744 }
1745 thumbTop = Math.max(thumbTop, trackTop
1746 - halfThumbHeight);
1747 thumbTop = Math.min(thumbTop, trackBottom
1748 - halfThumbHeight);
1749
1750 setThumbLocation(thumbRect.x, thumbTop);
1751
1752 thumbMiddle = thumbTop + halfThumbHeight;
1753 slider.setValue(valueForYPosition(thumbMiddle));
1754 break;
1755 case JSlider.HORIZONTAL:
1756 int halfThumbWidth = thumbRect.width / 2;
1757 int thumbLeft = e.getX() - offset;
1758 int trackLeft = trackRect.x;
1759 int trackRight = trackRect.x + (trackRect.width - 1);
1760 int hMax = xPositionForValue(slider.getMaximum()
1761 - slider.getExtent());
1762
1763 if (drawInverted()) {
1764 trackLeft = hMax;
1765 } else {
1766 trackRight = hMax;
1767 }
1768 thumbLeft = Math.max(thumbLeft, trackLeft
1769 - halfThumbWidth);
1770 thumbLeft = Math.min(thumbLeft, trackRight
1771 - halfThumbWidth);
1772
1773 setThumbLocation(thumbLeft, thumbRect.y);
1774
1775 thumbMiddle = thumbLeft + halfThumbWidth;
1776 slider.setValue(valueForXPosition(thumbMiddle));
1777 break;
1778 default:
1779 return;
1780 }
1781 }
1782
1783 public void mouseMoved(MouseEvent e) {
1784 }
1785 }
1786
1787 /**
1788 * Scroll-event listener.
1789 *
1790 * This class should be treated as a "protected" inner class.
1791 * Instantiate it only within subclasses of <Foo>.
1792 */
1793 public class ScrollListener implements ActionListener {
1794 // changed this class to public to avoid bogus IllegalAccessException
1795 // bug in InternetExplorer browser. It was protected. Work around
1796 // for 4109432
1797 int direction = POSITIVE_SCROLL;
1798 boolean useBlockIncrement;
1799
1800 public ScrollListener() {
1801 direction = POSITIVE_SCROLL;
1802 useBlockIncrement = true;
1803 }
1804
1805 public ScrollListener(int dir, boolean block) {
1806 direction = dir;
1807 useBlockIncrement = block;
1808 }
1809
1810 public void setDirection(int direction) {
1811 this .direction = direction;
1812 }
1813
1814 public void setScrollByBlock(boolean block) {
1815 this .useBlockIncrement = block;
1816 }
1817
1818 public void actionPerformed(ActionEvent e) {
1819 if (useBlockIncrement) {
1820 scrollByBlock(direction);
1821 } else {
1822 scrollByUnit(direction);
1823 }
1824 if (!trackListener.shouldScroll(direction)) {
1825 ((Timer) e.getSource()).stop();
1826 }
1827 }
1828 }
1829
1830 /**
1831 * Listener for resizing events.
1832 * <p>
1833 * This class should be treated as a "protected" inner class.
1834 * Instantiate it only within subclasses of <Foo>.
1835 */
1836 public class ComponentHandler extends ComponentAdapter {
1837 // NOTE: This class exists only for backward compatability. All
1838 // its functionality has been moved into Handler. If you need to add
1839 // new functionality add it to the Handler, but make sure this
1840 // class calls into the Handler.
1841 public void componentResized(ComponentEvent e) {
1842 getHandler().componentResized(e);
1843 }
1844 };
1845
1846 /**
1847 * Focus-change listener.
1848 * <p>
1849 * This class should be treated as a "protected" inner class.
1850 * Instantiate it only within subclasses of <Foo>.
1851 */
1852 public class FocusHandler implements FocusListener {
1853 // NOTE: This class exists only for backward compatability. All
1854 // its functionality has been moved into Handler. If you need to add
1855 // new functionality add it to the Handler, but make sure this
1856 // class calls into the Handler.
1857 public void focusGained(FocusEvent e) {
1858 getHandler().focusGained(e);
1859 }
1860
1861 public void focusLost(FocusEvent e) {
1862 getHandler().focusLost(e);
1863 }
1864 }
1865
1866 /**
1867 * As of Java 2 platform v1.3 this undocumented class is no longer used.
1868 * The recommended approach to creating bindings is to use a
1869 * combination of an <code>ActionMap</code>, to contain the action,
1870 * and an <code>InputMap</code> to contain the mapping from KeyStroke
1871 * to action description. The InputMap is is usually described in the
1872 * LookAndFeel tables.
1873 * <p>
1874 * Please refer to the key bindings specification for further details.
1875 * <p>
1876 * This class should be treated as a "protected" inner class.
1877 * Instantiate it only within subclasses of <Foo>.
1878 */
1879 public class ActionScroller extends AbstractAction {
1880 // NOTE: This class exists only for backward compatability. All
1881 // its functionality has been moved into Actions. If you need to add
1882 // new functionality add it to the Actions, but make sure this
1883 // class calls into the Actions.
1884 int dir;
1885 boolean block;
1886 JSlider slider;
1887
1888 public ActionScroller(JSlider slider, int dir, boolean block) {
1889 this .dir = dir;
1890 this .block = block;
1891 this .slider = slider;
1892 }
1893
1894 public void actionPerformed(ActionEvent e) {
1895 SHARED_ACTION
1896 .scroll(slider, BasicSliderUI.this , dir, block);
1897 }
1898
1899 public boolean isEnabled() {
1900 boolean b = true;
1901 if (slider != null) {
1902 b = slider.isEnabled();
1903 }
1904 return b;
1905 }
1906
1907 };
1908
1909 /**
1910 * A static version of the above.
1911 */
1912 static class SharedActionScroller extends AbstractAction {
1913 // NOTE: This class exists only for backward compatability. All
1914 // its functionality has been moved into Actions. If you need to add
1915 // new functionality add it to the Actions, but make sure this
1916 // class calls into the Actions.
1917 int dir;
1918 boolean block;
1919
1920 public SharedActionScroller(int dir, boolean block) {
1921 this .dir = dir;
1922 this .block = block;
1923 }
1924
1925 public void actionPerformed(ActionEvent evt) {
1926 JSlider slider = (JSlider) evt.getSource();
1927 BasicSliderUI ui = (BasicSliderUI) BasicLookAndFeel
1928 .getUIOfType(slider.getUI(), BasicSliderUI.class);
1929 if (ui == null) {
1930 return;
1931 }
1932 SHARED_ACTION.scroll(slider, ui, dir, block);
1933 }
1934 }
1935
1936 private static class Actions extends UIAction {
1937 public static final String POSITIVE_UNIT_INCREMENT = "positiveUnitIncrement";
1938 public static final String POSITIVE_BLOCK_INCREMENT = "positiveBlockIncrement";
1939 public static final String NEGATIVE_UNIT_INCREMENT = "negativeUnitIncrement";
1940 public static final String NEGATIVE_BLOCK_INCREMENT = "negativeBlockIncrement";
1941 public static final String MIN_SCROLL_INCREMENT = "minScroll";
1942 public static final String MAX_SCROLL_INCREMENT = "maxScroll";
1943
1944 Actions() {
1945 super (null);
1946 }
1947
1948 public Actions(String name) {
1949 super (name);
1950 }
1951
1952 public void actionPerformed(ActionEvent evt) {
1953 JSlider slider = (JSlider) evt.getSource();
1954 BasicSliderUI ui = (BasicSliderUI) BasicLookAndFeel
1955 .getUIOfType(slider.getUI(), BasicSliderUI.class);
1956 String name = getName();
1957
1958 if (ui == null) {
1959 return;
1960 }
1961 if (POSITIVE_UNIT_INCREMENT == name) {
1962 scroll(slider, ui, POSITIVE_SCROLL, false);
1963 } else if (NEGATIVE_UNIT_INCREMENT == name) {
1964 scroll(slider, ui, NEGATIVE_SCROLL, false);
1965 } else if (POSITIVE_BLOCK_INCREMENT == name) {
1966 scroll(slider, ui, POSITIVE_SCROLL, true);
1967 } else if (NEGATIVE_BLOCK_INCREMENT == name) {
1968 scroll(slider, ui, NEGATIVE_SCROLL, true);
1969 } else if (MIN_SCROLL_INCREMENT == name) {
1970 scroll(slider, ui, MIN_SCROLL, false);
1971 } else if (MAX_SCROLL_INCREMENT == name) {
1972 scroll(slider, ui, MAX_SCROLL, false);
1973 }
1974 }
1975
1976 private void scroll(JSlider slider, BasicSliderUI ui,
1977 int direction, boolean isBlock) {
1978 boolean invert = slider.getInverted();
1979
1980 if (direction == NEGATIVE_SCROLL
1981 || direction == POSITIVE_SCROLL) {
1982 if (invert) {
1983 direction = (direction == POSITIVE_SCROLL) ? NEGATIVE_SCROLL
1984 : POSITIVE_SCROLL;
1985 }
1986
1987 if (isBlock) {
1988 ui.scrollByBlock(direction);
1989 } else {
1990 ui.scrollByUnit(direction);
1991 }
1992 } else { // MIN or MAX
1993 if (invert) {
1994 direction = (direction == MIN_SCROLL) ? MAX_SCROLL
1995 : MIN_SCROLL;
1996 }
1997
1998 slider.setValue((direction == MIN_SCROLL) ? slider
1999 .getMinimum() : slider.getMaximum());
2000 }
2001 }
2002 }
2003 }
|