/*
* Copyright (C) 2002-@year@ by University of Maryland, College Park, MD 20742, USA
* All rights reserved.
*
* Piccolo was written at the Human-Computer Interaction Laboratory
* www.cs.umd.edu/hcil by Jesse Grosjean under the supervision of Ben Bederson.
* The Piccolo website is www.cs.umd.edu/hcil/piccolo
*/
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 javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.plaf.ScrollPaneUI;
/**
* 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 {
// A reusable null action
protected PNullAction nullAction = null;
// Are key actions disabled on this component?
protected boolean disableKeyActions = false;
/**
* Pass on the constructor info to the super
*/
public PScrollPane(Component view, int vsbPolicy, int hsbPolicy) {
super(view, vsbPolicy, hsbPolicy);
// Set the layout and sync it with the scroll pane
PScrollPaneLayout layout = new PScrollPaneLayout.UIResource();
setLayout(layout);
layout.syncWithScrollPane(this);
}
/**
* Pass on the constructor info to the super
*/
public PScrollPane(Component view) {
this(view, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
/**
* Pass on the constructor info to the super
*/
public PScrollPane(int vsbPolicy, int hsbPolicy) {
this(null, vsbPolicy, hsbPolicy);
}
/**
* Pass on the constructor info to the super
*/
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(boolean disable) {
if (disable && this.disableKeyActions != disable) {
this.disableKeyActions = disable;
disableKeyActions();
}
else if (!disable && this.disableKeyActions != disable) {
this.disableKeyActions = disable;
installCustomKeyActions();
}
}
/**
* Sets the UI
*/
public void setUI(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() {
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() {
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 Jazz viewport
* @return The jazz 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 {
/** 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 boolean block;
protected PScrollAction(String name, int orientation, int direction, boolean block) {
super(name);
this.orientation = orientation;
this.direction = direction;
this.block = block;
}
public void actionPerformed(ActionEvent e) {
JScrollPane scrollpane = (JScrollPane) e.getSource();
// LEG: Modification to only perform these actions if the relevant
// scrollbar is actually showing
if ((orientation == SwingConstants.VERTICAL && scrollpane.getVerticalScrollBar().isShowing())
|| (orientation == SwingConstants.HORIZONTAL && scrollpane.getHorizontalScrollBar().isShowing())) {
JViewport vp = scrollpane.getViewport();
Component view;
if (vp != null && (view = vp.getView()) != null) {
Rectangle visRect = vp.getViewRect();
// LEG: Modification to query the viewport for the
// view size rather than going directly to the view
Dimension vSize = vp.getViewSize();
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 = 10;
}
}
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());
}
}
}
}
/**
* 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 {
protected PScrollHomeAction(String name) {
super(name);
}
public void actionPerformed(ActionEvent e) {
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()) {
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 {
protected PScrollEndAction(String name) {
super(name);
}
public void actionPerformed(ActionEvent e) {
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()) {
JViewport vp = scrollpane.getViewport();
if (vp != null && vp.getView() != null) {
Rectangle visRect = vp.getViewRect();
// LEG: Modification to query the viewport for the
// view size rather than going directly to the view
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 {
public void actionPerformed(ActionEvent e) {
}
}
}