/*
* Copyright (c) 2002, 2010, 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.synth;
import java.awt.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
/**
* Provides the Synth L&F UI delegate for
* {@link javax.swing.JScrollBar}.
*
* @author Scott Violet
* @since 1.7
*/
public class SynthScrollBarUI extends BasicScrollBarUI
implements PropertyChangeListener, SynthUI {
private SynthStyle style;
private SynthStyle thumbStyle;
private SynthStyle trackStyle;
private boolean validMinimumThumbSize;
public static ComponentUI createUI(JComponent c) {
return new SynthScrollBarUI();
}
/**
* @inheritDoc
*/
@Override
protected void installDefaults() {
super.installDefaults();
trackHighlight = NO_HIGHLIGHT;
if (scrollbar.getLayout() == null ||
(scrollbar.getLayout() instanceof UIResource)) {
scrollbar.setLayout(this);
}
configureScrollBarColors();
updateStyle(scrollbar);
}
/**
* @inheritDoc
*/
@Override
protected void configureScrollBarColors() {
}
private void updateStyle(JScrollBar c) {
SynthStyle oldStyle = style;
SynthContext context = getContext(c, ENABLED);
style = SynthLookAndFeel.updateStyle(context, this);
if (style != oldStyle) {
scrollBarWidth = style.getInt(context,"ScrollBar.thumbHeight", 14);
minimumThumbSize = (Dimension)style.get(context,
"ScrollBar.minimumThumbSize");
if (minimumThumbSize == null) {
minimumThumbSize = new Dimension();
validMinimumThumbSize = false;
}
else {
validMinimumThumbSize = true;
}
maximumThumbSize = (Dimension)style.get(context,
"ScrollBar.maximumThumbSize");
if (maximumThumbSize == null) {
maximumThumbSize = new Dimension(4096, 4097);
}
incrGap = style.getInt(context, "ScrollBar.incrementButtonGap", 0);
decrGap = style.getInt(context, "ScrollBar.decrementButtonGap", 0);
// handle scaling for sizeVarients for special case components. The
// key "JComponent.sizeVariant" scales for large/small/mini
// components are based on Apples LAF
String scaleKey = (String)scrollbar.getClientProperty(
"JComponent.sizeVariant");
if (scaleKey != null){
if ("large".equals(scaleKey)){
scrollBarWidth *= 1.15;
incrGap *= 1.15;
decrGap *= 1.15;
} else if ("small".equals(scaleKey)){
scrollBarWidth *= 0.857;
incrGap *= 0.857;
decrGap *= 0.857;
} else if ("mini".equals(scaleKey)){
scrollBarWidth *= 0.714;
incrGap *= 0.714;
decrGap *= 0.714;
}
}
if (oldStyle != null) {
uninstallKeyboardActions();
installKeyboardActions();
}
}
context.dispose();
context = getContext(c, Region.SCROLL_BAR_TRACK, ENABLED);
trackStyle = SynthLookAndFeel.updateStyle(context, this);
context.dispose();
context = getContext(c, Region.SCROLL_BAR_THUMB, ENABLED);
thumbStyle = SynthLookAndFeel.updateStyle(context, this);
context.dispose();
}
/**
* @inheritDoc
*/
@Override
protected void installListeners() {
super.installListeners();
scrollbar.addPropertyChangeListener(this);
}
/**
* @inheritDoc
*/
@Override
protected void uninstallListeners() {
super.uninstallListeners();
scrollbar.removePropertyChangeListener(this);
}
/**
* @inheritDoc
*/
@Override
protected void uninstallDefaults(){
SynthContext context = getContext(scrollbar, ENABLED);
style.uninstallDefaults(context);
context.dispose();
style = null;
context = getContext(scrollbar, Region.SCROLL_BAR_TRACK, ENABLED);
trackStyle.uninstallDefaults(context);
context.dispose();
trackStyle = null;
context = getContext(scrollbar, Region.SCROLL_BAR_THUMB, ENABLED);
thumbStyle.uninstallDefaults(context);
context.dispose();
thumbStyle = null;
super.uninstallDefaults();
}
/**
* @inheritDoc
*/
@Override
public SynthContext getContext(JComponent c) {
return getContext(c, SynthLookAndFeel.getComponentState(c));
}
private SynthContext getContext(JComponent c, int state) {
return SynthContext.getContext(SynthContext.class, c,
SynthLookAndFeel.getRegion(c), style, state);
}
private SynthContext getContext(JComponent c, Region region) {
return getContext(c, region, getComponentState(c, region));
}
private SynthContext getContext(JComponent c, Region region, int state) {
SynthStyle style = trackStyle;
if (region == Region.SCROLL_BAR_THUMB) {
style = thumbStyle;
}
return SynthContext.getContext(SynthContext.class, c, region, style,
state);
}
private int getComponentState(JComponent c, Region region) {
if (region == Region.SCROLL_BAR_THUMB && isThumbRollover() &&
c.isEnabled()) {
return MOUSE_OVER;
}
return SynthLookAndFeel.getComponentState(c);
}
/**
* @inheritDoc
*/
@Override
public boolean getSupportsAbsolutePositioning() {
SynthContext context = getContext(scrollbar);
boolean value = style.getBoolean(context,
"ScrollBar.allowsAbsolutePositioning", false);
context.dispose();
return value;
}
/**
* Notifies this UI delegate to repaint the specified component.
* This method paints the component background, then calls
* the {@link #paint(SynthContext,Graphics)} method.
*
* <p>In general, this method does not need to be overridden by subclasses.
* All Look and Feel rendering code should reside in the {@code paint} method.
*
* @param g the {@code Graphics} object used for painting
* @param c the component being painted
* @see #paint(SynthContext,Graphics)
*/
@Override
public void update(Graphics g, JComponent c) {
SynthContext context = getContext(c);
SynthLookAndFeel.update(context, g);
context.getPainter().paintScrollBarBackground(context,
g, 0, 0, c.getWidth(), c.getHeight(),
scrollbar.getOrientation());
paint(context, g);
context.dispose();
}
/**
* Paints the specified component according to the Look and Feel.
* <p>This method is not used by Synth Look and Feel.
* Painting is handled by the {@link #paint(SynthContext,Graphics)} method.
*
* @param g the {@code Graphics} object used for painting
* @param c the component being painted
* @see #paint(SynthContext,Graphics)
*/
@Override
public void paint(Graphics g, JComponent c) {
SynthContext context = getContext(c);
paint(context, g);
context.dispose();
}
/**
* Paints the specified component.
*
* @param context context for the component being painted
* @param g the {@code Graphics} object used for painting
* @see #update(Graphics,JComponent)
*/
protected void paint(SynthContext context, Graphics g) {
SynthContext subcontext = getContext(scrollbar,
Region.SCROLL_BAR_TRACK);
paintTrack(subcontext, g, getTrackBounds());
subcontext.dispose();
subcontext = getContext(scrollbar, Region.SCROLL_BAR_THUMB);
paintThumb(subcontext, g, getThumbBounds());
subcontext.dispose();
}
/**
* @inheritDoc
*/
@Override
public void paintBorder(SynthContext context, Graphics g, int x,
int y, int w, int h) {
context.getPainter().paintScrollBarBorder(context, g, x, y, w, h,
scrollbar.getOrientation());
}
/**
* Paints the scrollbar track.
*
* @param context context for the component being painted
* @param g {@code Graphics} object used for painting
* @param trackBounds bounding box for the track
*/
protected void paintTrack(SynthContext context, Graphics g,
Rectangle trackBounds) {
SynthLookAndFeel.updateSubregion(context, g, trackBounds);
context.getPainter().paintScrollBarTrackBackground(context, g, trackBounds.x,
trackBounds.y, trackBounds.width, trackBounds.height,
scrollbar.getOrientation());
context.getPainter().paintScrollBarTrackBorder(context, g, trackBounds.x,
trackBounds.y, trackBounds.width, trackBounds.height,
scrollbar.getOrientation());
}
/**
* Paints the scrollbar thumb.
*
* @param context context for the component being painted
* @param g {@code Graphics} object used for painting
* @param thumbBounds bounding box for the thumb
*/
protected void paintThumb(SynthContext context, Graphics g,
Rectangle thumbBounds) {
SynthLookAndFeel.updateSubregion(context, g, thumbBounds);
int orientation = scrollbar.getOrientation();
context.getPainter().paintScrollBarThumbBackground(context, g, thumbBounds.x,
thumbBounds.y, thumbBounds.width, thumbBounds.height,
orientation);
context.getPainter().paintScrollBarThumbBorder(context, g, thumbBounds.x,
thumbBounds.y, thumbBounds.width, thumbBounds.height,
orientation);
}
/**
* A vertical scrollbar's preferred width is the maximum of
* preferred widths of the (non <code>null</code>)
* increment/decrement buttons,
* and the minimum width of the thumb. The preferred height is the
* sum of the preferred heights of the same parts. The basis for
* the preferred size of a horizontal scrollbar is similar.
* <p>
* The <code>preferredSize</code> is only computed once, subsequent
* calls to this method just return a cached size.
*
* @param c the <code>JScrollBar</code> that's delegating this method to us
* @return the preferred size of a Basic JScrollBar
* @see #getMaximumSize
* @see #getMinimumSize
*/
@Override
public Dimension getPreferredSize(JComponent c) {
Insets insets = c.getInsets();
return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
? new Dimension(scrollBarWidth + insets.left + insets.right, 48)
: new Dimension(48, scrollBarWidth + insets.top + insets.bottom);
}
/**
* @inheritDoc
*/
@Override
protected Dimension getMinimumThumbSize() {
if (!validMinimumThumbSize) {
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
minimumThumbSize.width = scrollBarWidth;
minimumThumbSize.height = 7;
} else {
minimumThumbSize.width = 7;
minimumThumbSize.height = scrollBarWidth;
}
}
return minimumThumbSize;
}
/**
* @inheritDoc
*/
@Override
protected JButton createDecreaseButton(int orientation) {
SynthArrowButton synthArrowButton = new SynthArrowButton(orientation) {
@Override
public boolean contains(int x, int y) {
if (decrGap < 0) { //there is an overlap between the track and button
int width = getWidth();
int height = getHeight();
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
//adjust the height by decrGap
//Note: decrGap is negative!
height += decrGap;
} else {
//adjust the width by decrGap
//Note: decrGap is negative!
width += decrGap;
}
return (x >= 0) && (x < width) && (y >= 0) && (y < height);
}
return super.contains(x, y);
}
};
synthArrowButton.setName("ScrollBar.button");
return synthArrowButton;
}
/**
* @inheritDoc
*/
@Override
protected JButton createIncreaseButton(int orientation) {
SynthArrowButton synthArrowButton = new SynthArrowButton(orientation) {
@Override
public boolean contains(int x, int y) {
if (incrGap < 0) { //there is an overlap between the track and button
int width = getWidth();
int height = getHeight();
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
//adjust the height and y by incrGap
//Note: incrGap is negative!
height += incrGap;
y += incrGap;
} else {
//adjust the width and x by incrGap
//Note: incrGap is negative!
width += incrGap;
x += incrGap;
}
return (x >= 0) && (x < width) && (y >= 0) && (y < height);
}
return super.contains(x, y);
}
};
synthArrowButton.setName("ScrollBar.button");
return synthArrowButton;
}
/**
* @inheritDoc
*/
@Override
protected void setThumbRollover(boolean active) {
if (isThumbRollover() != active) {
scrollbar.repaint(getThumbBounds());
super.setThumbRollover(active);
}
}
private void updateButtonDirections() {
int orient = scrollbar.getOrientation();
if (scrollbar.getComponentOrientation().isLeftToRight()) {
((SynthArrowButton)incrButton).setDirection(
orient == HORIZONTAL? EAST : SOUTH);
((SynthArrowButton)decrButton).setDirection(
orient == HORIZONTAL? WEST : NORTH);
}
else {
((SynthArrowButton)incrButton).setDirection(
orient == HORIZONTAL? WEST : SOUTH);
((SynthArrowButton)decrButton).setDirection(
orient == HORIZONTAL ? EAST : NORTH);
}
}
//
// PropertyChangeListener
//
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if (SynthLookAndFeel.shouldUpdateStyle(e)) {
updateStyle((JScrollBar)e.getSource());
}
if ("orientation" == propertyName) {
updateButtonDirections();
}
else if ("componentOrientation" == propertyName) {
updateButtonDirections();
}
}
}