// AP(r) Computer Science Marine Biology Simulation:
// The PseudoInfiniteViewport class is copyright(c) 2002 College Entrance
// Examination Board (www.collegeboard.com).
//
// This class is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation.
//
// This class is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
package edu.kzoo.grid.display;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolTip;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
/**
* AP® Computer Science Marine Biology Simulation:
* A PseudoInfiniteViewport
is a
* JViewport
subclass that
* translates scroll actions into pan actions across an
* unbounded view.
*
*
* The PseudoInfiniteViewport
class is
* copyright© 2002 College Entrance Examination Board
* (www.collegeboard.com).
*
* @author Julie Zelenski
* @version 1 August 2002
**/
public class PseudoInfiniteViewport extends JViewport
{
/** The Pannable
interface contains those methods that
* the view in a PseudoInfiniteViewport
needs to support
* to enable panning behavior along with scrolling.
**/
public interface Pannable
{
void panBy(int hDelta, int vDelta);
boolean isPannableUnbounded();
String getPannableTipText(); // return null if no tip desired
}
private static final int ORIGIN_TIP_DELAY = 1000;
private JScrollPane scrollParent;
private JPanel glassPane;
private JToolTip originTip;
private Timer originTipTimer;
private Point panPoint = new Point(0,0);
/** Construct a new PseudoInfiniteViewport object for the given scrollpane.
* @param parent the JScrollPane for which this will be the viewport
**/
public PseudoInfiniteViewport(JScrollPane parent)
{
scrollParent = parent;
setBackground(Color.lightGray);
}
/** Sets the view position (upper left) to a new point. Overridden from
* JViewport to do a pan, instead of scroll, on an unbounded view.
* @param pt the Point to become the upper left
**/
public void setViewPosition(Point pt)
{
boolean isAdjusting = scrollParent.getVerticalScrollBar().getValueIsAdjusting()
|| scrollParent.getHorizontalScrollBar().getValueIsAdjusting();
boolean changed = true;
if (viewIsUnbounded())
{
int hDelta = pt.x - panPoint.x;
int vDelta = pt.y - panPoint.y;
if (hDelta != 0 && vDelta == 0)
getPannableView().panBy(hDelta, vDelta);
else if (vDelta != 0 && hDelta == 0)
getPannableView().panBy(hDelta, vDelta);
else
changed = false; // no pan action was taken
panPoint = pt;
if (!panPoint.equals(getPanCenterPoint()) && !isAdjusting)
{ // needs recentering
panPoint = getPanCenterPoint();
fireStateChanged(); // update scrollbars to match
}
}
else // ordinary scroll behavior
{
changed = !getViewPosition().equals(pt);
super.setViewPosition(pt);
}
if (changed || isAdjusting) showOriginTip(); // briefly show tip
}
/** Returns current view position (upper left). Overridden from
* JViewport to use pan center point for unbounded view.
**/
public Point getViewPosition()
{
return (viewIsUnbounded() ? getPanCenterPoint() : super.getViewPosition());
}
/** Returns current view size. Overridden from
* JViewport to use preferred virtual size for unbounded view.
**/
public Dimension getViewSize()
{
return (viewIsUnbounded() ? getView().getPreferredSize() : super.getViewSize());
}
/** Shows a tool tip over the upper left corner of the viewport
* with the contents of the pannable view's pannable tip text
* (typically a string identifiying the corner point). Tip is
* removed after a short delay.
**/
public void showOriginTip()
{
if (getRootPane() == null) return;
// draw in glass pane to appear on top of other components
if (glassPane == null)
{
getRootPane().setGlassPane(glassPane = new JPanel());
glassPane.setOpaque(false);
glassPane.setLayout(null); // will control layout manually
glassPane.add(originTip = new JToolTip());
originTipTimer = new Timer(ORIGIN_TIP_DELAY, new ActionListener() {
public void actionPerformed(ActionEvent evt) { glassPane.setVisible(false); }});
originTipTimer.setRepeats(false);
}
String tipText = getPannableView().getPannableTipText();
if (tipText == null) return;
// set tip text to identify current origin of pannable view
originTip.setTipText(tipText);
// position tip to appear at upper left corner of viewport
originTip.setLocation(SwingUtilities.convertPoint(this, getLocation(), glassPane));
originTip.setSize(originTip.getPreferredSize());
// show glass pane (it contains tip)
glassPane.setVisible(true);
// this timer will hide the glass pane after a short delay
originTipTimer.restart();
}
// some simple private helpers
private Pannable getPannableView()
{
return (Pannable)getView();
}
private boolean viewIsUnbounded()
{
Pannable p = getPannableView();
return (p != null && p.isPannableUnbounded());
}
private Point getPanCenterPoint()
{
Dimension size = getViewSize();
return new Point(size.width/2, size.height/2);
}
}