edu.umd.cs.piccolox.swing.PScrollPane Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2011, Piccolo2D project, http://piccolo2d.org
* Copyright (c) 1998-2008, University of Maryland
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials provided with the
* distribution.
*
* None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
* contributors may be used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package edu.umd.cs.piccolox.swing;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.plaf.ScrollPaneUI;
import edu.umd.cs.piccolo.PCanvas;
/**
* A simple extension to a standard scroll pane that uses the jazz version of
* the viewport by default. Also uses the jazz version of ScrollPaneLayout
*
* @author Lance Good
*/
public class PScrollPane extends JScrollPane {
private static final long serialVersionUID = 1L;
/** A reusable null action. */
protected PNullAction nullAction = null;
/** Controls whether key actions are disabled on this component. */
protected boolean disableKeyActions = false;
private final AdjustmentListener scrollAdjustmentListener = new AdjustmentListener() {
private boolean lastAdjustingState = false;
public void adjustmentValueChanged(final AdjustmentEvent event) {
if (event.getSource() instanceof JScrollBar) {
JScrollBar scrollBar = (JScrollBar) event.getSource();
setAdjusting(scrollBar.getValueIsAdjusting());
}
}
/**
* Updates the underlying PCanvas' interacting flag depending on whether
* scroll bar adjustments are still taking place.
*
* @param isAdjusting true if the scroll bar is still being interacted
* with
*/
private void setAdjusting(final boolean isAdjusting) {
if (isAdjusting != lastAdjustingState) {
Component c = getViewport().getView();
if (c instanceof PCanvas) {
((PCanvas) c).setInteracting(isAdjusting);
}
lastAdjustingState = isAdjusting;
}
}
};
/**
* Constructs a scollpane for the provided component with the specified
* scrollbar policies.
*
* @param view component being viewed through the scrollpane
* @param vsbPolicy vertical scroll bar policy
* @param hsbPolicy horizontal scroll bar policy
*/
public PScrollPane(final Component view, final int vsbPolicy, final int hsbPolicy) {
super(view, vsbPolicy, hsbPolicy);
// Set the layout and sync it with the scroll pane
final PScrollPaneLayout layout = new PScrollPaneLayout.UIResource();
setLayout(layout);
layout.syncWithScrollPane(this);
horizontalScrollBar.addAdjustmentListener(scrollAdjustmentListener);
verticalScrollBar.addAdjustmentListener(scrollAdjustmentListener);
}
/**
* Intercepts the vertical scroll bar setter to ensure that the adjustment
* listener is installed appropriately.
*
* @param newVerticalScrollBar the new vertical scroll bar to use with this PScrollPane
*/
public void setVerticalScrollBar(final JScrollBar newVerticalScrollBar) {
if (verticalScrollBar != null) {
verticalScrollBar.removeAdjustmentListener(scrollAdjustmentListener);
}
super.setVerticalScrollBar(newVerticalScrollBar);
newVerticalScrollBar.addAdjustmentListener(scrollAdjustmentListener);
}
/**
* Intercepts the horizontal scroll bar setter to ensure that the adjustment
* listener is installed appropriately.
*
* @param newHorizontalScrollBar the new horizontal scroll bar to use with this PScrollPane
*/
public void setHorizontalScrollBar(final JScrollBar newHorizontalScrollBar) {
if (horizontalScrollBar != null) {
horizontalScrollBar.removeAdjustmentListener(scrollAdjustmentListener);
}
super.setHorizontalScrollBar(newHorizontalScrollBar);
newHorizontalScrollBar.addAdjustmentListener(scrollAdjustmentListener);
}
/**
* Constructs a scroll pane for the provided component.
*
* @param view component being viewed through the scroll pane
*/
public PScrollPane(final Component view) {
this(view, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
/**
* Constructs a scroll pane not attached to any component with the specified
* scroll bar policies.
*
* @param vsbPolicy vertical scroll bar policy
* @param hsbPolicy horizontal scroll bar policy
*/
public PScrollPane(final int vsbPolicy, final int hsbPolicy) {
this(null, vsbPolicy, hsbPolicy);
}
/**
* Constructs a scroll pane not attached to any component.
*/
public PScrollPane() {
this(null, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
/**
* Disable or enable key actions on this PScrollPane.
*
* @param disable true disables key actions, false enables key actions
*/
public void setKeyActionsDisabled(final boolean disable) {
if (disable && disableKeyActions != disable) {
disableKeyActions = disable;
disableKeyActions();
}
else if (!disable && disableKeyActions != disable) {
disableKeyActions = disable;
installCustomKeyActions();
}
}
/**
* Sets the UI.
*
* @param ui the scroll pane UI to associate with this PScollPane
*/
public void setUI(final ScrollPaneUI ui) {
super.setUI(ui);
if (!disableKeyActions) {
installCustomKeyActions();
}
else {
disableKeyActions();
}
}
/**
* Install custom key actions (in place of the Swing defaults) to correctly
* scroll the view.
*/
protected void installCustomKeyActions() {
final ActionMap map = getActionMap();
map.put("scrollUp", new PScrollAction("scrollUp", SwingConstants.VERTICAL, -1, true));
map.put("scrollDown", new PScrollAction("scrollDown", SwingConstants.VERTICAL, 1, true));
map.put("scrollLeft", new PScrollAction("scrollLeft", SwingConstants.HORIZONTAL, -1, true));
map.put("scrollRight", new PScrollAction("ScrollRight", SwingConstants.HORIZONTAL, 1, true));
map.put("unitScrollRight", new PScrollAction("UnitScrollRight", SwingConstants.HORIZONTAL, 1, false));
map.put("unitScrollLeft", new PScrollAction("UnitScrollLeft", SwingConstants.HORIZONTAL, -1, false));
map.put("unitScrollUp", new PScrollAction("UnitScrollUp", SwingConstants.VERTICAL, -1, false));
map.put("unitScrollDown", new PScrollAction("UnitScrollDown", SwingConstants.VERTICAL, 1, false));
map.put("scrollEnd", new PScrollEndAction("ScrollEnd"));
map.put("scrollHome", new PScrollHomeAction("ScrollHome"));
}
/**
* Disables key actions on this PScrollPane.
*/
protected void disableKeyActions() {
final ActionMap map = getActionMap();
if (nullAction == null) {
nullAction = new PNullAction();
}
map.put("scrollUp", nullAction);
map.put("scrollDown", nullAction);
map.put("scrollLeft", nullAction);
map.put("scrollRight", nullAction);
map.put("unitScrollRight", nullAction);
map.put("unitScrollLeft", nullAction);
map.put("unitScrollUp", nullAction);
map.put("unitScrollDown", nullAction);
map.put("scrollEnd", nullAction);
map.put("scrollHome", nullAction);
}
/**
* Overridden to create the Piccolo2D viewport.
*
* @return the Piccolo2D version of the viewport
*/
protected JViewport createViewport() {
return new PViewport();
}
/**
* Action to scroll left/right/up/down. Modified from
* javax.swing.plaf.basic.BasicScrollPaneUI.ScrollAction.
*
* Gets the view parameters (position and size) from the Viewport rather
* than directly from the view - also only performs its actions when the
* relevant scrollbar is visible.
*/
protected static class PScrollAction extends AbstractAction {
private static final int MINIMUM_SCROLL_SIZE = 10;
private static final long serialVersionUID = 1L;
/** Direction to scroll. */
protected int orientation;
/** 1 indicates scroll down, -1 up. */
protected int direction;
/** True indicates a block scroll, otherwise a unit scroll. */
private final boolean block;
/**
* Constructs a scroll action with the given name in the given
* orientiation stated and in the direction provided.
*
* @param name arbitrary name of action
* @param orientation horizontal or vertical
* @param direction 1 indicates scroll down, -1 up
* @param block true if block scroll as opposed to unit
*/
protected PScrollAction(final String name, final int orientation, final int direction, final boolean block) {
super(name);
this.orientation = orientation;
this.direction = direction;
this.block = block;
}
/**
* Performs the scroll action if the action was performed on visible
* scrollbars and if the viewport is valid.
*
* @param event the event responsible for this action being performed
*/
public void actionPerformed(final ActionEvent event) {
final JScrollPane scrollpane = (JScrollPane) event.getSource();
if (!isScrollEventOnVisibleScrollbars(scrollpane)) {
return;
}
final JViewport vp = scrollpane.getViewport();
if (vp == null) {
return;
}
Component view = vp.getView();
if (view == null) {
return;
}
final Rectangle visRect = vp.getViewRect();
// LEG: Modification to query the viewport for the
// view size rather than going directly to the view
final Dimension vSize = vp.getViewSize();
final int amount;
if (view instanceof Scrollable) {
if (block) {
amount = ((Scrollable) view).getScrollableBlockIncrement(visRect, orientation, direction);
}
else {
amount = ((Scrollable) view).getScrollableUnitIncrement(visRect, orientation, direction);
}
}
else {
if (block) {
if (orientation == SwingConstants.VERTICAL) {
amount = visRect.height;
}
else {
amount = visRect.width;
}
}
else {
amount = MINIMUM_SCROLL_SIZE;
}
}
if (orientation == SwingConstants.VERTICAL) {
visRect.y += amount * direction;
if (visRect.y + visRect.height > vSize.height) {
visRect.y = Math.max(0, vSize.height - visRect.height);
}
else if (visRect.y < 0) {
visRect.y = 0;
}
}
else {
visRect.x += amount * direction;
if (visRect.x + visRect.width > vSize.width) {
visRect.x = Math.max(0, vSize.width - visRect.width);
}
else if (visRect.x < 0) {
visRect.x = 0;
}
}
vp.setViewPosition(visRect.getLocation());
}
private boolean isScrollEventOnVisibleScrollbars(final JScrollPane scrollpane) {
return orientation == SwingConstants.VERTICAL && scrollpane.getVerticalScrollBar().isShowing()
|| orientation == SwingConstants.HORIZONTAL && scrollpane.getHorizontalScrollBar().isShowing();
}
}
/**
* Action to scroll to x,y location of 0,0. Modified from
* javax.swing.plaf.basic.BasicScrollPaneUI.ScrollEndAction.
*
* Only performs the event if a scrollbar is visible.
*/
private static class PScrollHomeAction extends AbstractAction {
private static final long serialVersionUID = 1L;
protected PScrollHomeAction(final String name) {
super(name);
}
public void actionPerformed(final ActionEvent e) {
final JScrollPane scrollpane = (JScrollPane) e.getSource();
// LEG: Modification to only perform these actions if one of the
// scrollbars is actually showing
if (scrollpane.getVerticalScrollBar().isShowing() || scrollpane.getHorizontalScrollBar().isShowing()) {
final JViewport vp = scrollpane.getViewport();
if (vp != null && vp.getView() != null) {
vp.setViewPosition(new Point(0, 0));
}
}
}
}
/**
* Action to scroll to last visible location. Modified from
* javax.swing.plaf.basic.BasicScrollPaneUI.ScrollEndAction.
*
* Gets the view size from the viewport rather than directly from the view -
* also only performs the event if a scrollbar is visible.
*/
protected static class PScrollEndAction extends AbstractAction {
private static final long serialVersionUID = 1L;
/**
* Constructs a scroll to end action with the given name.
*
* @param name name to assign to this action
*/
protected PScrollEndAction(final String name) {
super(name);
}
/**
* Scrolls to the end of the viewport if there are visible scrollbars.
*
* @param event event responsible for the scroll event
*/
public void actionPerformed(final ActionEvent event) {
final JScrollPane scrollpane = (JScrollPane) event.getSource();
// LEG: Modification to only perform these actions if one of the
// scrollbars is actually showing
if (scrollpane.getVerticalScrollBar().isShowing() || scrollpane.getHorizontalScrollBar().isShowing()) {
final JViewport vp = scrollpane.getViewport();
if (vp != null && vp.getView() != null) {
final Rectangle visRect = vp.getViewRect();
// LEG: Modification to query the viewport for the
// view size rather than going directly to the view
final Dimension size = vp.getViewSize();
vp.setViewPosition(new Point(size.width - visRect.width, size.height - visRect.height));
}
}
}
}
/**
* An action to do nothing - put into an action map to keep it from looking
* to its parent.
*/
protected static class PNullAction extends AbstractAction {
private static final long serialVersionUID = 1L;
/**
* Does nothing.
*
* @param e Event responsible for this action
*/
public void actionPerformed(final ActionEvent e) {
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy