/* * Copyright (c) 2008-2009, 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 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 { /** * */ private static final long serialVersionUID = 1L; // 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(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); } /** * Pass on the constructor info to the super */ public PScrollPane(final Component view) { this(view, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); } /** * Pass on the constructor info to the super */ public PScrollPane(final int vsbPolicy, final 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(final boolean disable) { if (disable && disableKeyActions != disable) { disableKeyActions = disable; disableKeyActions(); } else if (!disable && disableKeyActions != disable) { disableKeyActions = disable; installCustomKeyActions(); } } /** * Sets the UI */ 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 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 { /** * */ 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; 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; } public void actionPerformed(final ActionEvent e) { final 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()) { final JViewport vp = scrollpane.getViewport(); Component view; if (vp != null && (view = 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 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 { /** * */ 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; protected PScrollEndAction(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) { 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; public void actionPerformed(final ActionEvent e) { } } }