/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.plaf.metal;
import javax.swing.plaf.basic.BasicSliderUI;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Color;
import java.beans.*;
import javax.swing.*;
import javax.swing.plaf.*;
A Java L&F implementation of SliderUI.
Warning:
Serialized objects of this class will not be compatible with
future Swing releases. The current serialization support is
appropriate for short term storage or RMI between applications running
the same version of Swing. As of 1.4, support for long term storage
of all JavaBeans™
has been added to the java.beans
package. Please see XMLEncoder
.
Author: Tom Santos
/**
* A Java L&F implementation of SliderUI.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans™
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Tom Santos
*/
public class MetalSliderUI extends BasicSliderUI {
protected final int TICK_BUFFER = 4;
protected boolean filledSlider = false;
// NOTE: these next five variables are currently unused.
protected static Color thumbColor;
protected static Color highlightColor;
protected static Color darkShadowColor;
protected static int trackWidth;
protected static int tickLength;
private int safeLength;
A default horizontal thumb Icon
. This field might not be
used. To change the Icon
used by this delegate directly set it
using the Slider.horizontalThumbIcon
UIManager property.
/**
* A default horizontal thumb <code>Icon</code>. This field might not be
* used. To change the <code>Icon</code> used by this delegate directly set it
* using the <code>Slider.horizontalThumbIcon</code> UIManager property.
*/
protected static Icon horizThumbIcon;
A default vertical thumb Icon
. This field might not be
used. To change the Icon
used by this delegate directly set it
using the Slider.verticalThumbIcon
UIManager property.
/**
* A default vertical thumb <code>Icon</code>. This field might not be
* used. To change the <code>Icon</code> used by this delegate directly set it
* using the <code>Slider.verticalThumbIcon</code> UIManager property.
*/
protected static Icon vertThumbIcon;
private static Icon SAFE_HORIZ_THUMB_ICON;
private static Icon SAFE_VERT_THUMB_ICON;
protected final String SLIDER_FILL = "JSlider.isFilled";
public static ComponentUI createUI(JComponent c) {
return new MetalSliderUI();
}
public MetalSliderUI() {
super( null );
}
private static Icon getHorizThumbIcon() {
if (System.getSecurityManager() != null) {
return SAFE_HORIZ_THUMB_ICON;
} else {
return horizThumbIcon;
}
}
private static Icon getVertThumbIcon() {
if (System.getSecurityManager() != null) {
return SAFE_VERT_THUMB_ICON;
} else {
return vertThumbIcon;
}
}
public void installUI( JComponent c ) {
trackWidth = ((Integer)UIManager.get( "Slider.trackWidth" )).intValue();
tickLength = safeLength = ((Integer)UIManager.get( "Slider.majorTickLength" )).intValue();
horizThumbIcon = SAFE_HORIZ_THUMB_ICON =
UIManager.getIcon( "Slider.horizontalThumbIcon" );
vertThumbIcon = SAFE_VERT_THUMB_ICON =
UIManager.getIcon( "Slider.verticalThumbIcon" );
super.installUI( c );
thumbColor = UIManager.getColor("Slider.thumb");
highlightColor = UIManager.getColor("Slider.highlight");
darkShadowColor = UIManager.getColor("Slider.darkShadow");
scrollListener.setScrollByBlock( false );
prepareFilledSliderField();
}
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
return new MetalPropertyListener();
}
protected class MetalPropertyListener extends BasicSliderUI.PropertyChangeHandler {
public void propertyChange( PropertyChangeEvent e ) { // listen for slider fill
super.propertyChange( e );
if (e.getPropertyName().equals(SLIDER_FILL)) {
prepareFilledSliderField();
}
}
}
private void prepareFilledSliderField() {
// Use true for Ocean theme
filledSlider = MetalLookAndFeel.usingOcean();
Object sliderFillProp = slider.getClientProperty(SLIDER_FILL);
if (sliderFillProp != null) {
filledSlider = ((Boolean) sliderFillProp).booleanValue();
}
}
public void paintThumb(Graphics g) {
Rectangle knobBounds = thumbRect;
g.translate( knobBounds.x, knobBounds.y );
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
getHorizThumbIcon().paintIcon( slider, g, 0, 0 );
}
else {
getVertThumbIcon().paintIcon( slider, g, 0, 0 );
}
g.translate( -knobBounds.x, -knobBounds.y );
}
Returns a rectangle enclosing the track that will be painted.
/**
* Returns a rectangle enclosing the track that will be painted.
*/
private Rectangle getPaintTrackRect() {
int trackLeft = 0, trackRight, trackTop = 0, trackBottom;
if (slider.getOrientation() == JSlider.HORIZONTAL) {
trackBottom = (trackRect.height - 1) - getThumbOverhang();
trackTop = trackBottom - (getTrackWidth() - 1);
trackRight = trackRect.width - 1;
}
else {
if (MetalUtils.isLeftToRight(slider)) {
trackLeft = (trackRect.width - getThumbOverhang()) -
getTrackWidth();
trackRight = (trackRect.width - getThumbOverhang()) - 1;
}
else {
trackLeft = getThumbOverhang();
trackRight = getThumbOverhang() + getTrackWidth() - 1;
}
trackBottom = trackRect.height - 1;
}
return new Rectangle(trackRect.x + trackLeft, trackRect.y + trackTop,
trackRight - trackLeft, trackBottom - trackTop);
}
public void paintTrack(Graphics g) {
if (MetalLookAndFeel.usingOcean()) {
oceanPaintTrack(g);
return;
}
Color trackColor = !slider.isEnabled() ? MetalLookAndFeel.getControlShadow() :
slider.getForeground();
boolean leftToRight = MetalUtils.isLeftToRight(slider);
g.translate( trackRect.x, trackRect.y );
int trackLeft = 0;
int trackTop = 0;
int trackRight;
int trackBottom;
// Draw the track
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
trackBottom = (trackRect.height - 1) - getThumbOverhang();
trackTop = trackBottom - (getTrackWidth() - 1);
trackRight = trackRect.width - 1;
}
else {
if (leftToRight) {
trackLeft = (trackRect.width - getThumbOverhang()) -
getTrackWidth();
trackRight = (trackRect.width - getThumbOverhang()) - 1;
}
else {
trackLeft = getThumbOverhang();
trackRight = getThumbOverhang() + getTrackWidth() - 1;
}
trackBottom = trackRect.height - 1;
}
if ( slider.isEnabled() ) {
g.setColor( MetalLookAndFeel.getControlDarkShadow() );
g.drawRect( trackLeft, trackTop,
(trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1 );
g.setColor( MetalLookAndFeel.getControlHighlight() );
g.drawLine( trackLeft + 1, trackBottom, trackRight, trackBottom );
g.drawLine( trackRight, trackTop + 1, trackRight, trackBottom );
g.setColor( MetalLookAndFeel.getControlShadow() );
g.drawLine( trackLeft + 1, trackTop + 1, trackRight - 2, trackTop + 1 );
g.drawLine( trackLeft + 1, trackTop + 1, trackLeft + 1, trackBottom - 2 );
}
else {
g.setColor( MetalLookAndFeel.getControlShadow() );
g.drawRect( trackLeft, trackTop,
(trackRight - trackLeft) - 1, (trackBottom - trackTop) - 1 );
}
// Draw the fill
if ( filledSlider ) {
int middleOfThumb;
int fillTop;
int fillLeft;
int fillBottom;
int fillRight;
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
middleOfThumb = thumbRect.x + (thumbRect.width / 2);
middleOfThumb -= trackRect.x; // To compensate for the g.translate()
fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2;
if ( !drawInverted() ) {
fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
fillRight = middleOfThumb;
}
else {
fillLeft = middleOfThumb;
fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2;
}
}
else {
middleOfThumb = thumbRect.y + (thumbRect.height / 2);
middleOfThumb -= trackRect.y; // To compensate for the g.translate()
fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
fillRight = !slider.isEnabled() ? trackRight - 1 : trackRight - 2;
if ( !drawInverted() ) {
fillTop = middleOfThumb;
fillBottom = !slider.isEnabled() ? trackBottom - 1 : trackBottom - 2;
}
else {
fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
fillBottom = middleOfThumb;
}
}
if ( slider.isEnabled() ) {
g.setColor( slider.getBackground() );
g.drawLine( fillLeft, fillTop, fillRight, fillTop );
g.drawLine( fillLeft, fillTop, fillLeft, fillBottom );
g.setColor( MetalLookAndFeel.getControlShadow() );
g.fillRect( fillLeft + 1, fillTop + 1,
fillRight - fillLeft, fillBottom - fillTop );
}
else {
g.setColor( MetalLookAndFeel.getControlShadow() );
g.fillRect(fillLeft, fillTop, fillRight - fillLeft, fillBottom - fillTop);
}
}
g.translate( -trackRect.x, -trackRect.y );
}
private void oceanPaintTrack(Graphics g) {
boolean leftToRight = MetalUtils.isLeftToRight(slider);
boolean drawInverted = drawInverted();
Color sliderAltTrackColor = (Color)UIManager.get(
"Slider.altTrackColor");
// Translate to the origin of the painting rectangle
Rectangle paintRect = getPaintTrackRect();
g.translate(paintRect.x, paintRect.y);
// Width and height of the painting rectangle.
int w = paintRect.width;
int h = paintRect.height;
if (slider.getOrientation() == JSlider.HORIZONTAL) {
int middleOfThumb = thumbRect.x + thumbRect.width / 2 - paintRect.x;
if (slider.isEnabled()) {
int fillMinX;
int fillMaxX;
if (middleOfThumb > 0) {
g.setColor(drawInverted ? MetalLookAndFeel.getControlDarkShadow() :
MetalLookAndFeel.getPrimaryControlDarkShadow());
g.drawRect(0, 0, middleOfThumb - 1, h - 1);
}
if (middleOfThumb < w) {
g.setColor(drawInverted ? MetalLookAndFeel.getPrimaryControlDarkShadow() :
MetalLookAndFeel.getControlDarkShadow());
g.drawRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1);
}
if (filledSlider) {
g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
if (drawInverted) {
fillMinX = middleOfThumb;
fillMaxX = w - 2;
g.drawLine(1, 1, middleOfThumb, 1);
} else {
fillMinX = 1;
fillMaxX = middleOfThumb;
g.drawLine(middleOfThumb, 1, w - 1, 1);
}
if (h == 6) {
g.setColor(MetalLookAndFeel.getWhite());
g.drawLine(fillMinX, 1, fillMaxX, 1);
g.setColor(sliderAltTrackColor);
g.drawLine(fillMinX, 2, fillMaxX, 2);
g.setColor(MetalLookAndFeel.getControlShadow());
g.drawLine(fillMinX, 3, fillMaxX, 3);
g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
g.drawLine(fillMinX, 4, fillMaxX, 4);
}
}
} else {
g.setColor(MetalLookAndFeel.getControlShadow());
if (middleOfThumb > 0) {
if (!drawInverted && filledSlider) {
g.fillRect(0, 0, middleOfThumb - 1, h - 1);
} else {
g.drawRect(0, 0, middleOfThumb - 1, h - 1);
}
}
if (middleOfThumb < w) {
if (drawInverted && filledSlider) {
g.fillRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1);
} else {
g.drawRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1);
}
}
}
} else {
int middleOfThumb = thumbRect.y + (thumbRect.height / 2) - paintRect.y;
if (slider.isEnabled()) {
int fillMinY;
int fillMaxY;
if (middleOfThumb > 0) {
g.setColor(drawInverted ? MetalLookAndFeel.getPrimaryControlDarkShadow() :
MetalLookAndFeel.getControlDarkShadow());
g.drawRect(0, 0, w - 1, middleOfThumb - 1);
}
if (middleOfThumb < h) {
g.setColor(drawInverted ? MetalLookAndFeel.getControlDarkShadow() :
MetalLookAndFeel.getPrimaryControlDarkShadow());
g.drawRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1);
}
if (filledSlider) {
g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
if (drawInverted()) {
fillMinY = 1;
fillMaxY = middleOfThumb;
if (leftToRight) {
g.drawLine(1, middleOfThumb, 1, h - 1);
} else {
g.drawLine(w - 2, middleOfThumb, w - 2, h - 1);
}
} else {
fillMinY = middleOfThumb;
fillMaxY = h - 2;
if (leftToRight) {
g.drawLine(1, 1, 1, middleOfThumb);
} else {
g.drawLine(w - 2, 1, w - 2, middleOfThumb);
}
}
if (w == 6) {
g.setColor(leftToRight ? MetalLookAndFeel.getWhite() : MetalLookAndFeel.getPrimaryControlShadow());
g.drawLine(1, fillMinY, 1, fillMaxY);
g.setColor(leftToRight ? sliderAltTrackColor : MetalLookAndFeel.getControlShadow());
g.drawLine(2, fillMinY, 2, fillMaxY);
g.setColor(leftToRight ? MetalLookAndFeel.getControlShadow() : sliderAltTrackColor);
g.drawLine(3, fillMinY, 3, fillMaxY);
g.setColor(leftToRight ? MetalLookAndFeel.getPrimaryControlShadow() : MetalLookAndFeel.getWhite());
g.drawLine(4, fillMinY, 4, fillMaxY);
}
}
} else {
g.setColor(MetalLookAndFeel.getControlShadow());
if (middleOfThumb > 0) {
if (drawInverted && filledSlider) {
g.fillRect(0, 0, w - 1, middleOfThumb - 1);
} else {
g.drawRect(0, 0, w - 1, middleOfThumb - 1);
}
}
if (middleOfThumb < h) {
if (!drawInverted && filledSlider) {
g.fillRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1);
} else {
g.drawRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1);
}
}
}
}
g.translate(-paintRect.x, -paintRect.y);
}
public void paintFocus(Graphics g) {
}
protected Dimension getThumbSize() {
Dimension size = new Dimension();
if ( slider.getOrientation() == JSlider.VERTICAL ) {
size.width = getVertThumbIcon().getIconWidth();
size.height = getVertThumbIcon().getIconHeight();
}
else {
size.width = getHorizThumbIcon().getIconWidth();
size.height = getHorizThumbIcon().getIconHeight();
}
return size;
}
Gets the height of the tick area for horizontal sliders and the width of the
tick area for vertical sliders. BasicSliderUI uses the returned value to
determine the tick area rectangle.
/**
* Gets the height of the tick area for horizontal sliders and the width of the
* tick area for vertical sliders. BasicSliderUI uses the returned value to
* determine the tick area rectangle.
*/
public int getTickLength() {
return slider.getOrientation() == JSlider.HORIZONTAL ? safeLength + TICK_BUFFER + 1 :
safeLength + TICK_BUFFER + 3;
}
Returns the shorter dimension of the track.
/**
* Returns the shorter dimension of the track.
*/
protected int getTrackWidth() {
// This strange calculation is here to keep the
// track in proportion to the thumb.
final double kIdealTrackWidth = 7.0;
final double kIdealThumbHeight = 16.0;
final double kWidthScalar = kIdealTrackWidth / kIdealThumbHeight;
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
return (int)(kWidthScalar * thumbRect.height);
}
else {
return (int)(kWidthScalar * thumbRect.width);
}
}
Returns the longer dimension of the slide bar. (The slide bar is only the
part that runs directly under the thumb)
/**
* Returns the longer dimension of the slide bar. (The slide bar is only the
* part that runs directly under the thumb)
*/
protected int getTrackLength() {
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
return trackRect.width;
}
return trackRect.height;
}
Returns the amount that the thumb goes past the slide bar.
/**
* Returns the amount that the thumb goes past the slide bar.
*/
protected int getThumbOverhang() {
return (int)(getThumbSize().getHeight()-getTrackWidth())/2;
}
protected void scrollDueToClickInTrack( int dir ) {
scrollByUnit( dir );
}
protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
g.drawLine( x, TICK_BUFFER, x, TICK_BUFFER + (safeLength / 2) );
}
protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
g.drawLine( x, TICK_BUFFER , x, TICK_BUFFER + (safeLength - 1) );
}
protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
if (MetalUtils.isLeftToRight(slider)) {
g.drawLine( TICK_BUFFER, y, TICK_BUFFER + (safeLength / 2), y );
}
else {
g.drawLine( 0, y, safeLength/2, y );
}
}
protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
g.setColor( slider.isEnabled() ? slider.getForeground() : MetalLookAndFeel.getControlShadow() );
if (MetalUtils.isLeftToRight(slider)) {
g.drawLine( TICK_BUFFER, y, TICK_BUFFER + safeLength, y );
}
else {
g.drawLine( 0, y, safeLength, y );
}
}
}