package edu.umd.cs.piccolo.examples;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import javax.swing.ButtonGroup;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import edu.umd.cs.piccolo.PCanvas;
import edu.umd.cs.piccolo.PLayer;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.util.PAffineTransform;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolox.PFrame;
import edu.umd.cs.piccolox.swing.PDefaultScrollDirector;
import edu.umd.cs.piccolox.swing.PScrollDirector;
import edu.umd.cs.piccolox.swing.PScrollPane;
import edu.umd.cs.piccolox.swing.PViewport;
/**
* This creates a simple scene and allows switching between
* traditional scrolling where the scrollbars control the view
* and alternate scrolling where the scrollbars control the
* document. In laymans terms - in traditional window scrolling the stuff
* in the window moves in the opposite direction of the scroll bars and in
* document scrolling the stuff moves in the same direction as the scrollbars.
*
* Toggle buttons are provided to toggle between these two types
* of scrolling.
*
* @author Lance Good
* @author Ben Bederson
*/
public class ScrollingExample extends PFrame {
public ScrollingExample() {
this(null);
}
public ScrollingExample(PCanvas aCanvas) {
super("ScrollingExample", false, aCanvas);
}
public void initialize() {
final PCanvas canvas = getCanvas();
final PScrollPane scrollPane = new PScrollPane(canvas);
getContentPane().add(scrollPane);
final PViewport viewport = (PViewport) scrollPane.getViewport();
final PScrollDirector windowSD = viewport.getScrollDirector();
final PScrollDirector documentSD = new DocumentScrollDirector();
// Make some rectangles on the surface so we can see where we are
for (int x = 0; x < 20; x++) {
for (int y = 0; y < 20; y++) {
if (((x + y) % 2) == 0) {
PPath path = PPath.createRectangle(50 * x, 50 * y, 40, 40);
path.setPaint(Color.blue);
path.setStrokePaint(Color.black);
canvas.getLayer().addChild(path);
}
else if (((x + y) % 2) == 1) {
PPath path = PPath.createEllipse(50 * x, 50 * y, 40, 40);
path.setPaint(Color.blue);
path.setStrokePaint(Color.black);
canvas.getLayer().addChild(path);
}
}
}
// Now, create the toolbar
JToolBar toolBar = new JToolBar();
JToggleButton window = new JToggleButton("Window Scrolling");
JToggleButton document = new JToggleButton("Document Scrolling");
ButtonGroup bg = new ButtonGroup();
bg.add(window);
bg.add(document);
toolBar.add(window);
toolBar.add(document);
toolBar.setFloatable(false);
window.setSelected(true);
window.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
viewport.setScrollDirector(windowSD);
viewport.fireStateChanged();
scrollPane.revalidate();
}
});
document.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
viewport.setScrollDirector(documentSD);
viewport.fireStateChanged();
scrollPane.revalidate();
}
});
getContentPane().add(toolBar, BorderLayout.NORTH);
getContentPane().validate();
}
/**
* A modified scroll director that performs document based scroling
* rather than window based scrolling (ie. the scrollbars act in
* the inverse direction as normal)
*/
public class DocumentScrollDirector extends PDefaultScrollDirector {
/**
* Get the View position given the specified camera bounds - modified
* such that:
*
* Rather than finding the distance from the upper left corner
* of the window to the upper left corner of the document -
* we instead find the distance from the lower right corner
* of the window to the upper left corner of the document
* THEN we subtract that value from total document width so
* that the position is inverted
*
* @param viewBounds The bounds for which the view position will be computed
* @return The view position
*/
public Point getViewPosition(Rectangle2D viewBounds) {
Point pos = new Point();
if (camera != null) {
// First we compute the union of all the layers
PBounds layerBounds = new PBounds();
java.util.List layers = camera.getLayersReference();
for (Iterator i = layers.iterator(); i.hasNext();) {
PLayer layer = (PLayer) i.next();
layerBounds.add(layer.getFullBoundsReference());
}
// Then we put the bounds into camera coordinates and
// union the camera bounds
camera.viewToLocal(layerBounds);
layerBounds.add(viewBounds);
// Rather than finding the distance from the upper left corner
// of the window to the upper left corner of the document -
// we instead find the distance from the lower right corner
// of the window to the upper left corner of the document
// THEN we measure the offset from the lower right corner
// of the document
pos.setLocation(
(int) (layerBounds.getWidth() - (viewBounds.getX() + viewBounds.getWidth() - layerBounds.getX()) + 0.5),
(int) (layerBounds.getHeight() - (viewBounds.getY() + viewBounds.getHeight() - layerBounds.getY()) + 0.5));
}
return pos;
}
/**
* We do the same thing we did in getViewPosition above to flip the
* document-window position relationship
*
* @param x The new x position
* @param y The new y position
*/
public void setViewPosition(double x, double y) {
if (camera != null) {
// If a scroll is in progress - we ignore new scrolls -
// if we didn't, since the scrollbars depend on the camera location
// we can end up with an infinite loop
if (!scrollInProgress) {
scrollInProgress = true;
// Get the union of all the layers' bounds
PBounds layerBounds = new PBounds();
java.util.List layers = camera.getLayersReference();
for (Iterator i = layers.iterator(); i.hasNext();) {
PLayer layer = (PLayer) i.next();
layerBounds.add(layer.getFullBoundsReference());
}
PAffineTransform at = camera.getViewTransform();
at.transform(layerBounds,layerBounds);
// Union the camera view bounds
PBounds viewBounds = camera.getBoundsReference();
layerBounds.add(viewBounds);
// Now find the new view position in view coordinates -
// This is basically the distance from the lower right
// corner of the window to the upper left corner of the
// document
// We then measure the offset from the lower right corner
// of the document
Point2D newPoint =
new Point2D.Double(
layerBounds.getX() + layerBounds.getWidth() - (x + viewBounds.getWidth()),
layerBounds.getY() + layerBounds.getHeight() - (y + viewBounds.getHeight()));
// Now transform the new view position into global coords
camera.localToView(newPoint);
// Compute the new matrix values to put the camera at the
// correct location
double newX = - (at.getScaleX() * newPoint.getX() + at.getShearX() * newPoint.getY());
double newY = - (at.getShearY() * newPoint.getX() + at.getScaleY() * newPoint.getY());
at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(), newX, newY);
// Now actually set the camera's transform
camera.setViewTransform(at);
scrollInProgress = false;
}
}
}
}
public static void main(String[] args) {
new ScrollingExample();
}
}