/*
* $Id: JGraphPrintingScrollPane.java,v 1.1 2009/09/25 15:14:15 david Exp $
* Copyright (c) 2001-2005, Gaudenz Alder
*
* All rights reserved.
*
* See LICENSE file for license details. If you are unable to locate
* this file please contact info (at) jgraph (dot) com.
*/
package com.jgraph.util;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.RepaintManager;
import org.jgraph.JGraph;
/**
* Wrapper panel for a diagram/JGraph-pair that implements automatic sizing,
* backgrounds, printing and undo support. When wrapped in a scrollpane this
* panel adds rulers to the enclosing scrollpane. Furthermore, it automatically
* sets the minimum size and scale of the graph based on its settings.
*/
public class JGraphPrintingScrollPane extends JScrollPane implements Printable {
/**
* Specifies the default page scale. Default is 1.5
*/
public static final double DEFAULT_PAGESCALE = 1.5;
/**
* Background page format.
*/
protected PageFormat pageFormat = new PageFormat();
/**
* Specifies if the background page is visible. Default is true.
*/
protected boolean isPageVisible = true;
/**
* Defines the scaling for the background page metrics. Default is
* {@link #DEFAULT_PAGESCALE}.
*/
protected double pageScale = DEFAULT_PAGESCALE;
/**
* References the inner graph.
*/
protected JGraph graph;
/**
* Bound property names for the respective properties.
*/
public static String PROPERTY_METRIC = "metric",
PROPERTY_PAGEVISIBLE = "pageVisible",
PROPERTY_BACKGROUNDIMAGE = "backgroundImage",
PROPERTY_RULERSVISIBLE = "rulersVisible",
PROPERTY_PAGEFORMAT = "pageFormat",
PROPERTY_AUTOSCALEPOLICY = "autoScalePolicy",
PROPERTY_PAGESCALE = "pageScale";
/**
* Returns the inner graph.
*
* @return Returns the graph.
*/
public JGraph getGraph() {
return graph;
}
/**
* Returns the page format of the background page.
*
* @return Returns the pageFormat.
*/
public PageFormat getPageFormat() {
return pageFormat;
}
/**
* Sets the page format of the background page.Fires a property change event
* for {@link #PROPERTY_PAGEFORMAT}.
*
* @param pageFormat
* The pageFormat to set.
*/
public void setPageFormat(PageFormat pageFormat) {
Object oldValue = this.pageFormat;
this.pageFormat = pageFormat;
updateMinimumSize();
firePropertyChange(PROPERTY_PAGEFORMAT, oldValue, pageFormat);
}
/**
* Returns the scale of the page metrics.
*
* @return Returns the pageScale.
*/
public double getPageScale() {
return pageScale;
}
/**
* Sets the scale of the page metrics.Fires a property change event for
* {@link #PROPERTY_PAGESCALE}.
*
* @param pageScale
* The pageScale to set.
*/
public void setPageScale(double pageScale) {
double oldValue = this.pageScale;
this.pageScale = pageScale;
firePropertyChange(PROPERTY_PAGESCALE, oldValue, pageScale);
}
/**
* Updates the minimum size of the graph according to the current state of
* the background page: if the page is not visible then the minimum size is
* set to <code>null</code>, otherwise the minimum size is set to the
* smallest area of pages containing the graph.
*/
protected void updateMinimumSize() {
if (isPageVisible() && pageFormat != null) {
Rectangle2D bounds = graph.getCellBounds(graph.getRoots());
Dimension size = (bounds != null) ? new Dimension((int) (bounds
.getX() + bounds.getWidth()), (int) (bounds.getY() + bounds
.getHeight())) : new Dimension(1, 1);
int w = (int) (pageFormat.getWidth() * pageScale);
int h = (int) (pageFormat.getHeight() * pageScale);
int cols = (int) Math.ceil((double) (size.width - 5) / (double) w);
int rows = (int) Math.ceil((double) (size.height - 5) / (double) h);
size = new Dimension(Math.max(cols, 1) * w + 5, Math.max(rows, 1)
* h + 5);
graph.setMinimumSize(size);
} else {
graph.setMinimumSize(null);
}
graph.revalidate();
}
/**
* Computes the scale for the window autoscale policy.
*
* @param border
* The border to use.
* @return Returns the scale to use for the graph.
*/
protected double computeWindowScale(int border) {
Dimension size = getViewport().getExtentSize();
Rectangle2D p = getGraph().getCellBounds(getGraph().getRoots());
if (p != null) {
return Math.min((double) size.getWidth()
/ (p.getX() + p.getWidth() + border), (double) size
.getHeight()
/ (p.getY() + p.getHeight() + border));
}
return 0;
}
/**
* Computes the scale for the page autoscale policy.
*
* @return Returns the scale to use for the graph.
*/
protected double computePageScale() {
Dimension size = getViewport().getExtentSize();
Dimension p = getGraph().getMinimumSize();
if (p != null && (p.getWidth() != 0 || p.getHeight() != 0)) {
return Math.min((double) size.getWidth() / (double) p.getWidth(),
(double) size.getHeight() / (double) p.getHeight());
}
return 0;
}
/**
* Computes the scale for the pagewidth autoscale policy.
*
* @param border
* The border to use.
* @return Returns the scale to use for the graph.
*/
protected double computePageWidthScale(int border) {
Dimension size = getViewport().getExtentSize();
Dimension p = getGraph().getMinimumSize();
if (p != null && (p.getWidth() != 0 || p.getHeight() != 0)) {
size.width = size.width - border;
return (double) size.getWidth() / (double) p.getWidth();
}
return 0;
}
/**
* Prints the specified page on the specified graphics using
* <code>pageForm</code> for the page format.
*
* @param g
* The graphics to paint the graph on.
* @param printFormat
* The page format to use for printing.
* @param page
* The page to print
* @return Returns {@link Printable#PAGE_EXISTS} or
* {@link Printable#NO_SUCH_PAGE}.
*/
public int print(Graphics g, PageFormat printFormat, int page) {
Dimension pSize = graph.getPreferredSize();
int w = (int) (printFormat.getWidth() * pageScale);
int h = (int) (printFormat.getHeight() * pageScale);
int cols = (int) Math.max(Math.ceil((double) (pSize.width - 5)
/ (double) w), 1);
int rows = (int) Math.max(Math.ceil((double) (pSize.height - 5)
/ (double) h), 1);
if (page < cols * rows) {
// Configures graph for printing
RepaintManager currentManager = RepaintManager.currentManager(this);
currentManager.setDoubleBufferingEnabled(false);
double oldScale = getGraph().getScale();
getGraph().setScale(1 / pageScale);
int dx = (int) ((page % cols) * printFormat.getWidth());
int dy = (int) ((page % rows) * printFormat.getHeight());
g.translate(-dx, -dy);
g.setClip(dx, dy, (int) (dx + printFormat.getWidth()),
(int) (dy + printFormat.getHeight()));
// Prints the graph on the graphics.
getGraph().paint(g);
// Restores graph
g.translate(dx, dy);
graph.setScale(oldScale);
currentManager.setDoubleBufferingEnabled(true);
return PAGE_EXISTS;
} else {
return NO_SUCH_PAGE;
}
}
/**
* Viewport for diagram panes that is in charge of painting the background
* image or page.
*/
public class Viewport extends JViewport {
/**
* Paints the background.
*
* @param g
* The graphics object to paint the background on.
*/
public void paint(Graphics g) {
if (isPageVisible())
paintBackgroundPages((Graphics2D) g);
else
setBackground(graph.getBackground());
if (graph.getBackgroundImage() != null) {
paintBackgroundImage((Graphics2D) g);
}
setOpaque(!isPageVisible() && graph.getBackgroundImage() == null);
super.paint(g);
setOpaque(true);
}
/**
* Hook for subclassers to paint the background image.
*
* @param g2
* The graphics object to paint the image on.
*/
protected void paintBackgroundImage(Graphics2D g2) {
// Clears the background
if (!isPageVisible()) {
g2.setColor(graph.getBackground());
g2.fillRect(0, 0, graph.getWidth(), graph.getHeight());
}
// Paints the image
AffineTransform tmp = g2.getTransform();
Point offset = getViewPosition();
g2.translate(-offset.x, -offset.y);
g2.scale(graph.getScale(), graph.getScale());
Image img = graph.getBackgroundImage().getImage();
g2.drawImage(img, 0, 0, graph);
g2.setTransform(tmp);
}
/**
* Hook for subclassers to paint the background page(s).
*
* @param g2
* The graphics object to paint the background page(s) on.
*/
protected void paintBackgroundPages(Graphics2D g2) {
Point2D p = graph.toScreen(new Point2D.Double(
pageFormat.getWidth(), pageFormat.getHeight()));
Dimension pSize = graph.getPreferredSize();
int w = (int) (p.getX() * pageScale);
int h = (int) (p.getY() * pageScale);
int cols = (int) Math.max(Math.ceil((double) (pSize.width - 5)
/ (double) w), 1);
int rows = (int) Math.max(Math.ceil((double) (pSize.height - 5)
/ (double) h), 1);
g2.setColor(graph.getHandleColor());
// Draws the pages.
Point offset = getViewPosition();
g2.translate(-offset.x, -offset.y);
g2.fillRect(0, 0, graph.getWidth(), graph.getHeight());
g2.setColor(Color.darkGray);
g2.fillRect(3, 3, cols * w, rows * h);
g2.setColor(getGraph().getBackground());
g2.fillRect(1, 1, cols * w - 1, rows * h - 1);
// Draws the pagebreaks.
Stroke previousStroke = g2.getStroke();
g2.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER, 10.0f, new float[] { 1, 2 }, 0));
g2.setColor(Color.darkGray);
for (int i = 1; i < cols; i++)
g2.drawLine(i * w, 1, i * w, rows * h - 1);
for (int i = 1; i < rows; i++)
g2.drawLine(1, i * h, cols * w - 1, i * h);
// Restores the graphics.
g2.setStroke(previousStroke);
g2.translate(offset.x, offset.y);
g2.clipRect(0, 0, cols * w - 1 - offset.x, rows * h - 1 - offset.y);
}
}
/**
* Returns true if the background page is visible.
*
* @return Returns the isPageVisible.
*/
public boolean isPageVisible() {
return isPageVisible;
}
/**
* Sets if the background page should be visible.Fires a property change
* event for {@link #PROPERTY_PAGEVISIBLE}.
*
* @param isPageVisible
* The isPageVisible to set.
*/
public void setPageVisible(boolean isPageVisible) {
boolean oldValue = this.isPageVisible;
this.isPageVisible = isPageVisible;
updateMinimumSize();
firePropertyChange(PROPERTY_PAGEVISIBLE, oldValue, isPageVisible);
}
}