4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/Apackage com.apple.laf;
4632N/A
4632N/Aimport java.awt.*;
4632N/Aimport java.awt.event.*;
4632N/Aimport java.beans.*;
4632N/Aimport java.util.*;
4632N/A
4632N/Aimport javax.swing.*;
4632N/Aimport javax.swing.Timer;
4632N/Aimport javax.swing.event.*;
4632N/Aimport javax.swing.plaf.*;
4632N/A
4632N/Aimport apple.laf.*;
4632N/Aimport apple.laf.JRSUIConstants.*;
4632N/Aimport apple.laf.JRSUIState.ScrollBarState;
4632N/A
4632N/Aimport com.apple.laf.AquaUtils.RecyclableSingleton;
4632N/A
4632N/Apublic class AquaScrollBarUI extends ScrollBarUI {
4632N/A private static final int kInitialDelay = 300;
4632N/A private static final int kNormalDelay = 100;
4632N/A
4632N/A // when we make small and mini scrollbars, this will no longer be a constant
4632N/A static final int MIN_ARROW_COLLAPSE_SIZE = 64;
4632N/A
4632N/A // tracking state
4632N/A protected boolean fIsDragging;
4632N/A protected Timer fScrollTimer;
4632N/A protected ScrollListener fScrollListener;
4632N/A protected TrackListener fTrackListener;
4632N/A protected Hit fTrackHighlight = Hit.NONE;
4632N/A protected Hit fMousePart = Hit.NONE; // Which arrow (if any) we moused pressed down in (used by arrow drag tracking)
4632N/A
4632N/A protected JScrollBar fScrollBar;
4632N/A protected ModelListener fModelListener;
4632N/A protected PropertyChangeListener fPropertyChangeListener;
4632N/A
4632N/A protected final AquaPainter<ScrollBarState> painter = AquaPainter.create(JRSUIStateFactory.getScrollBar());
4632N/A
4632N/A // Create PLAF
4632N/A public static ComponentUI createUI(final JComponent c) {
4632N/A return new AquaScrollBarUI();
4632N/A }
4632N/A
4632N/A public AquaScrollBarUI() { }
4632N/A
4632N/A public void installUI(final JComponent c) {
4632N/A fScrollBar = (JScrollBar)c;
4632N/A installListeners();
4632N/A configureScrollBarColors();
4632N/A }
4632N/A
4632N/A public void uninstallUI(final JComponent c) {
4632N/A uninstallListeners();
4632N/A fScrollBar = null;
4632N/A }
4632N/A
4632N/A protected void configureScrollBarColors() {
4632N/A LookAndFeel.installColors(fScrollBar, "ScrollBar.background", "ScrollBar.foreground");
4632N/A }
4632N/A
4632N/A protected TrackListener createTrackListener() {
4632N/A return new TrackListener();
4632N/A }
4632N/A
4632N/A protected ScrollListener createScrollListener() {
4632N/A return new ScrollListener();
4632N/A }
4632N/A
4632N/A protected void installListeners() {
4632N/A fTrackListener = createTrackListener();
4632N/A fModelListener = createModelListener();
4632N/A fPropertyChangeListener = createPropertyChangeListener();
4632N/A fScrollBar.addMouseListener(fTrackListener);
4632N/A fScrollBar.addMouseMotionListener(fTrackListener);
4632N/A fScrollBar.getModel().addChangeListener(fModelListener);
4632N/A fScrollBar.addPropertyChangeListener(fPropertyChangeListener);
4632N/A fScrollListener = createScrollListener();
4632N/A fScrollTimer = new Timer(kNormalDelay, fScrollListener);
4632N/A fScrollTimer.setInitialDelay(kInitialDelay); // default InitialDelay?
4632N/A }
4632N/A
4632N/A protected void uninstallListeners() {
4632N/A fScrollTimer.stop();
4632N/A fScrollTimer = null;
4632N/A fScrollBar.getModel().removeChangeListener(fModelListener);
4632N/A fScrollBar.removeMouseListener(fTrackListener);
4632N/A fScrollBar.removeMouseMotionListener(fTrackListener);
4632N/A fScrollBar.removePropertyChangeListener(fPropertyChangeListener);
4632N/A }
4632N/A
4632N/A protected PropertyChangeListener createPropertyChangeListener() {
4632N/A return new PropertyChangeHandler();
4632N/A }
4632N/A
4632N/A protected ModelListener createModelListener() {
4632N/A return new ModelListener();
4632N/A }
4632N/A
4632N/A protected void syncState(final JComponent c) {
4632N/A final ScrollBarState scrollBarState = painter.state;
4632N/A scrollBarState.set(isHorizontal() ? Orientation.HORIZONTAL : Orientation.VERTICAL);
4632N/A
4632N/A final float trackExtent = fScrollBar.getMaximum() - fScrollBar.getMinimum() - fScrollBar.getModel().getExtent();
4632N/A if (trackExtent <= 0.0f) {
4632N/A scrollBarState.set(NothingToScroll.YES);
4632N/A return;
4632N/A }
4632N/A
4632N/A final ScrollBarPart pressedPart = getPressedPart();
4632N/A scrollBarState.set(pressedPart);
4632N/A scrollBarState.set(getState(c, pressedPart));
4632N/A scrollBarState.set(NothingToScroll.NO);
4632N/A scrollBarState.setValue((fScrollBar.getValue() - fScrollBar.getMinimum()) / trackExtent);
4632N/A scrollBarState.setThumbStart(getThumbStart());
4632N/A scrollBarState.setThumbPercent(getThumbPercent());
4632N/A scrollBarState.set(shouldShowArrows() ? ShowArrows.YES : ShowArrows.NO);
4632N/A }
4632N/A
4632N/A public void paint(final Graphics g, final JComponent c) {
4632N/A syncState(c);
4632N/A painter.paint(g, c, 0, 0, fScrollBar.getWidth(), fScrollBar.getHeight());
4632N/A }
4632N/A
4632N/A protected State getState(final JComponent c, final ScrollBarPart pressedPart) {
4632N/A if (!AquaFocusHandler.isActive(c)) return State.INACTIVE;
4632N/A if (!c.isEnabled()) return State.INACTIVE;
4632N/A if (pressedPart != ScrollBarPart.NONE) return State.PRESSED;
4632N/A return State.ACTIVE;
4632N/A }
4632N/A
4632N/A static final RecyclableSingleton<Map<Hit, ScrollBarPart>> hitToPressedPartMap = new RecyclableSingleton<Map<Hit,ScrollBarPart>>(){
4632N/A @Override
4632N/A protected Map<Hit, ScrollBarPart> getInstance() {
4632N/A final Map<Hit, ScrollBarPart> map = new HashMap<Hit, ScrollBarPart>(7);
4632N/A map.put(ScrollBarHit.ARROW_MAX, ScrollBarPart.ARROW_MAX);
4632N/A map.put(ScrollBarHit.ARROW_MIN, ScrollBarPart.ARROW_MIN);
4632N/A map.put(ScrollBarHit.ARROW_MAX_INSIDE, ScrollBarPart.ARROW_MAX_INSIDE);
4632N/A map.put(ScrollBarHit.ARROW_MIN_INSIDE, ScrollBarPart.ARROW_MIN_INSIDE);
4632N/A map.put(ScrollBarHit.TRACK_MAX, ScrollBarPart.TRACK_MAX);
4632N/A map.put(ScrollBarHit.TRACK_MIN, ScrollBarPart.TRACK_MIN);
4632N/A map.put(ScrollBarHit.THUMB, ScrollBarPart.THUMB);
4632N/A return map;
4632N/A }
4632N/A };
4632N/A protected ScrollBarPart getPressedPart() {
4632N/A if (!fTrackListener.fInArrows || !fTrackListener.fStillInArrow) return ScrollBarPart.NONE;
4632N/A final ScrollBarPart pressedPart = hitToPressedPartMap.get().get(fMousePart);
4632N/A if (pressedPart == null) return ScrollBarPart.NONE;
4632N/A return pressedPart;
4632N/A }
4632N/A
4632N/A protected boolean shouldShowArrows() {
4632N/A return MIN_ARROW_COLLAPSE_SIZE < (isHorizontal() ? fScrollBar.getWidth() : fScrollBar.getHeight());
4632N/A }
4632N/A
4632N/A // Layout Methods
4632N/A // Layout is controlled by the user in the Appearance Control Panel
4632N/A // Theme will redraw correctly for the current layout
4632N/A public void layoutContainer(final Container fScrollBarContainer) {
4632N/A fScrollBar.repaint();
4632N/A fScrollBar.revalidate();
4632N/A }
4632N/A
4632N/A protected Rectangle getTrackBounds() {
4632N/A return new Rectangle(0, 0, fScrollBar.getWidth(), fScrollBar.getHeight());
4632N/A }
4632N/A
4632N/A protected Rectangle getDragBounds() {
4632N/A return new Rectangle(0, 0, fScrollBar.getWidth(), fScrollBar.getHeight());
4632N/A }
4632N/A
4632N/A protected void startTimer(final boolean initial) {
4632N/A fScrollTimer.setInitialDelay(initial ? kInitialDelay : kNormalDelay); // default InitialDelay?
4632N/A fScrollTimer.start();
4632N/A }
4632N/A
4632N/A protected void scrollByBlock(final int direction) {
4632N/A synchronized(fScrollBar) {
4632N/A final int oldValue = fScrollBar.getValue();
4632N/A final int blockIncrement = fScrollBar.getBlockIncrement(direction);
4632N/A final int delta = blockIncrement * ((direction > 0) ? +1 : -1);
4632N/A
4632N/A fScrollBar.setValue(oldValue + delta);
4632N/A fTrackHighlight = direction > 0 ? ScrollBarHit.TRACK_MAX : ScrollBarHit.TRACK_MIN;
4632N/A fScrollBar.repaint();
4632N/A fScrollListener.setDirection(direction);
4632N/A fScrollListener.setScrollByBlock(true);
4632N/A }
4632N/A }
4632N/A
4632N/A protected void scrollByUnit(final int direction) {
4632N/A synchronized(fScrollBar) {
4632N/A int delta = fScrollBar.getUnitIncrement(direction);
4632N/A if (direction <= 0) delta = -delta;
4632N/A
4632N/A fScrollBar.setValue(delta + fScrollBar.getValue());
4632N/A fScrollBar.repaint();
4632N/A fScrollListener.setDirection(direction);
4632N/A fScrollListener.setScrollByBlock(false);
4632N/A }
4632N/A }
4632N/A
4632N/A protected Hit getPartHit(final int x, final int y) {
4632N/A syncState(fScrollBar);
4632N/A return JRSUIUtils.HitDetection.getHitForPoint(painter.getControl(), 0, 0, fScrollBar.getWidth(), fScrollBar.getHeight(), x, y);
4632N/A }
4632N/A
4632N/A protected class PropertyChangeHandler implements PropertyChangeListener {
4632N/A public void propertyChange(final PropertyChangeEvent e) {
4632N/A final String propertyName = e.getPropertyName();
4632N/A
4632N/A if ("model".equals(propertyName)) {
4632N/A final BoundedRangeModel oldModel = (BoundedRangeModel)e.getOldValue();
4632N/A final BoundedRangeModel newModel = (BoundedRangeModel)e.getNewValue();
4632N/A oldModel.removeChangeListener(fModelListener);
4632N/A newModel.addChangeListener(fModelListener);
4632N/A fScrollBar.repaint();
4632N/A fScrollBar.revalidate();
4632N/A } else if (AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(propertyName)) {
4632N/A fScrollBar.repaint();
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A protected class ModelListener implements ChangeListener {
4632N/A public void stateChanged(final ChangeEvent e) {
4632N/A layoutContainer(fScrollBar);
4632N/A }
4632N/A }
4632N/A
4632N/A // Track mouse drags.
4632N/A protected class TrackListener extends MouseAdapter implements MouseMotionListener {
4632N/A protected transient int fCurrentMouseX, fCurrentMouseY;
4632N/A protected transient boolean fInArrows; // are we currently tracking arrows?
4632N/A protected transient boolean fStillInArrow = false; // Whether mouse is in an arrow during arrow tracking
4632N/A protected transient boolean fStillInTrack = false; // Whether mouse is in the track during pageup/down tracking
4632N/A protected transient int fFirstMouseX, fFirstMouseY, fFirstValue; // Values for getValueFromOffset
4632N/A
4632N/A public void mouseReleased(final MouseEvent e) {
4632N/A if (!fScrollBar.isEnabled()) return;
4632N/A if (fInArrows) {
4632N/A mouseReleasedInArrows(e);
4632N/A } else {
4632N/A mouseReleasedInTrack(e);
4632N/A }
4632N/A
4632N/A fInArrows = false;
4632N/A fStillInArrow = false;
4632N/A fStillInTrack = false;
4632N/A
4632N/A fScrollBar.repaint();
4632N/A fScrollBar.revalidate();
4632N/A }
4632N/A
4632N/A public void mousePressed(final MouseEvent e) {
4632N/A if (!fScrollBar.isEnabled()) return;
4632N/A
4632N/A final Hit part = getPartHit(e.getX(), e.getY());
4632N/A fInArrows = HitUtil.isArrow(part);
4632N/A if (fInArrows) {
4632N/A mousePressedInArrows(e, part);
4632N/A } else {
4632N/A if (part == Hit.NONE) {
4632N/A fTrackHighlight = Hit.NONE;
4632N/A } else {
4632N/A mousePressedInTrack(e, part);
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A public void mouseDragged(final MouseEvent e) {
4632N/A if (!fScrollBar.isEnabled()) return;
4632N/A
4632N/A if (fInArrows) {
4632N/A mouseDraggedInArrows(e);
4632N/A } else if (fIsDragging) {
4632N/A mouseDraggedInTrack(e);
4632N/A } else {
4632N/A // In pageup/down zones
4639N/A
4639N/A // check that thumb has not been scrolled under the mouse cursor
4639N/A final Hit previousPart = getPartHit(fCurrentMouseX, fCurrentMouseY);
4639N/A if (!HitUtil.isTrack(previousPart)) {
4639N/A fStillInTrack = false;
4639N/A }
4639N/A
4632N/A fCurrentMouseX = e.getX();
4632N/A fCurrentMouseY = e.getY();
4632N/A
4632N/A final Hit part = getPartHit(e.getX(), e.getY());
4632N/A final boolean temp = HitUtil.isTrack(part);
4632N/A if (temp == fStillInTrack) return;
4632N/A
4632N/A fStillInTrack = temp;
4632N/A if (!fStillInTrack) {
4632N/A fScrollTimer.stop();
4632N/A } else {
4632N/A fScrollListener.actionPerformed(new ActionEvent(fScrollTimer, 0, ""));
4632N/A startTimer(false);
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A int getValueFromOffset(final int xOffset, final int yOffset, final int firstValue) {
4632N/A final boolean isHoriz = isHorizontal();
4632N/A
4632N/A // find the amount of pixels we've moved x & y (we only care about one)
4632N/A final int offsetWeCareAbout = isHoriz ? xOffset : yOffset;
4632N/A
4632N/A // now based on that floating point percentage compute the real scroller value.
4632N/A final int visibleAmt = fScrollBar.getVisibleAmount();
4632N/A final int max = fScrollBar.getMaximum();
4632N/A final int min = fScrollBar.getMinimum();
4632N/A final int extent = max - min;
4632N/A
4632N/A // ask native to tell us what the new float that is a ratio of how much scrollable area
4632N/A // we have moved (not the thumb area, just the scrollable). If the
4632N/A // scroller goes 0-100 with a visible area of 20 we are getting a ratio of the
4632N/A // remaining 80.
4632N/A syncState(fScrollBar);
4632N/A final double offsetChange = JRSUIUtils.ScrollBar.getNativeOffsetChange(painter.getControl(), 0, 0, fScrollBar.getWidth(), fScrollBar.getHeight(), offsetWeCareAbout, visibleAmt, extent);
4632N/A
4632N/A // the scrollable area is the extent - visible amount;
4632N/A final int scrollableArea = extent - visibleAmt;
4632N/A
4632N/A final int changeByValue = (int)(offsetChange * scrollableArea);
4632N/A int newValue = firstValue + changeByValue;
4632N/A newValue = Math.max(min, newValue);
4632N/A newValue = Math.min((max - visibleAmt), newValue);
4632N/A return newValue;
4632N/A }
4632N/A
4632N/A /**
4632N/A * Arrow Listeners
4632N/A */
4632N/A // Because we are handling both mousePressed and Actions
4632N/A // we need to make sure we don't fire under both conditions.
4632N/A // (keyfocus on scrollbars causes action without mousePress
4632N/A void mousePressedInArrows(final MouseEvent e, final Hit part) {
4632N/A final int direction = HitUtil.isIncrement(part) ? 1 : -1;
4632N/A
4632N/A fStillInArrow = true;
4632N/A scrollByUnit(direction);
4632N/A fScrollTimer.stop();
4632N/A fScrollListener.setDirection(direction);
4632N/A fScrollListener.setScrollByBlock(false);
4632N/A
4632N/A fMousePart = part;
4632N/A startTimer(true);
4632N/A }
4632N/A
4632N/A void mouseReleasedInArrows(final MouseEvent e) {
4632N/A fScrollTimer.stop();
4632N/A fMousePart = Hit.NONE;
4632N/A fScrollBar.setValueIsAdjusting(false);
4632N/A }
4632N/A
4632N/A void mouseDraggedInArrows(final MouseEvent e) {
4632N/A final Hit whichPart = getPartHit(e.getX(), e.getY());
4632N/A
4632N/A if ((fMousePart == whichPart) && fStillInArrow) return; // Nothing has changed, so return
4632N/A
4632N/A if (fMousePart != whichPart && !HitUtil.isArrow(whichPart)) {
4632N/A // The mouse is not over the arrow we mouse pressed in, so stop the timer and mark as
4632N/A // not being in the arrow
4632N/A fScrollTimer.stop();
4632N/A fStillInArrow = false;
4632N/A fScrollBar.repaint();
4632N/A } else {
4632N/A // We are in the arrow we mouse pressed down in originally, but the timer was stopped so we need
4632N/A // to start it up again.
4632N/A fMousePart = whichPart;
4632N/A fScrollListener.setDirection(HitUtil.isIncrement(whichPart) ? 1 : -1);
4632N/A fStillInArrow = true;
4632N/A fScrollListener.actionPerformed(new ActionEvent(fScrollTimer, 0, ""));
4632N/A startTimer(false);
4632N/A }
4632N/A
4632N/A fScrollBar.repaint();
4632N/A }
4632N/A
4632N/A void mouseReleasedInTrack(final MouseEvent e) {
4632N/A if (fTrackHighlight != Hit.NONE) {
4632N/A fScrollBar.repaint();
4632N/A }
4632N/A
4632N/A fTrackHighlight = Hit.NONE;
4632N/A fIsDragging = false;
4632N/A fScrollTimer.stop();
4632N/A fScrollBar.setValueIsAdjusting(false);
4632N/A }
4632N/A
4632N/A /**
4632N/A * Adjust the fScrollBars value based on the result of hitTestTrack
4632N/A */
4632N/A void mousePressedInTrack(final MouseEvent e, final Hit part) {
4632N/A fScrollBar.setValueIsAdjusting(true);
4632N/A
4632N/A // If option-click, toggle scroll-to-here
4632N/A boolean shouldScrollToHere = (part != ScrollBarHit.THUMB) && JRSUIUtils.ScrollBar.useScrollToClick();
4632N/A if (e.isAltDown()) shouldScrollToHere = !shouldScrollToHere;
4632N/A
4632N/A // pretend the mouse was dragged from a point in the current thumb to the current mouse point in one big jump
4632N/A if (shouldScrollToHere) {
4632N/A final Point p = getScrollToHereStartPoint(e.getX(), e.getY());
4632N/A fFirstMouseX = p.x;
4632N/A fFirstMouseY = p.y;
4632N/A fFirstValue = fScrollBar.getValue();
4632N/A moveToMouse(e);
4632N/A
4632N/A // OK, now we're in the thumb - any subsequent dragging should move it
4632N/A fTrackHighlight = ScrollBarHit.THUMB;
4632N/A fIsDragging = true;
4632N/A return;
4632N/A }
4632N/A
4632N/A fCurrentMouseX = e.getX();
4632N/A fCurrentMouseY = e.getY();
4632N/A
4632N/A int direction = 0;
4632N/A if (part == ScrollBarHit.TRACK_MIN) {
4632N/A fTrackHighlight = ScrollBarHit.TRACK_MIN;
4632N/A direction = -1;
4632N/A } else if (part == ScrollBarHit.TRACK_MAX) {
4632N/A fTrackHighlight = ScrollBarHit.TRACK_MAX;
4632N/A direction = 1;
4632N/A } else {
4632N/A fFirstValue = fScrollBar.getValue();
4632N/A fFirstMouseX = fCurrentMouseX;
4632N/A fFirstMouseY = fCurrentMouseY;
4632N/A fTrackHighlight = ScrollBarHit.THUMB;
4632N/A fIsDragging = true;
4632N/A return;
4632N/A }
4632N/A
4632N/A fIsDragging = false;
4632N/A fStillInTrack = true;
4632N/A
4632N/A scrollByBlock(direction);
4632N/A // Check the new location of the thumb
4632N/A // stop scrolling if the thumb is under the mouse??
4632N/A
4632N/A final Hit newPart = getPartHit(fCurrentMouseX, fCurrentMouseY);
4632N/A if (newPart == ScrollBarHit.TRACK_MIN || newPart == ScrollBarHit.TRACK_MAX) {
4632N/A fScrollTimer.stop();
4632N/A fScrollListener.setDirection(((newPart == ScrollBarHit.TRACK_MAX) ? 1 : -1));
4632N/A fScrollListener.setScrollByBlock(true);
4632N/A startTimer(true);
4632N/A }
4632N/A }
4632N/A
4632N/A /**
4632N/A * Set the models value to the position of the top/left
4632N/A * of the thumb relative to the origin of the track.
4632N/A */
4632N/A void mouseDraggedInTrack(final MouseEvent e) {
4632N/A moveToMouse(e);
4632N/A }
4632N/A
4632N/A // For normal mouse dragging or click-to-here
4632N/A // fCurrentMouseX, fCurrentMouseY, and fFirstValue must be set
4632N/A void moveToMouse(final MouseEvent e) {
4632N/A fCurrentMouseX = e.getX();
4632N/A fCurrentMouseY = e.getY();
4632N/A
4632N/A final int oldValue = fScrollBar.getValue();
4632N/A final int newValue = getValueFromOffset(fCurrentMouseX - fFirstMouseX, fCurrentMouseY - fFirstMouseY, fFirstValue);
4632N/A if (newValue == oldValue) return;
4632N/A
4632N/A fScrollBar.setValue(newValue);
4632N/A final Rectangle dirtyRect = getTrackBounds();
4632N/A fScrollBar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
4632N/A }
4632N/A }
4632N/A
4632N/A /**
4632N/A * Listener for scrolling events initiated in the ScrollPane.
4632N/A */
4632N/A protected class ScrollListener implements ActionListener {
4632N/A boolean fUseBlockIncrement;
4632N/A int fDirection = 1;
4632N/A
4632N/A void setDirection(final int direction) {
4632N/A this.fDirection = direction;
4632N/A }
4632N/A
4632N/A void setScrollByBlock(final boolean block) {
4632N/A this.fUseBlockIncrement = block;
4632N/A }
4632N/A
4632N/A public void actionPerformed(final ActionEvent e) {
4632N/A if (fUseBlockIncrement) {
4632N/A Hit newPart = getPartHit(fTrackListener.fCurrentMouseX, fTrackListener.fCurrentMouseY);
4632N/A
4632N/A if (newPart == ScrollBarHit.TRACK_MIN || newPart == ScrollBarHit.TRACK_MAX) {
4632N/A final int newDirection = (newPart == ScrollBarHit.TRACK_MAX ? 1 : -1);
4632N/A if (fDirection != newDirection) {
4632N/A fDirection = newDirection;
4632N/A }
4632N/A }
4632N/A
4632N/A scrollByBlock(fDirection);
4632N/A newPart = getPartHit(fTrackListener.fCurrentMouseX, fTrackListener.fCurrentMouseY);
4632N/A
4632N/A if (newPart == ScrollBarHit.THUMB) {
4632N/A ((Timer)e.getSource()).stop();
4632N/A }
4632N/A } else {
4632N/A scrollByUnit(fDirection);
4632N/A }
4632N/A
4632N/A if (fDirection > 0 && fScrollBar.getValue() + fScrollBar.getVisibleAmount() >= fScrollBar.getMaximum()) {
4632N/A ((Timer)e.getSource()).stop();
4632N/A } else if (fDirection < 0 && fScrollBar.getValue() <= fScrollBar.getMinimum()) {
4632N/A ((Timer)e.getSource()).stop();
4632N/A }
4632N/A }
4632N/A }
4632N/A
4632N/A float getThumbStart() {
4632N/A final int max = fScrollBar.getMaximum();
4632N/A final int min = fScrollBar.getMinimum();
4632N/A final int extent = max - min;
4632N/A if (extent <= 0) return 0f;
4632N/A
4632N/A return (float)(fScrollBar.getValue() - fScrollBar.getMinimum()) / (float)extent;
4632N/A }
4632N/A
4632N/A float getThumbPercent() {
4632N/A final int visible = fScrollBar.getVisibleAmount();
4632N/A final int max = fScrollBar.getMaximum();
4632N/A final int min = fScrollBar.getMinimum();
4632N/A final int extent = max - min;
4632N/A if (extent <= 0) return 0f;
4632N/A
4632N/A return (float)visible / (float)extent;
4632N/A }
4632N/A
4632N/A /**
4632N/A * A scrollbar's preferred width is 16 by a reasonable size to hold
4632N/A * the arrows
4632N/A *
4632N/A * @param c The JScrollBar that's delegating this method to us.
4632N/A * @return The preferred size of a Basic JScrollBar.
4632N/A * @see #getMaximumSize
4632N/A * @see #getMinimumSize
4632N/A */
4632N/A public Dimension getPreferredSize(final JComponent c) {
4632N/A return isHorizontal() ? new Dimension(96, 15) : new Dimension(15, 96);
4632N/A }
4632N/A
4632N/A public Dimension getMinimumSize(final JComponent c) {
4632N/A return isHorizontal() ? new Dimension(54, 15) : new Dimension(15, 54);
4632N/A }
4632N/A
4632N/A public Dimension getMaximumSize(final JComponent c) {
4632N/A return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
4632N/A }
4632N/A
4632N/A boolean isHorizontal() {
4632N/A return fScrollBar.getOrientation() == Adjustable.HORIZONTAL;
4632N/A }
4632N/A
4632N/A // only do scroll-to-here for page up and page down regions, when the option key is pressed
4632N/A // This gets the point where the mouse would have been clicked in the current thumb
4632N/A // so we can pretend the mouse was dragged to the current mouse point in one big jump
4632N/A Point getScrollToHereStartPoint(final int clickPosX, final int clickPosY) {
4632N/A // prepare the track rectangle and limit rectangle so we can do our calculations
4632N/A final Rectangle limitRect = getDragBounds(); // GetThemeTrackDragRect
4632N/A
4632N/A // determine the bounding rectangle for our thumb region
4632N/A syncState(fScrollBar);
4632N/A double[] rect = new double[4];
4632N/A JRSUIUtils.ScrollBar.getPartBounds(rect, painter.getControl(), 0, 0, fScrollBar.getWidth(), fScrollBar.getHeight(), ScrollBarPart.THUMB);
4632N/A final Rectangle r = new Rectangle((int)rect[0], (int)rect[1], (int)rect[2], (int)rect[3]);
4632N/A
4632N/A // figure out the scroll-to-here start location based on our orientation, the
4632N/A // click position, and where it must be in the thumb to travel to the endpoints
4632N/A // properly.
4632N/A final Point startPoint = new Point(clickPosX, clickPosY);
4632N/A
4632N/A if (isHorizontal()) {
4632N/A final int halfWidth = r.width / 2;
4632N/A final int limitRectRight = limitRect.x + limitRect.width;
4632N/A
4632N/A if (clickPosX + halfWidth > limitRectRight) {
4632N/A // Up against right edge
4632N/A startPoint.x = r.x + r.width - limitRectRight - clickPosX - 1;
4632N/A } else if (clickPosX - halfWidth < limitRect.x) {
4632N/A // Up against left edge
4632N/A startPoint.x = r.x + clickPosX - limitRect.x;
4632N/A } else {
4632N/A // Center the thumb
4632N/A startPoint.x = r.x + halfWidth;
4632N/A }
4632N/A
4632N/A // Pretend clicked in middle of indicator vertically
4632N/A startPoint.y = (r.y + r.height) / 2;
4632N/A return startPoint;
4632N/A }
4632N/A
4632N/A final int halfHeight = r.height / 2;
4632N/A final int limitRectBottom = limitRect.y + limitRect.height;
4632N/A
4632N/A if (clickPosY + halfHeight > limitRectBottom) {
4632N/A // Up against bottom edge
4632N/A startPoint.y = r.y + r.height - limitRectBottom - clickPosY - 1;
4632N/A } else if (clickPosY - halfHeight < limitRect.y) {
4632N/A // Up against top edge
4632N/A startPoint.y = r.y + clickPosY - limitRect.y;
4632N/A } else {
4632N/A // Center the thumb
4632N/A startPoint.y = r.y + halfHeight;
4632N/A }
4632N/A
4632N/A // Pretend clicked in middle of indicator horizontally
4632N/A startPoint.x = (r.x + r.width) / 2;
4632N/A
4632N/A return startPoint;
4632N/A }
4632N/A
4632N/A static class HitUtil {
4632N/A static boolean isIncrement(final Hit hit) {
4632N/A return (hit == ScrollBarHit.ARROW_MAX) || (hit == ScrollBarHit.ARROW_MAX_INSIDE);
4632N/A }
4632N/A
4632N/A static boolean isDecrement(final Hit hit) {
4632N/A return (hit == ScrollBarHit.ARROW_MIN) || (hit == ScrollBarHit.ARROW_MIN_INSIDE);
4632N/A }
4632N/A
4632N/A static boolean isArrow(final Hit hit) {
4632N/A return isIncrement(hit) || isDecrement(hit);
4632N/A }
4632N/A
4632N/A static boolean isTrack(final Hit hit) {
4632N/A return (hit == ScrollBarHit.TRACK_MAX) || (hit == ScrollBarHit.TRACK_MIN);
4632N/A }
4632N/A }
4632N/A}