1 /* 2 * $Id: JGraphPrintingScrollPane.java,v 1.1 2009/09/25 15:14:15 david Exp $ 3 * Copyright (c) 2001-2005, Gaudenz Alder 4 * 5 * All rights reserved. 6 * 7 * See LICENSE file for license details. If you are unable to locate 8 * this file please contact info (at) jgraph (dot) com. 9 */ 10 package com.jgraph.util; 11 12 import java.awt.BasicStroke; 13 import java.awt.Color; 14 import java.awt.Dimension; 15 import java.awt.Graphics; 16 import java.awt.Graphics2D; 17 import java.awt.Image; 18 import java.awt.Point; 19 import java.awt.Stroke; 20 import java.awt.geom.AffineTransform; 21 import java.awt.geom.Point2D; 22 import java.awt.geom.Rectangle2D; 23 import java.awt.print.PageFormat; 24 import java.awt.print.Printable; 25 26 import javax.swing.JScrollPane; 27 import javax.swing.JViewport; 28 import javax.swing.RepaintManager; 29 30 import org.jgraph.JGraph; 31 32 /** 33 * Wrapper panel for a diagram/JGraph-pair that implements automatic sizing, 34 * backgrounds, printing and undo support. When wrapped in a scrollpane this 35 * panel adds rulers to the enclosing scrollpane. Furthermore, it automatically 36 * sets the minimum size and scale of the graph based on its settings. 37 */ 38 public class JGraphPrintingScrollPane extends JScrollPane implements Printable { 39 40 /** 41 * Specifies the default page scale. Default is 1.5 42 */ 43 public static final double DEFAULT_PAGESCALE = 1.5; 44 45 /** 46 * Background page format. 47 */ 48 protected PageFormat pageFormat = new PageFormat(); 49 50 /** 51 * Specifies if the background page is visible. Default is true. 52 */ 53 protected boolean isPageVisible = true; 54 55 /** 56 * Defines the scaling for the background page metrics. Default is 57 * {@link #DEFAULT_PAGESCALE}. 58 */ 59 protected double pageScale = DEFAULT_PAGESCALE; 60 61 /** 62 * References the inner graph. 63 */ 64 protected JGraph graph; 65 66 /** 67 * Bound property names for the respective properties. 68 */ 69 public static String PROPERTY_METRIC = "metric", 70 PROPERTY_PAGEVISIBLE = "pageVisible", 71 PROPERTY_BACKGROUNDIMAGE = "backgroundImage", 72 PROPERTY_RULERSVISIBLE = "rulersVisible", 73 PROPERTY_PAGEFORMAT = "pageFormat", 74 PROPERTY_AUTOSCALEPOLICY = "autoScalePolicy", 75 PROPERTY_PAGESCALE = "pageScale"; 76 77 /** 78 * Returns the inner graph. 79 * 80 * @return Returns the graph. 81 */ getGraph()82 public JGraph getGraph() { 83 return graph; 84 } 85 86 /** 87 * Returns the page format of the background page. 88 * 89 * @return Returns the pageFormat. 90 */ getPageFormat()91 public PageFormat getPageFormat() { 92 return pageFormat; 93 } 94 95 /** 96 * Sets the page format of the background page.Fires a property change event 97 * for {@link #PROPERTY_PAGEFORMAT}. 98 * 99 * @param pageFormat 100 * The pageFormat to set. 101 */ setPageFormat(PageFormat pageFormat)102 public void setPageFormat(PageFormat pageFormat) { 103 Object oldValue = this.pageFormat; 104 this.pageFormat = pageFormat; 105 updateMinimumSize(); 106 firePropertyChange(PROPERTY_PAGEFORMAT, oldValue, pageFormat); 107 } 108 109 /** 110 * Returns the scale of the page metrics. 111 * 112 * @return Returns the pageScale. 113 */ getPageScale()114 public double getPageScale() { 115 return pageScale; 116 } 117 118 /** 119 * Sets the scale of the page metrics.Fires a property change event for 120 * {@link #PROPERTY_PAGESCALE}. 121 * 122 * @param pageScale 123 * The pageScale to set. 124 */ setPageScale(double pageScale)125 public void setPageScale(double pageScale) { 126 double oldValue = this.pageScale; 127 this.pageScale = pageScale; 128 firePropertyChange(PROPERTY_PAGESCALE, oldValue, pageScale); 129 } 130 131 /** 132 * Updates the minimum size of the graph according to the current state of 133 * the background page: if the page is not visible then the minimum size is 134 * set to <code>null</code>, otherwise the minimum size is set to the 135 * smallest area of pages containing the graph. 136 */ updateMinimumSize()137 protected void updateMinimumSize() { 138 if (isPageVisible() && pageFormat != null) { 139 Rectangle2D bounds = graph.getCellBounds(graph.getRoots()); 140 Dimension size = (bounds != null) ? new Dimension((int) (bounds 141 .getX() + bounds.getWidth()), (int) (bounds.getY() + bounds 142 .getHeight())) : new Dimension(1, 1); 143 int w = (int) (pageFormat.getWidth() * pageScale); 144 int h = (int) (pageFormat.getHeight() * pageScale); 145 int cols = (int) Math.ceil((double) (size.width - 5) / (double) w); 146 int rows = (int) Math.ceil((double) (size.height - 5) / (double) h); 147 size = new Dimension(Math.max(cols, 1) * w + 5, Math.max(rows, 1) 148 * h + 5); 149 graph.setMinimumSize(size); 150 } else { 151 graph.setMinimumSize(null); 152 } 153 graph.revalidate(); 154 } 155 156 /** 157 * Computes the scale for the window autoscale policy. 158 * 159 * @param border 160 * The border to use. 161 * @return Returns the scale to use for the graph. 162 */ computeWindowScale(int border)163 protected double computeWindowScale(int border) { 164 Dimension size = getViewport().getExtentSize(); 165 Rectangle2D p = getGraph().getCellBounds(getGraph().getRoots()); 166 if (p != null) { 167 return Math.min((double) size.getWidth() 168 / (p.getX() + p.getWidth() + border), (double) size 169 .getHeight() 170 / (p.getY() + p.getHeight() + border)); 171 } 172 return 0; 173 } 174 175 /** 176 * Computes the scale for the page autoscale policy. 177 * 178 * @return Returns the scale to use for the graph. 179 */ computePageScale()180 protected double computePageScale() { 181 Dimension size = getViewport().getExtentSize(); 182 Dimension p = getGraph().getMinimumSize(); 183 if (p != null && (p.getWidth() != 0 || p.getHeight() != 0)) { 184 return Math.min((double) size.getWidth() / (double) p.getWidth(), 185 (double) size.getHeight() / (double) p.getHeight()); 186 } 187 return 0; 188 } 189 190 /** 191 * Computes the scale for the pagewidth autoscale policy. 192 * 193 * @param border 194 * The border to use. 195 * @return Returns the scale to use for the graph. 196 */ computePageWidthScale(int border)197 protected double computePageWidthScale(int border) { 198 Dimension size = getViewport().getExtentSize(); 199 Dimension p = getGraph().getMinimumSize(); 200 if (p != null && (p.getWidth() != 0 || p.getHeight() != 0)) { 201 size.width = size.width - border; 202 return (double) size.getWidth() / (double) p.getWidth(); 203 } 204 return 0; 205 } 206 207 /** 208 * Prints the specified page on the specified graphics using 209 * <code>pageForm</code> for the page format. 210 * 211 * @param g 212 * The graphics to paint the graph on. 213 * @param printFormat 214 * The page format to use for printing. 215 * @param page 216 * The page to print 217 * @return Returns {@link Printable#PAGE_EXISTS} or 218 * {@link Printable#NO_SUCH_PAGE}. 219 */ print(Graphics g, PageFormat printFormat, int page)220 public int print(Graphics g, PageFormat printFormat, int page) { 221 Dimension pSize = graph.getPreferredSize(); 222 int w = (int) (printFormat.getWidth() * pageScale); 223 int h = (int) (printFormat.getHeight() * pageScale); 224 int cols = (int) Math.max(Math.ceil((double) (pSize.width - 5) 225 / (double) w), 1); 226 int rows = (int) Math.max(Math.ceil((double) (pSize.height - 5) 227 / (double) h), 1); 228 if (page < cols * rows) { 229 230 // Configures graph for printing 231 RepaintManager currentManager = RepaintManager.currentManager(this); 232 currentManager.setDoubleBufferingEnabled(false); 233 double oldScale = getGraph().getScale(); 234 getGraph().setScale(1 / pageScale); 235 int dx = (int) ((page % cols) * printFormat.getWidth()); 236 int dy = (int) ((page % rows) * printFormat.getHeight()); 237 g.translate(-dx, -dy); 238 g.setClip(dx, dy, (int) (dx + printFormat.getWidth()), 239 (int) (dy + printFormat.getHeight())); 240 241 // Prints the graph on the graphics. 242 getGraph().paint(g); 243 244 // Restores graph 245 g.translate(dx, dy); 246 graph.setScale(oldScale); 247 currentManager.setDoubleBufferingEnabled(true); 248 return PAGE_EXISTS; 249 } else { 250 return NO_SUCH_PAGE; 251 } 252 } 253 254 /** 255 * Viewport for diagram panes that is in charge of painting the background 256 * image or page. 257 */ 258 public class Viewport extends JViewport { 259 260 /** 261 * Paints the background. 262 * 263 * @param g 264 * The graphics object to paint the background on. 265 */ paint(Graphics g)266 public void paint(Graphics g) { 267 if (isPageVisible()) 268 paintBackgroundPages((Graphics2D) g); 269 else 270 setBackground(graph.getBackground()); 271 if (graph.getBackgroundImage() != null) { 272 paintBackgroundImage((Graphics2D) g); 273 } 274 setOpaque(!isPageVisible() && graph.getBackgroundImage() == null); 275 super.paint(g); 276 setOpaque(true); 277 } 278 279 /** 280 * Hook for subclassers to paint the background image. 281 * 282 * @param g2 283 * The graphics object to paint the image on. 284 */ paintBackgroundImage(Graphics2D g2)285 protected void paintBackgroundImage(Graphics2D g2) { 286 // Clears the background 287 if (!isPageVisible()) { 288 g2.setColor(graph.getBackground()); 289 g2.fillRect(0, 0, graph.getWidth(), graph.getHeight()); 290 } 291 // Paints the image 292 AffineTransform tmp = g2.getTransform(); 293 Point offset = getViewPosition(); 294 g2.translate(-offset.x, -offset.y); 295 g2.scale(graph.getScale(), graph.getScale()); 296 Image img = graph.getBackgroundImage().getImage(); 297 g2.drawImage(img, 0, 0, graph); 298 g2.setTransform(tmp); 299 } 300 301 /** 302 * Hook for subclassers to paint the background page(s). 303 * 304 * @param g2 305 * The graphics object to paint the background page(s) on. 306 */ paintBackgroundPages(Graphics2D g2)307 protected void paintBackgroundPages(Graphics2D g2) { 308 Point2D p = graph.toScreen(new Point2D.Double( 309 pageFormat.getWidth(), pageFormat.getHeight())); 310 Dimension pSize = graph.getPreferredSize(); 311 int w = (int) (p.getX() * pageScale); 312 int h = (int) (p.getY() * pageScale); 313 int cols = (int) Math.max(Math.ceil((double) (pSize.width - 5) 314 / (double) w), 1); 315 int rows = (int) Math.max(Math.ceil((double) (pSize.height - 5) 316 / (double) h), 1); 317 g2.setColor(graph.getHandleColor()); 318 319 // Draws the pages. 320 Point offset = getViewPosition(); 321 g2.translate(-offset.x, -offset.y); 322 g2.fillRect(0, 0, graph.getWidth(), graph.getHeight()); 323 g2.setColor(Color.darkGray); 324 g2.fillRect(3, 3, cols * w, rows * h); 325 g2.setColor(getGraph().getBackground()); 326 g2.fillRect(1, 1, cols * w - 1, rows * h - 1); 327 328 // Draws the pagebreaks. 329 Stroke previousStroke = g2.getStroke(); 330 g2.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, 331 BasicStroke.JOIN_MITER, 10.0f, new float[] { 1, 2 }, 0)); 332 g2.setColor(Color.darkGray); 333 for (int i = 1; i < cols; i++) 334 g2.drawLine(i * w, 1, i * w, rows * h - 1); 335 for (int i = 1; i < rows; i++) 336 g2.drawLine(1, i * h, cols * w - 1, i * h); 337 338 // Restores the graphics. 339 g2.setStroke(previousStroke); 340 g2.translate(offset.x, offset.y); 341 g2.clipRect(0, 0, cols * w - 1 - offset.x, rows * h - 1 - offset.y); 342 } 343 344 } 345 346 /** 347 * Returns true if the background page is visible. 348 * 349 * @return Returns the isPageVisible. 350 */ isPageVisible()351 public boolean isPageVisible() { 352 return isPageVisible; 353 } 354 355 /** 356 * Sets if the background page should be visible.Fires a property change 357 * event for {@link #PROPERTY_PAGEVISIBLE}. 358 * 359 * @param isPageVisible 360 * The isPageVisible to set. 361 */ setPageVisible(boolean isPageVisible)362 public void setPageVisible(boolean isPageVisible) { 363 boolean oldValue = this.isPageVisible; 364 this.isPageVisible = isPageVisible; 365 updateMinimumSize(); 366 firePropertyChange(PROPERTY_PAGEVISIBLE, oldValue, isPageVisible); 367 } 368 }