ch.randelshofer.quaqua.QuaquaScrollBarUI Maven / Gradle / Ivy
Show all versions of Quaqua Show documentation
/*
* @(#)QuaquaScrollBarUI.java
*
* Copyright (c) 2005-2010 Werner Randelshofer, Immensee, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package ch.randelshofer.quaqua;
import ch.randelshofer.quaqua.util.Debug;
import ch.randelshofer.quaqua.util.Methods;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
/**
* QuaquaScrollBarUI.
*
* @author Werner Randelshofer
* @version $Id: QuaquaScrollBarUI.java 361 2010-11-21 11:19:20Z wrandelshofer $
*/
public class QuaquaScrollBarUI extends BasicScrollBarUI {
protected Dimension smallMinimumThumbSize;
protected boolean isPlaceButtonsTogether;
/**
* Creates a new instance.
*/
public QuaquaScrollBarUI() {
}
public static ComponentUI createUI(JComponent c) {
return new QuaquaScrollBarUI();
}
@Override
protected void installDefaults() {
super.installDefaults();
smallMinimumThumbSize = (Dimension) UIManager.get("ScrollBar.smallMinimumThumbSize");
updatePlaceButtonsTogether();
LookAndFeel.installColorsAndFont(scrollbar, "ScrollBar.background", "ScrollBar.foreground", "ScrollBar.font");
scrollbar.setFocusable(UIManager.getBoolean("ScrollBar.focusable"));
}
private void updatePlaceButtonsTogether() {
Object value = scrollbar.getClientProperty("Quaqua.ScrollBar.placeButtonsTogether");
if (value == null) {
isPlaceButtonsTogether = UIManager.getBoolean("ScrollBar.placeButtonsTogether");
} else {
isPlaceButtonsTogether = value.equals(Boolean.TRUE);
}
}
private boolean isPlaceButtonsTogether() {
return isPlaceButtonsTogether;
}
@Override
protected TrackListener createTrackListener() {
return new QuaquaTrackListener();
}
@Override
protected ArrowButtonListener createArrowButtonListener() {
return new QuaquaArrowButtonListener();
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new QuaquaPropertyChangeHandler();
}
@Override
protected JButton createDecreaseButton(int orientation) {
return new QuaquaArrowButton(scrollbar);
}
@Override
protected JButton createIncreaseButton(int orientation) {
return new QuaquaArrowButton(scrollbar);
}
@Override
protected ScrollListener createScrollListener() {
return new QuaquaScrollListener();
}
/**
* Returns true, if the scrollbar is using the small size style.
*/
private boolean isSmall() {
// If we are on a JScrollPane, we also use the JScrollPane to
// determine the size style.
if (scrollbar.getParent() instanceof JScrollPane) {
return QuaquaUtilities.isSmallSizeVariant((JScrollPane) scrollbar.getParent())//
|| QuaquaUtilities.isSmallSizeVariant(scrollbar);
} else {
return QuaquaUtilities.isSmallSizeVariant(scrollbar);
}
}
/**
* Return the smallest acceptable size for the thumb. If the scrollbar
* becomes so small that this size isn't available, the thumb will be
* hidden.
*
* Warning : the value returned by this method should not be
* be modified, it's a shared static constant.
*
* @return The smallest acceptable size for the thumb.
* @see #getMaximumThumbSize
*/
@Override
protected Dimension getMinimumThumbSize() {
return isSmall()
? smallMinimumThumbSize
: minimumThumbSize;
}
@Override
public Dimension getMaximumSize(JComponent c) {
if (isSmall()) {
return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
? new Dimension(11, Integer.MAX_VALUE)
: new Dimension(Integer.MAX_VALUE, 11);
} else {
return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
? new Dimension(15, Integer.MAX_VALUE)
: new Dimension(Integer.MAX_VALUE, 15);
}
}
@Override
public Dimension getPreferredSize(JComponent c) {
if (isSmall()) {
return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
? new Dimension(11, 24)
: new Dimension(24, 11);
} else {
return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
? new Dimension(15, 32)
: new Dimension(32, 15);
}
}
/**
* Return the largest acceptable size for the thumb. To create a fixed
* size thumb one make this method and getMinimumThumbSize
* return the same value.
*
* Warning : the value returned by this method should not be
* be modified, it's a shared static constant.
*
* @return The largest acceptable size for the thumb.
* @see #getMinimumThumbSize
*/
@Override
protected Dimension getMaximumThumbSize() {
return maximumThumbSize;
}
@Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
Debug.paint(g, c, this);
}
@Override
protected void paintThumb(Graphics gr, JComponent c, Rectangle thumbBounds) {
if (thumbBounds.isEmpty()) {
return;
}
Graphics2D g = (Graphics2D) gr;
boolean isSmall = isSmall();
if (!scrollbar.isEnabled()
|| !QuaquaUtilities.isOnActiveWindow(scrollbar)) {
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
Border trackAndButtons = UIManager.getBorder(isSmall ? "ScrollBar.small.ivThumb" : "ScrollBar.ivThumb");
trackAndButtons.paintBorder(c, g,
thumbBounds.x, thumbBounds.y,
thumbBounds.width, thumbBounds.height);
} else {
Border trackAndButtons = UIManager.getBorder(isSmall ? "ScrollBar.small.ihThumb" : "ScrollBar.ihThumb");
trackAndButtons.paintBorder(c, g,
thumbBounds.x, thumbBounds.y,
thumbBounds.width, thumbBounds.height);
}
} else {
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
Icon thumbBegin = ((Icon[]) UIManager.get(isSmall ? "ScrollBar.small.vThumbTop" : "ScrollBar.vThumbTop"))[thumbBounds.y % 5];
thumbBegin.paintIcon(c, g, thumbBounds.x, thumbBounds.y);
Icon thumbEnd = ((Icon[]) UIManager.get(isSmall ? "ScrollBar.small.vThumbBottom" : "ScrollBar.vThumbBottom"))[(thumbBounds.y + thumbBounds.height) % 5];
thumbEnd.paintIcon(c, g, thumbBounds.x, thumbBounds.y + thumbBounds.height - thumbEnd.getIconHeight());
BufferedImage img = (BufferedImage) UIManager.get(isSmall ? "ScrollBar.small.vThumbBody" : "ScrollBar.vThumbBody");
TexturePaint paint = new TexturePaint(
img,
new Rectangle(thumbBounds.x, 0, img.getWidth(), img.getHeight()));
g.setPaint(paint);
g.fillRect(thumbBounds.x, thumbBounds.y + thumbBegin.getIconHeight(), getPreferredSize(c).width, thumbBounds.height - thumbBegin.getIconHeight() - thumbEnd.getIconHeight());
} else {
Icon thumbBegin = ((Icon[]) UIManager.get(isSmall ? "ScrollBar.small.hThumbLeft" : "ScrollBar.hThumbLeft"))[thumbBounds.x % 5];
thumbBegin.paintIcon(c, g, thumbBounds.x, thumbBounds.y);
Icon thumbEnd = ((Icon[]) UIManager.get(isSmall ? "ScrollBar.small.hThumbRight" : "ScrollBar.hThumbRight"))[(thumbBounds.x + thumbBounds.width) % 5];
thumbEnd.paintIcon(c, g, thumbBounds.x + thumbBounds.width - thumbEnd.getIconWidth(), thumbBounds.y);
BufferedImage img = (BufferedImage) UIManager.get(isSmall ? "ScrollBar.small.hThumbBody" : "ScrollBar.hThumbBody");
TexturePaint paint = new TexturePaint(
img,
new Rectangle(0, thumbBounds.y, img.getWidth(), img.getHeight()));
g.setPaint(paint);
g.fillRect(thumbBounds.x + thumbBegin.getIconWidth(), thumbBounds.y, thumbBounds.width - thumbBegin.getIconWidth() - thumbEnd.getIconWidth(), getPreferredSize(c).height);
}
}
}
/**
* This method actually paints the track plus the button artwork.
*/
@Override
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
Dimension sbSize = scrollbar.getSize();
Insets sbInsets = scrollbar.getInsets();
Rectangle contentBounds = new Rectangle(
sbInsets.left, sbInsets.top,
sbSize.width - sbInsets.left - sbInsets.right,
sbSize.height - sbInsets.top - sbInsets.bottom);
Border trackAndButtons = getTrackAndButtonsBorder();
if (trackAndButtons == null) return;
Insets tbInsets = trackAndButtons.getBorderInsets(scrollbar);
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
if (isPlaceButtonsTogether()
&& contentBounds.height < tbInsets.top + tbInsets.bottom) {
// Special treatment, if track and buttons do
// not fit into available space
Graphics clipped = g.create(contentBounds.x, contentBounds.y, contentBounds.width, contentBounds.height);
int buttonsH = incrButton.getPreferredSize().height * 2;
trackAndButtons.paintBorder(c, g,
contentBounds.x,
contentBounds.y + contentBounds.height - tbInsets.top - tbInsets.bottom + Math.max(0, (buttonsH - contentBounds.height) / 2),
contentBounds.width,
tbInsets.top + tbInsets.bottom);
clipped.dispose();
} else {
trackAndButtons.paintBorder(c, g, contentBounds.x, contentBounds.y, contentBounds.width, contentBounds.height);
}
} else {
if (isPlaceButtonsTogether()
&& contentBounds.width < tbInsets.left + tbInsets.right) {
// Special treatment, if track and buttons do
// not fit into available space
//trackAndButtons.paintBorder(c, g, contentBounds.x, contentBounds.y, contentBounds.width, contentBounds.height);
Graphics clipped = g.create(contentBounds.x, contentBounds.y, contentBounds.width, contentBounds.height);
int buttonsW = incrButton.getPreferredSize().width * 2;
trackAndButtons.paintBorder(c, g,
contentBounds.x + contentBounds.width - tbInsets.left - tbInsets.right + Math.max(0, (buttonsW - contentBounds.width) / 2),
contentBounds.y,
tbInsets.left + tbInsets.right,
contentBounds.height);
clipped.dispose();
} else {
trackAndButtons.paintBorder(c, g, contentBounds.x, contentBounds.y, contentBounds.width, contentBounds.height);
}
}
}
/**
* We use a border to fill the background of the scroll bar.
* The border contains the track and the buttons in their current visual
* representation depending on the various states of the scroll bar
* ('enabled','pressed','inactive').
*/
protected Border getTrackAndButtonsBorder() {
char vh = (scrollbar.getOrientation() == JScrollBar.VERTICAL) ? 'v' : 'h';
boolean isSmall = isSmall();
// Return empty track if extent of scroll bar fully visible
if (scrollbar.getMinimum() + scrollbar.getVisibleAmount() == scrollbar.getMaximum()) {
return UIManager.getBorder(isSmall ? "ScrollBar.small." + vh + "Track" : "ScrollBar." + vh + "Track");
}
// compute button index
int buttonsIndex;
// Scroll Bar arrows are always shown in active state by some Mac OS X
// Applications and by some they are grayed on inactive windows.
//if (scrollbar.isEnabled() && QuaquaUtilities.isOnActiveWindow(scrollbar)) {
if (scrollbar.isEnabled()) {
if (incrButton.getModel().isArmed() && incrButton.getModel().isPressed()) {
buttonsIndex = 1;
} else if (decrButton.getModel().isArmed() && decrButton.getModel().isPressed()) {
buttonsIndex = 3;
} else {
buttonsIndex = 0;
}
} else {
buttonsIndex = 2;
}
Border[] borders;
if (isPlaceButtonsTogether()) {
borders = (Border[]) UIManager.get(isSmall ? "ScrollBar.smallTog." + vh + "Buttons" : "ScrollBar.tog." + vh + "Buttons");
} else {
borders = (Border[]) UIManager.get(isSmall ? "ScrollBar.smallSep." + vh + "Buttons" : "ScrollBar.sep." + vh + "Buttons");
}
return (borders == null) ? null : borders[buttonsIndex];
}
/**
* Indicates whether the user can absolutely position the offset with
* a mouse click (depending on the settings in "Appearance" panel of
* " the "System Preferences" application).
*
The return value is determined from the UIManager property
* ScrollBar.allowsAbsolutePositioning.
*/
@Override
public boolean getSupportsAbsolutePositioning() {
return UIManager.getBoolean("ScrollBar.supportsAbsolutePositioning");
}
@Override
protected void layoutVScrollbar(JScrollBar sb) {
Dimension sbSize = sb.getSize();
Insets sbInsets = sb.getInsets();
// Width and left edge of the buttons and thumb.
int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
int itemX = sbInsets.left;
/**
* Hide thumb and buttons if the complete extent is shown.
* Note that setting the
* thumbs bounds will cause a repaint.
*/
if (sb.getMinimum() + sb.getVisibleAmount() == sb.getMaximum()) {
trackRect.setBounds(sbInsets.left, sbInsets.top, itemW, sbSize.height - (sbInsets.top + sbInsets.bottom));
decrButton.setBounds(0, 0, 0, 0);
incrButton.setBounds(0, 0, 0, 0);
setThumbBounds(0, 0, 0, 0);
return;
}
boolean isSmall = isSmall();
int incrButtonH, decrButtonH;
incrButtonH = decrButtonH = (isSmall) ? 12 : 16;
// The thumb must fit within the height left over after we
// subtract the preferredSize of the buttons and the insets.
int sbInsetsH = sbInsets.top + sbInsets.bottom;
int sbButtonsH = decrButtonH + incrButtonH;
int trackY;
int trackH;
int thumbH;
int thumbY;
int incrButtonY;
int decrButtonY;
int sbAvailButtonH = (sbSize.height - sbInsetsH);
// Compute y-locations and heights
if (isPlaceButtonsTogether()) {
incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
decrButtonY = incrButtonY - decrButtonH;
// If the buttons don't fit, allocate half of the available
// space to each.
if (sbAvailButtonH < sbButtonsH) {
incrButtonH = sbAvailButtonH / 2;
decrButtonH = sbAvailButtonH - incrButtonH;
incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
decrButtonY = sbInsets.top;
}
trackY = sbInsets.top + ((isSmall) ? 4 : 5); // depends on size style
trackH = decrButtonY - trackY + ((isSmall) ? 0 : 3); // depends on size style
} else {
decrButtonY = sbInsets.top;
incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
// If the buttons don't fit, allocate half of the available
// space to each.
if (sbAvailButtonH < sbButtonsH) {
incrButtonH = decrButtonH = sbAvailButtonH / 2;
incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
}
trackY = decrButtonY + decrButtonH + ((isSmall) ? 0 : -1);
trackH = incrButtonY - trackY + ((isSmall) ? 2 : 3);
}
decrButton.setBounds(itemX, decrButtonY, itemW, decrButtonH);
incrButton.setBounds(itemX, incrButtonY, itemW, incrButtonH);
trackRect.setBounds(itemX, trackY, itemW, trackH);
/* Compute the height and origin of the thumb. The case
* where the thumb is at the bottom edge is handled specially
* to avoid numerical problems in computing thumbY. Enforce
* the thumbs min/max dimensions. If the thumb doesn't
* fit in the track (trackH) we'll hide it later.
*/
float min = sb.getMinimum();
float extent = sb.getVisibleAmount();
float range = sb.getMaximum() - min;
float value = sb.getValue();
float ftrackH = (float) trackH;
thumbH = (range <= 0)
? getMaximumThumbSize().height : (int) (ftrackH * (extent / range));
thumbH = Math.max(thumbH, getMinimumThumbSize().height);
thumbH = Math.min(thumbH, getMaximumThumbSize().height);
thumbY = trackY + trackH - thumbH;
if (sb.getValue() < (sb.getMaximum() - sb.getVisibleAmount())) {
float thumbRange = ftrackH - thumbH;
thumbY = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
thumbY += trackY;
}
/* If the thumb isn't going to fit, zero it's bounds. Otherwise
* make sure it fits into the track. Note that setting the
* thumbs bounds will cause a repaint.
*/
if (thumbH >= trackH) {
setThumbBounds(0, 0, 0, 0);
} else {
setThumbBounds(itemX, thumbY, itemW, thumbH);
}
}
@Override
protected void layoutHScrollbar(JScrollBar sb) {
Dimension sbSize = sb.getSize();
Insets sbInsets = sb.getInsets();
/* Height and top edge of the buttons and thumb.
*/
int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
int itemY = sbInsets.top;
/**
* Hide thumb and buttons if the complete extent is shown.
* Note that setting the
* thumbs bounds will cause a repaint.
*/
if (sb.getMinimum() + sb.getVisibleAmount() == sb.getMaximum()) {
trackRect.setBounds(sbInsets.left, sbInsets.top, sbSize.width - (sbInsets.left + sbInsets.right), itemH);
decrButton.setBounds(0, 0, 0, 0);
incrButton.setBounds(0, 0, 0, 0);
setThumbBounds(0, 0, 0, 0);
return;
}
boolean isSmall = isSmall();
int leftButtonW, rightButtonW;
leftButtonW = rightButtonW = (isSmall) ? 12 : 16;
// The thumb must fit within the width left over after we
// subtract the preferredSize of the buttons and the insets.
int sbInsetsW = sbInsets.left + sbInsets.right;
int sbButtonsW = leftButtonW + rightButtonW;
int trackX;
int trackW;
int thumbW;
int thumbX;
int rightButtonX;
int leftButtonX;
int sbAvailButtonW = (sbSize.width - sbInsetsW);
boolean ltr;
if (isPlaceButtonsTogether()) {
ltr = true;
// Nominal locations of the buttons, assuming their preferred
// size will fit.
rightButtonX = sbSize.width - (sbInsets.right + rightButtonW);
leftButtonX = rightButtonX - leftButtonW;
// If the buttons don't fit, allocate half of the available
// space to each and move the right one over.
if (sbAvailButtonW < sbButtonsW) {
leftButtonW = sbAvailButtonW / 2;
rightButtonW = sbAvailButtonW - leftButtonW;
leftButtonX = sbInsets.left;
rightButtonX = leftButtonX + leftButtonW;
}
trackX = sbInsets.left + 5;
trackW = leftButtonX - trackX;
if (!isSmall) {
trackW += 1;
}
} else {
ltr = sb.getComponentOrientation().isLeftToRight();
if (!ltr) {
int helper = leftButtonW;
leftButtonW = rightButtonW;
rightButtonW = helper;
}
// Nominal locations of the buttons, assuming their preferred
// size will fit.
leftButtonX = sbInsets.left;
rightButtonX = sbSize.width - (sbInsets.right + rightButtonW);
// If the buttons don't fit, allocate half of the available
// space to each and move the right one over.
if (sbAvailButtonW < sbButtonsW) {
rightButtonW = leftButtonW = sbAvailButtonW / 2;
rightButtonX = sbSize.width - (sbInsets.right + rightButtonW);
}
trackX = leftButtonX + leftButtonW - 1;
trackW = rightButtonX - trackX + 1;
}
(ltr ? decrButton : incrButton).setBounds(leftButtonX, itemY, leftButtonW, itemH);
(ltr ? incrButton : decrButton).setBounds(rightButtonX, itemY, rightButtonW, itemH);
trackRect.setBounds(trackX, itemY, trackW, itemH);
/* Compute the width and origin of the thumb. Enforce
* the thumbs min/max dimensions. The case where the thumb
* is at the right edge is handled specially to avoid numerical
* problems in computing thumbX. If the thumb doesn't
* fit in the track (trackH) we'll hide it later.
*/
float min = sb.getMinimum();
float max = sb.getMaximum();
float extent = sb.getVisibleAmount();
float range = max - min;
float value = sb.getValue();
float ftrackW = (float) trackW;
thumbW = (range <= 0)
? getMaximumThumbSize().width : (int) (ftrackW * (extent / range));
thumbW = Math.max(thumbW, getMinimumThumbSize().width);
thumbW = Math.min(thumbW, getMaximumThumbSize().width);
thumbX = trackX + trackW - thumbW;
if (sb.getValue() < (max - sb.getVisibleAmount())) {
float thumbRange = ftrackW - thumbW;
thumbX = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
thumbX += trackX;
}
/* Make sure the thumb fits between the buttons. Note
* that setting the thumbs bounds causes a repaint.
*/
if (thumbW >= (int) trackW) {
setThumbBounds(0, 0, 0, 0);
} else {
setThumbBounds(thumbX, itemY, thumbW, itemH);
}
}
@Override
protected void scrollByUnit(int direction) {
scrollByUnits(scrollbar, direction, 1, false);
}
/*
* Method for scrolling by a unit increment.
* Added for mouse wheel scrolling support, RFE 4202656.
*
* If limitByBlock is set to true, the scrollbar will scroll at least 1
* unit increment, but will not scroll farther than the block increment.
* See BasicScrollPaneUI.Handler.mouseWheelMoved().
*/
static void scrollByUnits(JScrollBar scrollbar, int direction,
int units, boolean limitToBlock) {
// This method is called from QuaquaScrollPaneUI to implement wheel
// scrolling, as well as from scrollByUnit().
int delta;
int limit = -1;
if (limitToBlock) {
if (direction < 0) {
limit = scrollbar.getValue() -
scrollbar.getBlockIncrement(direction);
}
else {
limit = scrollbar.getValue() +
scrollbar.getBlockIncrement(direction);
}
}
for (int i=0; i 0) {
delta = scrollbar.getUnitIncrement(direction);
}
else {
delta = -scrollbar.getUnitIncrement(direction);
}
int oldValue = scrollbar.getValue();
int newValue = oldValue + delta;
// Check for overflow.
if (delta > 0 && newValue < oldValue) {
newValue = scrollbar.getMaximum();
}
else if (delta < 0 && newValue > oldValue) {
newValue = scrollbar.getMinimum();
}
if (oldValue == newValue) {
break;
}
if (limitToBlock && i > 0) {
assert limit != -1;
if ((direction < 0 && newValue < limit) ||
(direction > 0 && newValue > limit)) {
break;
}
}
scrollbar.setValue(newValue);
}
}
@Override
protected void scrollByBlock(int direction)
{
scrollByBlock(scrollbar, direction);
trackHighlight = direction > 0 ? INCREASE_HIGHLIGHT : DECREASE_HIGHLIGHT;
Rectangle dirtyRect = getTrackBounds();
scrollbar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
}
/*
* Method for scrolling by a block increment.
* Added for mouse wheel scrolling support, RFE 4202656.
*/
static void scrollByBlock(JScrollBar scrollbar, int direction) {
// This method is called from BasicScrollPaneUI to implement wheel
// scrolling, and also from scrollByBlock().
int oldValue = scrollbar.getValue();
int blockIncrement = scrollbar.getBlockIncrement(direction);
int delta = blockIncrement * ((direction > 0) ? +1 : -1);
int newValue = oldValue + delta;
// Check for overflow.
if (delta > 0 && newValue < oldValue) {
newValue = scrollbar.getMaximum();
}
else if (delta < 0 && newValue > oldValue) {
newValue = scrollbar.getMinimum();
}
scrollbar.setValue(newValue);
}
protected class QuaquaTrackListener extends TrackListener {
protected transient int direction = +1;
@Override
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)
|| (!getSupportsAbsolutePositioning()
&& SwingUtilities.isMiddleMouseButton(e))) {
return;
}
if (!scrollbar.isEnabled()) {
return;
}
Rectangle r = getTrackBounds();
scrollbar.repaint(r.x, r.y, r.width, r.height);
trackHighlight = NO_HIGHLIGHT;
isDragging = false;
offset = 0;
scrollTimer.stop();
scrollbar.setValueIsAdjusting(false);
}
/**
* If the mouse is pressed above the "thumb" component
* then reduce the scrollbars value by one page ("page up"),
* otherwise increase it by one page. If there is no
* thumb then page up if the mouse is in the upper half
* of the track.
*/
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)
|| (!getSupportsAbsolutePositioning()
&& SwingUtilities.isMiddleMouseButton(e))) {
return;
}
if (!scrollbar.isEnabled()) {
return;
}
if (!scrollbar.hasFocus() && scrollbar.isRequestFocusEnabled()) {
scrollbar.requestFocus();
}
scrollbar.setValueIsAdjusting(true);
currentMouseX = e.getX();
currentMouseY = e.getY();
// Clicked in the Thumb area?
if (getThumbBounds().contains(currentMouseX, currentMouseY)) {
switch (scrollbar.getOrientation()) {
case JScrollBar.VERTICAL:
offset = currentMouseY - getThumbBounds().y;
break;
case JScrollBar.HORIZONTAL:
offset = currentMouseX - getThumbBounds().x;
break;
}
isDragging = true;
return;
} else if (getSupportsAbsolutePositioning()
|| SwingUtilities.isMiddleMouseButton(e)) {
switch (scrollbar.getOrientation()) {
case JScrollBar.VERTICAL:
offset = getThumbBounds().height / 2;
break;
case JScrollBar.HORIZONTAL:
offset = getThumbBounds().width / 2;
break;
}
isDragging = true;
setValueFrom(e);
return;
}
isDragging = false;
Dimension sbSize = scrollbar.getSize();
direction = +1;
switch (scrollbar.getOrientation()) {
case JScrollBar.VERTICAL:
if (getThumbBounds().isEmpty()) {
int scrollbarCenter = sbSize.height / 2;
direction = (currentMouseY < scrollbarCenter) ? -1 : +1;
} else {
int thumbY = getThumbBounds().y;
direction = (currentMouseY < thumbY) ? -1 : +1;
}
break;
case JScrollBar.HORIZONTAL:
if (getThumbBounds().isEmpty()) {
int scrollbarCenter = sbSize.width / 2;
direction = (currentMouseX < scrollbarCenter) ? -1 : +1;
} else {
int thumbX = getThumbBounds().x;
direction = (currentMouseX < thumbX) ? -1 : +1;
}
if (!scrollbar.getComponentOrientation().isLeftToRight()) {
direction = -direction;
}
break;
}
scrollByBlock(direction);
scrollTimer.stop();
scrollListener.setDirection(direction);
scrollListener.setScrollByBlock(true);
startScrollTimerIfNecessary();
}
/**
* Set the models value to the position of the thumb's top of Vertical
* scrollbar, or the left/right of Horizontal scrollbar in
* left-to-right/right-to-left scrollbar relative to the origin of the
* track.
*/
@Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)
|| (!getSupportsAbsolutePositioning()
&& SwingUtilities.isMiddleMouseButton(e))) {
return;
}
if (!scrollbar.isEnabled() || getThumbBounds().isEmpty()) {
return;
}
if (isDragging) {
setValueFrom(e);
} else {
currentMouseX = e.getX();
currentMouseY = e.getY();
startScrollTimerIfNecessary();
}
}
private void setValueFrom(MouseEvent e) {
BoundedRangeModel model = scrollbar.getModel();
Rectangle thumbR = getThumbBounds();
Rectangle trackR = getTrackBounds();
int thumbMin, thumbMax, thumbPos;
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
thumbMin = trackR.y;
thumbMax = trackR.y + trackR.height - thumbR.height;
thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getY() - offset)));
setThumbBounds(thumbR.x, thumbPos, thumbR.width, thumbR.height);
} else {
/*
if (scrollbar.getComponentOrientation().isLeftToRight()) {
thumbMin = decrButton.getX() + decrButton.getWidth();
thumbMax = incrButton.getX() - thumbR.width;
} else {
thumbMin = incrButton.getX() + incrButton.getWidth();
thumbMax = decrButton.getX() - thumbR.width;
}*/
thumbMin = trackR.x;
thumbMax = trackR.x + trackR.width - thumbR.width;
thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getX() - offset)));
setThumbBounds(thumbPos, thumbR.y, thumbR.width, thumbR.height);
}
/* Set the scrollbars value. If the thumb has reached the end of
* the scrollbar, then just set the value to its maximum. Otherwise
* compute the value as accurately as possible.
*/
if (thumbPos == thumbMax) {
if (scrollbar.getOrientation() == JScrollBar.VERTICAL
|| scrollbar.getComponentOrientation().isLeftToRight()) {
scrollbar.setValue(model.getMaximum() - model.getExtent());
} else {
scrollbar.setValue(model.getMinimum());
}
} else {
float valueMax = model.getMaximum() - model.getExtent();
float valueRange = valueMax - model.getMinimum();
float thumbValue = thumbPos - thumbMin;
float thumbRange = thumbMax - thumbMin;
int value;
if (scrollbar.getOrientation() == JScrollBar.VERTICAL
|| scrollbar.getComponentOrientation().isLeftToRight()) {
value = (int) (0.5 + ((thumbValue / thumbRange) * valueRange));
} else {
value = (int) (0.5 + (((thumbMax - thumbPos) / thumbRange) * valueRange));
}
scrollbar.setValue(value + model.getMinimum());
}
}
private void startScrollTimerIfNecessary() {
if (scrollTimer.isRunning()) {
return;
}
switch (scrollbar.getOrientation()) {
case JScrollBar.VERTICAL:
if (direction > 0) {
if (getThumbBounds().y + getThumbBounds().height
< ((QuaquaTrackListener) trackListener).currentMouseY) {
scrollTimer.start();
}
} else if (getThumbBounds().y
> ((QuaquaTrackListener) trackListener).currentMouseY) {
scrollTimer.start();
}
break;
case JScrollBar.HORIZONTAL:
if (direction > 0) {
if (getThumbBounds().x + getThumbBounds().width
< ((QuaquaTrackListener) trackListener).currentMouseX) {
scrollTimer.start();
}
} else if (getThumbBounds().x
> ((QuaquaTrackListener) trackListener).currentMouseX) {
scrollTimer.start();
}
break;
}
}
@Override
public void mouseMoved(MouseEvent e) {
}
private int getCurrentMouseX() {
return currentMouseX;
}
private int getCurrentMouseY() {
return currentMouseY;
}
}
/**
* Listener for cursor keys.
*/
protected class QuaquaArrowButtonListener extends ArrowButtonListener {
@Override
public void mousePressed(MouseEvent evt) {
super.mousePressed(evt);
repaintButtons();
}
@Override
public void mouseReleased(MouseEvent evt) {
super.mouseReleased(evt);
repaintButtons();
}
@Override
public void mouseEntered(MouseEvent evt) {
super.mouseEntered(evt);
if ((evt.getModifiers() & (InputEvent.BUTTON1_MASK | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) != 0) {
//if (evt.getButton() != MouseEvent.NOBUTTON) {
repaintButtons();
}
}
@Override
public void mouseExited(MouseEvent evt) {
super.mouseExited(evt);
if ((evt.getModifiers() & (InputEvent.BUTTON1_MASK | InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) != 0) {
//if (evt.getButton() != MouseEvent.NOBUTTON) {
repaintButtons();
}
}
protected void repaintButtons() {
Dimension sbSize = scrollbar.getSize();
Insets sbInsets = scrollbar.getInsets();
Rectangle contentBounds = new Rectangle(
sbInsets.left, sbInsets.top,
sbSize.width - sbInsets.left - sbInsets.right,
sbSize.height - sbInsets.top - sbInsets.bottom);
if (isPlaceButtonsTogether()) {
Border trackAndButtonsBorder = getTrackAndButtonsBorder();
Insets tbInsets = trackAndButtonsBorder.getBorderInsets(scrollbar);
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
scrollbar.repaint(
contentBounds.x,
contentBounds.y + contentBounds.height - tbInsets.bottom,
contentBounds.width,
tbInsets.bottom);
} else {
scrollbar.repaint(
contentBounds.x + contentBounds.width - tbInsets.right,
contentBounds.y,
tbInsets.right,
contentBounds.height);
}
} else {
scrollbar.repaint();
}
}
}
/**
* Listener for scrolling events initiated in the
* ScrollPane
.
*/
protected class QuaquaScrollListener extends ScrollListener {
int direction = +1;
boolean useBlockIncrement;
public QuaquaScrollListener() {
direction = +1;
useBlockIncrement = false;
}
public QuaquaScrollListener(int dir, boolean block) {
direction = dir;
useBlockIncrement = block;
}
@Override
public void setDirection(int direction) {
this.direction = direction;
}
@Override
public void setScrollByBlock(boolean block) {
this.useBlockIncrement = block;
}
@Override
public void actionPerformed(ActionEvent e) {
QuaquaTrackListener trackListener = (QuaquaTrackListener) QuaquaScrollBarUI.this.trackListener;
if (useBlockIncrement) {
scrollByBlock(direction);
// Stop scrolling if the thumb catches up with the mouse
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
if (direction > 0) {
if (getThumbBounds().y + getThumbBounds().height
>= trackListener.getCurrentMouseY()) {
((Timer) e.getSource()).stop();
}
} else if (getThumbBounds().y <= trackListener.getCurrentMouseY()) {
((Timer) e.getSource()).stop();
}
} else {
if (direction > 0) {
if (getThumbBounds().x + getThumbBounds().width
>= trackListener.getCurrentMouseX()) {
((Timer) e.getSource()).stop();
}
} else if (getThumbBounds().x <= trackListener.getCurrentMouseX()) {
((Timer) e.getSource()).stop();
}
}
} else {
scrollByUnit(direction);
}
if (direction > 0
&& scrollbar.getValue() + scrollbar.getVisibleAmount()
>= scrollbar.getMaximum()) {
((Timer) e.getSource()).stop();
} else if (direction < 0
&& scrollbar.getValue() <= scrollbar.getMinimum()) {
((Timer) e.getSource()).stop();
}
}
}
public class QuaquaPropertyChangeHandler extends BasicScrollBarUI.PropertyChangeHandler {
@Override
public void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
if ("Frame.active".equals(name)) {
scrollbar.repaint();
} else if ("Quaqua.ScrollBar.placeButtonsTogether".equals(name)) {
updatePlaceButtonsTogether();
} else if (name.equals("JComponent.sizeVariant")) {
QuaquaUtilities.applySizeVariant(scrollbar);
}
super.propertyChange(e);
}
}
}