/*
* Copyright (c) 2009 Kathryn Huxtable and Kenneth Orr.
*
* This file is part of the SeaGlass Pluggable Look and Feel.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id: SeaGlassScrollBarUI.java 1595 2011-08-09 20:33:48Z rosstauscher@gmx.de $
*/
package com.seaglasslookandfeel.ui;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicScrollBarUI;
import javax.swing.plaf.synth.Region;
import javax.swing.plaf.synth.SynthContext;
import javax.swing.plaf.synth.SynthLookAndFeel;
import javax.swing.plaf.synth.SynthStyle;
import sun.swing.DefaultLookup;
import com.seaglasslookandfeel.SeaGlassContext;
import com.seaglasslookandfeel.SeaGlassLookAndFeel;
import com.seaglasslookandfeel.SeaGlassRegion;
import com.seaglasslookandfeel.SeaGlassStyle;
import com.seaglasslookandfeel.component.SeaGlassArrowButton;
import com.seaglasslookandfeel.state.ScrollBarButtonsTogetherState;
import com.seaglasslookandfeel.state.State;
/**
* SeaGlassScrollBarUI implementation.
*
* Based on SynthScrollBarUI by Scott Violet.
*
* @see javax.swing.plaf.synth.SynthScrollBarUI
*/
public class SeaGlassScrollBarUI extends BasicScrollBarUI implements PropertyChangeListener, SeaglassUI {
private static final State buttonsTogether = new ScrollBarButtonsTogetherState();
private SynthStyle style;
private SynthStyle thumbStyle;
private SynthStyle trackStyle;
private SynthStyle capStyle;
private MouseButtonListener mouseButtonListener;
private boolean validMinimumThumbSize;
private int scrollBarWidth;
// These two variables should be removed when the corrosponding ones in
// BasicScrollBarUI are made protected
private int incrGap;
private int decrGap;
private int capSize;
public static ComponentUI createUI(JComponent c) {
return new SeaGlassScrollBarUI();
}
protected void installDefaults() {
/*
* NOTE: This next line of code was added because, since incrGap and
* decrGap in BasicScrollBarUI are private, I need to have some way of
* updating them. This is an incomplete solution (since it implies that
* the incrGap and decrGap are set once, and not reset per state.
* Probably ok, but not always ok). This line of code should be removed
* at the same time that incrGap and decrGap are removed and made
* protected in the super class.
*/
super.installDefaults();
trackHighlight = NO_HIGHLIGHT;
if (scrollbar.getLayout() == null || (scrollbar.getLayout() instanceof UIResource)) {
scrollbar.setLayout(this);
}
updateStyle(scrollbar);
}
protected void configureScrollBarColors() {
}
private void updateStyle(JScrollBar c) {
SynthStyle oldStyle = style;
SeaGlassContext context = getContext(c, ENABLED);
style = SeaGlassLookAndFeel.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);
capSize = style.getInt(context, "ScrollBar.capSize", 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 = SeaGlassStyle.getSizeVariant(scrollbar);
if (scaleKey != null) {
if (SeaGlassStyle.LARGE_KEY.equals(scaleKey)) {
scrollBarWidth *= 1.15;
incrGap *= 1.15;
decrGap *= 1.15;
} else if (SeaGlassStyle.SMALL_KEY.equals(scaleKey)) {
scrollBarWidth *= 0.857;
incrGap *= 0.857;
decrGap *= 0.857;
} else if (SeaGlassStyle.MINI_KEY.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 = SeaGlassLookAndFeel.updateStyle(context, this);
context.dispose();
context = getContext(c, Region.SCROLL_BAR_THUMB, ENABLED);
thumbStyle = SeaGlassLookAndFeel.updateStyle(context, this);
context.dispose();
context = getContext(c, SeaGlassRegion.SCROLL_BAR_CAP, ENABLED);
capStyle = SeaGlassLookAndFeel.updateStyle(context, this);
context.dispose();
}
protected void installListeners() {
super.installListeners();
scrollbar.addPropertyChangeListener(this);
mouseButtonListener = new MouseButtonListener();
scrollbar.addMouseListener(mouseButtonListener);
}
protected void uninstallListeners() {
super.uninstallListeners();
scrollbar.removePropertyChangeListener(this);
scrollbar.removeMouseListener(mouseButtonListener);
}
protected void uninstallDefaults() {
SeaGlassContext 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;
context = getContext(scrollbar, SeaGlassRegion.SCROLL_BAR_CAP, ENABLED);
capStyle.uninstallDefaults(context);
context.dispose();
capStyle = null;
super.uninstallDefaults();
}
public SeaGlassContext getContext(JComponent c) {
return getContext(c, getComponentState(c));
}
private SeaGlassContext getContext(JComponent c, int state) {
return SeaGlassContext.getContext(SeaGlassContext.class, c, SynthLookAndFeel.getRegion(c), style, state);
}
private int getComponentState(JComponent c) {
return SeaGlassLookAndFeel.getComponentState(c);
}
private SeaGlassContext getContext(JComponent c, Region region) {
return getContext(c, region, getComponentState(c, region));
}
private SeaGlassContext getContext(JComponent c, Region region, int state) {
SynthStyle style = trackStyle;
if (region == Region.SCROLL_BAR_THUMB) {
style = thumbStyle;
} else if (region == SeaGlassRegion.SCROLL_BAR_CAP) {
style = capStyle;
}
return SeaGlassContext.getContext(SeaGlassContext.class, c, region, style, state);
}
public JButton getDecreaseButton() {
return decrButton;
}
public JButton getIncreaseButton() {
return incrButton;
}
public boolean isMouseButtonDown() {
return isMouseButtonDown;
}
private int getComponentState(JComponent c, Region region) {
if (region == Region.SCROLL_BAR_THUMB && isThumbRollover() && c.isEnabled()) {
if (isMouseButtonDown) {
return PRESSED;
}
return MOUSE_OVER;
}
return SeaGlassLookAndFeel.getComponentState(c);
}
public boolean getSupportsAbsolutePositioning() {
SeaGlassContext context = getContext(scrollbar);
boolean value = style.getBoolean(context, "ScrollBar.allowsAbsolutePositioning", false);
context.dispose();
return value;
}
public void update(Graphics g, JComponent c) {
SeaGlassContext context = getContext(c);
SeaGlassLookAndFeel.update(context, g);
context.getPainter().paintScrollBarBackground(context, g, 0, 0, c.getWidth(), c.getHeight(), scrollbar.getOrientation());
paint(context, g);
context.dispose();
}
public void paint(Graphics g, JComponent c) {
SeaGlassContext context = getContext(c);
paint(context, g);
context.dispose();
}
protected void paint(SeaGlassContext context, Graphics g) {
SeaGlassContext subcontext = getContext(scrollbar, Region.SCROLL_BAR_TRACK);
paintTrack(subcontext, g, getTrackBounds());
subcontext.dispose();
if (buttonsTogether.isInState(context.getComponent())) {
subcontext = getContext(scrollbar, SeaGlassRegion.SCROLL_BAR_CAP);
paintCap(subcontext, g, getCapBounds());
subcontext.dispose();
}
subcontext = getContext(scrollbar, Region.SCROLL_BAR_THUMB);
paintThumb(subcontext, g, getThumbBounds());
subcontext.dispose();
}
public void paintBorder(SynthContext context, Graphics g, int x, int y, int w, int h) {
((SeaGlassContext) context).getPainter().paintScrollBarBorder(context, g, x, y, w, h, scrollbar.getOrientation());
}
protected void paintTrack(SeaGlassContext ss, Graphics g, Rectangle trackBounds) {
SeaGlassLookAndFeel.updateSubregion(ss, g, trackBounds);
ss.getPainter().paintScrollBarTrackBackground(ss, g, trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height,
scrollbar.getOrientation());
ss.getPainter().paintScrollBarTrackBorder(ss, g, trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height,
scrollbar.getOrientation());
}
protected void paintThumb(SeaGlassContext ss, Graphics g, Rectangle thumbBounds) {
SeaGlassLookAndFeel.updateSubregion(ss, g, thumbBounds);
int orientation = scrollbar.getOrientation();
ss.getPainter().paintScrollBarThumbBackground(ss, g, thumbBounds.x, thumbBounds.y, thumbBounds.width, thumbBounds.height,
orientation);
ss.getPainter().paintScrollBarThumbBorder(ss, g, thumbBounds.x, thumbBounds.y, thumbBounds.width, thumbBounds.height, orientation);
}
protected void paintCap(SeaGlassContext ss, Graphics g, Rectangle capBounds) {
SeaGlassLookAndFeel.updateSubregion(ss, g, capBounds);
int orientation = scrollbar.getOrientation();
ss.getPainter().paintScrollBarThumbBackground(ss, g, capBounds.x, capBounds.y, capBounds.width, capBounds.height, orientation);
}
protected Rectangle getCapBounds() {
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
return new Rectangle(0, 0, scrollBarWidth, capSize);
} else if (scrollbar.getComponentOrientation().isLeftToRight()) {
return new Rectangle(0, 0, capSize, scrollBarWidth);
} else {
return new Rectangle(scrollbar.getWidth() - capSize, 0, capSize, scrollBarWidth);
}
}
/**
* 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
*/
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);
}
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;
}
protected JButton createDecreaseButton(int orientation) {
SeaGlassArrowButton synthArrowButton = new SeaGlassArrowButton(orientation) {
@Override
public boolean contains(int x, int y) {
// if there is an overlap between the track and button
if (decrGap < 0) {
// FIXME Need to take RtL orientation into account.
if (buttonsTogether.isInState(scrollbar)) {
int minX = 0;
int minY = 0;
if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
// adjust the height by decrGap
// Note: decrGap is negative!
minY -= decrGap;
} else {
// adjust the width by decrGap
// Note: decrGap is negative!
minX -= decrGap;
}
return (x >= minX) && (x < getWidth()) && (y >= minY) && (y < getHeight());
} else {
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;
}
protected JButton createIncreaseButton(int orientation) {
SeaGlassArrowButton synthArrowButton = new SeaGlassArrowButton(orientation) {
@Override
public boolean contains(int x, int y) {
// if there is an overlap between the track and button
if (incrGap < 0 && !buttonsTogether.isInState(scrollbar)) {
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;
}
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()) {
((SeaGlassArrowButton) incrButton).setDirection(orient == HORIZONTAL ? EAST : SOUTH);
((SeaGlassArrowButton) decrButton).setDirection(orient == HORIZONTAL ? WEST : NORTH);
} else {
((SeaGlassArrowButton) incrButton).setDirection(orient == HORIZONTAL ? WEST : SOUTH);
((SeaGlassArrowButton) decrButton).setDirection(orient == HORIZONTAL ? EAST : NORTH);
}
}
//
// PropertyChangeListener
//
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if (SeaGlassLookAndFeel.shouldUpdateStyle(e)) {
updateStyle((JScrollBar) e.getSource());
}
if ("orientation" == propertyName) {
updateButtonDirections();
} else if ("componentOrientation" == propertyName) {
updateButtonDirections();
}
}
protected void layoutVScrollbar(JScrollBar sb) {
if (!buttonsTogether.isInState(sb)) {
super.layoutVScrollbar(sb);
} else {
layoutVScrollbarTogether(sb);
}
}
protected void layoutHScrollbar(JScrollBar sb) {
if (!buttonsTogether.isInState(sb)) {
super.layoutHScrollbar(sb);
} else {
if (sb.getComponentOrientation().isLeftToRight()) {
layoutHScrollbarTogetherLeftToRight(sb);
} else {
layoutHScrollbarTogetherRightToLeft(sb);
}
}
}
private void layoutVScrollbarTogether(JScrollBar sb) {
ScrollbarLayoutValues lv = new ScrollbarLayoutValues();
Dimension sbSize = sb.getSize();
Insets sbInsets = sb.getInsets();
layoutScrollbarTogether(sb, lv, sbSize.height, sbSize.width, sbInsets.top, sbInsets.bottom, sbInsets.left, sbInsets.right,
decrButton.getPreferredSize().height, getMinimumThumbSize().height, getMaximumThumbSize().height);
trackRect.setBounds(lv.itemEdge, lv.trackPosition, lv.itemThickness, lv.trackLength);
decrButton.setBounds(lv.itemEdge, lv.decrButtonPosition, lv.itemThickness, lv.decrButtonLength);
incrButton.setBounds(lv.itemEdge, lv.incrButtonPosition, lv.itemThickness, lv.incrButtonLength);
if (lv.thumbLength > 0) {
setThumbBounds(lv.itemEdge, lv.thumbPosition, lv.itemThickness, lv.thumbLength);
} else {
setThumbBounds(0, 0, 0, 0);
}
}
private void layoutHScrollbarTogetherLeftToRight(JScrollBar sb) {
ScrollbarLayoutValues lv = new ScrollbarLayoutValues();
Dimension sbSize = sb.getSize();
Insets sbInsets = sb.getInsets();
layoutScrollbarTogether(sb, lv, sbSize.width, sbSize.height, sbInsets.left, sbInsets.right, sbInsets.top, sbInsets.bottom,
decrButton.getPreferredSize().width, getMinimumThumbSize().width, getMaximumThumbSize().width);
trackRect.setBounds(lv.trackPosition, lv.itemEdge, lv.trackLength, lv.itemThickness);
decrButton.setBounds(lv.decrButtonPosition, lv.itemEdge, lv.decrButtonLength, lv.itemThickness);
incrButton.setBounds(lv.incrButtonPosition, lv.itemEdge, lv.incrButtonLength, lv.itemThickness);
if (lv.thumbLength > 0) {
setThumbBounds(lv.thumbPosition, lv.itemEdge, lv.thumbLength, lv.itemThickness);
} else {
setThumbBounds(0, 0, 0, 0);
}
}
private void layoutHScrollbarTogetherRightToLeft(JScrollBar sb) {
ScrollbarLayoutValues lv = new ScrollbarLayoutValues();
Dimension sbSize = sb.getSize();
Insets sbInsets = sb.getInsets();
layoutScrollbarTogether(sb, lv, sbSize.width, sbSize.height, sbInsets.left, sbInsets.right, sbInsets.top, sbInsets.bottom,
decrButton.getPreferredSize().width, getMinimumThumbSize().width, getMaximumThumbSize().width);
// Flip the positions of the buttons.
lv.incrButtonPosition = sbInsets.left;
lv.decrButtonPosition = lv.incrButtonPosition + lv.incrButtonLength;
// Make the thumb position relative to the old track and flip it,
// keeping the position at the left.
lv.thumbPosition = lv.trackPosition + lv.trackLength - lv.thumbPosition - lv.thumbLength;
// Flip the position of the track.
lv.trackPosition = lv.decrButtonPosition + lv.decrButtonLength + incrGap;
// Make the thumb position relative to the scrollbar.
lv.thumbPosition += lv.trackPosition;
trackRect.setBounds(lv.trackPosition, lv.itemEdge, lv.trackLength, lv.itemThickness);
decrButton.setBounds(lv.decrButtonPosition, lv.itemEdge, lv.decrButtonLength, lv.itemThickness);
incrButton.setBounds(lv.incrButtonPosition, lv.itemEdge, lv.incrButtonLength, lv.itemThickness);
if (lv.thumbLength > 0) {
setThumbBounds(lv.thumbPosition, lv.itemEdge, lv.thumbLength, lv.itemThickness);
} else {
setThumbBounds(0, 0, 0, 0);
}
}
/**
* Holds scrollbar layout values in an orientation-independent way.
*/
private class ScrollbarLayoutValues {
int itemEdge;
int itemThickness;
int trackPosition;
int trackLength;
int incrButtonPosition;
int incrButtonLength;
int decrButtonPosition;
int decrButtonLength;
int thumbPosition;
int thumbLength;
}
private void layoutScrollbarTogether(JScrollBar sb, ScrollbarLayoutValues lv, int sbLength, int sbThickness, int insetLengthStart,
int insetLengthEnd, int insetThicknessStart, int insetThicknessEnd, int decrPreferredLength, int minThumbLength, int maxThumbLength) {
/*
* Width and left edge of the buttons and thumb.
*/
lv.itemThickness = sbThickness - (insetThicknessStart + insetThicknessEnd);
lv.itemEdge = insetThicknessStart;
/*
* Nominal locations of the buttons, assuming their preferred size will
* fit.
*/
boolean squareButtons = DefaultLookup.getBoolean(scrollbar, this, "ScrollBar.squareButtons", false);
lv.incrButtonLength = lv.itemThickness + 1;
lv.incrButtonPosition = sbLength - insetLengthEnd - lv.incrButtonLength;
lv.decrButtonLength = squareButtons ? lv.itemThickness : decrPreferredLength;
lv.decrButtonPosition = lv.incrButtonPosition - lv.decrButtonLength;
/*
* The thumb must fit within the height left over after we subtract the
* preferredSize of the buttons and the insets and the gaps
*/
int sbInsetsSpace = insetLengthStart + insetLengthEnd;
int sbButtonsSpace = lv.decrButtonLength + lv.incrButtonLength;
/*
* If the buttons don't fit, allocate half of the available space to
* each and move the lower one (incrButton) down.
*/
int sbAvailButtonSpace = sbLength - sbInsetsSpace;
if (sbAvailButtonSpace < sbButtonsSpace) {
lv.incrButtonLength = lv.decrButtonLength = sbAvailButtonSpace / 2;
lv.incrButtonPosition = sbLength - insetLengthEnd - lv.incrButtonLength;
lv.decrButtonPosition = insetLengthStart;
}
/*
* Update the trackRect field.
*/
lv.trackPosition = insetLengthStart + capSize + decrGap;
lv.trackLength = lv.decrButtonPosition - incrGap - lv.trackPosition;
int gaps = decrGap + incrGap;
float trackLength = sbLength - sbInsetsSpace - sbButtonsSpace - gaps - capSize;
/*
* 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 max = sb.getMaximum();
float extent = sb.getVisibleAmount();
float range = max - min;
float value = sb.getValue();
/*
* Update the thumb bounds.
*/
lv.thumbLength = (range <= 0) ? maxThumbLength : (int) (trackLength * (extent / range));
lv.thumbLength = Math.min(Math.max(lv.thumbLength, minThumbLength), maxThumbLength);
int maxThumbPosition = lv.trackPosition + lv.trackLength - lv.thumbLength;
lv.thumbPosition = maxThumbPosition;
if (value < (max - extent)) {
float thumbRange = lv.trackLength - lv.thumbLength;
lv.thumbPosition = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
lv.thumbPosition += lv.trackPosition;
}
/*
* If the thumb isn't going to fit, zero its bounds. Otherwise make sure
* it fits between the buttons. Note that setting the thumbs bounds will
* cause a repaint.
*/
if (lv.thumbLength >= (int) trackLength) {
lv.thumbLength = 0;
} else {
if (lv.thumbPosition > maxThumbPosition) {
lv.thumbPosition = maxThumbPosition;
}
if (lv.thumbPosition < lv.trackPosition) {
lv.thumbPosition = lv.trackPosition;
}
}
}
private boolean isMouseButtonDown = false;
/**
* Track mouse drags.
*/
protected class MouseButtonListener extends MouseAdapter implements MouseListener {
protected transient int currentMouseX, currentMouseY;
public void mouseReleased(MouseEvent e) {
if (isMouseButtonDown) {
isMouseButtonDown = 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.
*/
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e) || (!getSupportsAbsolutePositioning() && SwingUtilities.isMiddleMouseButton(e)))
return;
if (!scrollbar.isEnabled()) return;
currentMouseX = e.getX();
currentMouseY = e.getY();
isMouseButtonDown = false;
// Clicked in the Thumb area?
if (getThumbBounds().contains(currentMouseX, currentMouseY)) {
isMouseButtonDown = true;
scrollbar.repaint();
}
}
}
/**
* Listener for cursor keys.
*/
protected class ArrowButtonListener extends MouseAdapter {
/*
* Because we are handling both mousePressed and Actions we need to make
* sure we don't fire under both conditions. (keyfocus on scrollbars
* causes action without mousePress
*/
boolean handledEvent;
public void mousePressed(MouseEvent e) {
if (!scrollbar.isEnabled()) {
return;
}
// not an unmodified left mouse button
// if(e.getModifiers() != InputEvent.BUTTON1_MASK) {return; }
if (!SwingUtilities.isLeftMouseButton(e)) {
return;
}
int direction = (e.getSource() == incrButton) ? 1 : -1;
scrollByUnit(direction);
scrollTimer.stop();
scrollListener.setDirection(direction);
scrollListener.setScrollByBlock(false);
scrollTimer.start();
handledEvent = true;
if (!scrollbar.hasFocus() && scrollbar.isRequestFocusEnabled()) {
scrollbar.requestFocus();
}
}
public void mouseReleased(MouseEvent e) {
scrollTimer.stop();
handledEvent = false;
scrollbar.setValueIsAdjusting(false);
}
}
}