001: /*
002: * $Id: JGraphEditorDiagramPane.java,v 1.7 2005/08/07 13:25:43 gaudenz Exp $
003: * Copyright (c) 2001-2005, Gaudenz Alder
004: *
005: * All rights reserved.
006: *
007: * See LICENSE file for license details. If you are unable to locate
008: * this file please contact info (at) jgraph (dot) com.
009: */
010: package com.jgraph.editor.factory;
011:
012: import java.awt.AlphaComposite;
013: import java.awt.BasicStroke;
014: import java.awt.Color;
015: import java.awt.Component;
016: import java.awt.Dimension;
017: import java.awt.Graphics;
018: import java.awt.Graphics2D;
019: import java.awt.Image;
020: import java.awt.Point;
021: import java.awt.Stroke;
022: import java.awt.event.ComponentAdapter;
023: import java.awt.event.ComponentEvent;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.Point2D;
026: import java.awt.geom.Rectangle2D;
027: import java.awt.image.BufferedImage;
028: import java.awt.print.PageFormat;
029: import java.awt.print.Printable;
030: import java.beans.PropertyChangeEvent;
031: import java.beans.PropertyChangeListener;
032:
033: import javax.swing.ImageIcon;
034: import javax.swing.JScrollPane;
035: import javax.swing.JViewport;
036: import javax.swing.RepaintManager;
037:
038: import org.jgraph.JGraph;
039: import org.jgraph.event.GraphLayoutCacheEvent;
040: import org.jgraph.event.GraphLayoutCacheListener;
041: import org.jgraph.event.GraphModelEvent;
042: import org.jgraph.event.GraphModelListener;
043: import org.jgraph.graph.GraphUndoManager;
044:
045: import com.jgraph.editor.JGraphEditorDiagram;
046:
047: /**
048: * Wrapper panel for a diagram/JGraph-pair that implements automatic sizing,
049: * backgrounds, printing and undo support. When wrapped in a scrollpane this
050: * panel adds rulers to the enclosing scrollpane. Furthermore, it automatically
051: * sets the minimum size and scale of the graph based on its settings.
052: */
053: public class JGraphEditorDiagramPane extends JScrollPane implements
054: Printable {
055:
056: /**
057: * Specifies the default page scale. Default is 1.5
058: */
059: public static final double DEFAULT_PAGESCALE = 1.5;
060:
061: /**
062: * Specifies the size of the undo history. Default is 100.
063: */
064: public static final int DEFAULT_HISTORYSIZE = 100;
065:
066: /**
067: * Specifies the default unit system. Default is metric.
068: */
069: public static final boolean DEFAULT_ISMETRIC = true;
070:
071: /**
072: * Defines the no autoscaling policy.
073: */
074: public static final int AUTOSCALE_POLICY_NONE = 0;
075:
076: /**
077: * Defines the window-size autoscaling policy.
078: */
079: public static final int AUTOSCALE_POLICY_WINDOW = 1;
080:
081: /**
082: * Defines the page autoscaling policy.
083: */
084: public static final int AUTOSCALE_POLICY_PAGE = 2;
085:
086: /**
087: * Defines the pagewidth autoscaling policy.
088: */
089: public static final int AUTOSCALE_POLICY_PAGEWIDTH = 3;
090:
091: /**
092: * References the diagram this pane represents.
093: */
094: protected JGraphEditorDiagram diagram;
095:
096: /**
097: * Background page format.
098: */
099: protected PageFormat pageFormat = new PageFormat();
100:
101: /**
102: * Defines the scaling for the background page metrics. Default is
103: * {@link #DEFAULT_PAGESCALE}.
104: */
105: protected double pageScale = DEFAULT_PAGESCALE;
106:
107: /**
108: * Holds the rulers to be used in a parent scrollpane.
109: */
110: protected JGraphEditorRuler verticalRuler, horizontalRuler;
111:
112: /**
113: * Specifies if the rules should be visible. Default is true.
114: */
115: protected boolean isRulersVisible = false;
116:
117: /**
118: * Specifies if the rulers should use metric units. Default is true.
119: */
120: protected boolean isMetric = true;
121:
122: /**
123: * Specifies if the background page is visible. Default is true.
124: */
125: protected boolean isPageVisible = true;
126:
127: /**
128: * Specified the autoscaling policy. Default is
129: * {@link #AUTOSCALE_POLICY_NONE}.
130: */
131: protected int autoScalePolicy = AUTOSCALE_POLICY_NONE;
132:
133: /**
134: * Holds the background image.
135: */
136: protected ImageIcon backgroundImage;
137:
138: /**
139: * Holds the undo manager for the graph.
140: */
141: protected transient GraphUndoManager undoManager = new GraphUndoManager();
142:
143: /**
144: * References the inner graph.
145: */
146: protected JGraph graph;
147:
148: /**
149: * Holds the autoscale reset listener to be used with the graph. This
150: * listener is used to reset the autoscale policy to none if the scale of
151: * the graph is changed manually, ie. <i>not</i> by means of automatic
152: * scaling.
153: */
154: protected PropertyChangeListener autoScaleResetListener = createAutoScaleResetListener();
155:
156: /**
157: * Bound property names for the respective properties.
158: */
159: public static String PROPERTY_METRIC = "metric",
160: PROPERTY_PAGEVISIBLE = "pageVisible",
161: PROPERTY_BACKGROUNDIMAGE = "backgroundImage",
162: PROPERTY_RULERSVISIBLE = "rulersVisible",
163: PROPERTY_PAGEFORMAT = "pageFormat",
164: PROPERTY_AUTOSCALEPOLICY = "autoScalePolicy",
165: PROPERTY_PAGESCALE = "pageScale";
166:
167: /**
168: * Constructs a new graph pane for the specified diagram and graph.
169: */
170: public JGraphEditorDiagramPane(JGraphEditorDiagram diagram,
171: JGraph graph) {
172: super ();
173: this .diagram = diagram;
174: this .graph = graph;
175: setViewport(createViewport());
176: getViewport().add(graph);
177: setFocusable(false);
178: undoManager.setLimit(DEFAULT_HISTORYSIZE);
179:
180: // Creates and initializes the rulers.
181: createRulers();
182: // In case the default changes to true
183: setRulersVisible(isRulersVisible());
184:
185: // Installs undo support. Default history size is 100.
186: graph.getModel().addUndoableEditListener(getGraphUndoManager());
187:
188: // Resets the autoscale policy if the is manually changed.
189: graph.addPropertyChangeListener(autoScaleResetListener);
190:
191: // Install listener for adjusting min size and autoscaling
192: graph.getModel().addGraphModelListener(
193: new GraphModelListener() {
194: public void graphChanged(GraphModelEvent e) {
195: updateMinimumSize();
196: updateScale();
197: }
198: });
199:
200: // Same for the layout cache
201: graph.getGraphLayoutCache().addGraphLayoutCacheListener(
202: new GraphLayoutCacheListener() {
203: public void graphLayoutCacheChanged(
204: GraphLayoutCacheEvent e) {
205: updateMinimumSize();
206: updateScale();
207: }
208: });
209:
210: // Installs listener for autoscaling.
211: getViewport().addComponentListener(new ComponentAdapter() {
212: public void componentResized(ComponentEvent arg0) {
213: updateScale();
214: }
215: });
216:
217: // Initializes controlled graph properties.
218: updateMinimumSize();
219: updateScale();
220: }
221:
222: /**
223: * Hook for subclassers to create the autoscale reset listener.
224: *
225: * @return Returns a new autoscale reset listener.
226: */
227: public PropertyChangeListener createAutoScaleResetListener() {
228: return new PropertyChangeListener() {
229: public void propertyChange(PropertyChangeEvent evt) {
230: if (evt.getPropertyName().equals(JGraph.SCALE_PROPERTY)
231: && getAutoScalePolicy() != AUTOSCALE_POLICY_NONE) {
232: setAutoScalePolicy(AUTOSCALE_POLICY_NONE);
233: }
234: }
235: };
236: }
237:
238: /**
239: * Hook for subclassers to create the viewport.
240: *
241: * @return Returns a new viewport to be used in the panel.
242: */
243: protected JViewport createViewport() {
244: return new Viewport();
245: }
246:
247: /**
248: * Hook for subclassers to create the rulers. This implementation sets the
249: * {@link #horizontalRuler} and {@link #verticalRuler}.
250: */
251: protected void createRulers() {
252: horizontalRuler = new JGraphEditorRuler(graph,
253: JGraphEditorRuler.ORIENTATION_HORIZONTAL);
254: verticalRuler = new JGraphEditorRuler(graph,
255: JGraphEditorRuler.ORIENTATION_VERTICAL);
256: }
257:
258: /**
259: * Returns a {@link BufferedImage} for the graph using inset as an empty
260: * border around the cells of the graph. If bg is null then a transparent
261: * background is applied to the image, else the background is filled with
262: * the bg color. Therefore, one should only use a null background if the
263: * fileformat support transparency, eg. GIF and PNG. For JPG, you can use
264: * <code>Color.WHITE</code> for example. This implementation also takes
265: * into account potential background images.
266: *
267: * @return Returns an image of the graph.
268: */
269: public BufferedImage getImage(Color bg, int inset) {
270: Object[] cells = graph.getRoots();
271: Rectangle2D bounds = graph.getCellBounds(cells);
272: if (bounds != null) {
273: graph.toScreen(bounds);
274: BufferedImage img = new BufferedImage((int) bounds
275: .getWidth()
276: + 2 * inset, (int) bounds.getHeight() + 2 * inset,
277: (bg != null) ? BufferedImage.TYPE_INT_RGB
278: : BufferedImage.TYPE_INT_ARGB);
279: Graphics2D graphics = img.createGraphics();
280: if (backgroundImage != null) {
281: graphics.drawImage(backgroundImage.getImage(), 0, 0,
282: this );
283: } else if (bg != null) {
284: graphics.setColor(bg);
285: graphics
286: .fillRect(0, 0, img.getWidth(), img.getHeight());
287: } else {
288: graphics.setComposite(AlphaComposite.getInstance(
289: AlphaComposite.CLEAR, 0.0f));
290: graphics
291: .fillRect(0, 0, img.getWidth(), img.getHeight());
292: graphics.setComposite(AlphaComposite.SrcOver);
293: }
294: graphics.translate((int) (-bounds.getX() + inset),
295: (int) (-bounds.getY() + inset));
296: boolean tmp = isDoubleBuffered();
297: RepaintManager currentManager = RepaintManager
298: .currentManager(this );
299: currentManager.setDoubleBufferingEnabled(false);
300: graph.paint(graphics);
301: currentManager.setDoubleBufferingEnabled(tmp);
302: return img;
303: }
304: return null;
305: }
306:
307: /**
308: * Returns the graph undo manager.
309: *
310: * @return Returns the graphUndoManager.
311: */
312: public GraphUndoManager getGraphUndoManager() {
313: return undoManager;
314: }
315:
316: /**
317: * Sets the graph undo manager.
318: *
319: * @param undoManager
320: * The graphUndoManager to set.
321: */
322: public void setGraphUndoManager(GraphUndoManager undoManager) {
323: this .undoManager = undoManager;
324: }
325:
326: /**
327: * Returns the inner graph.
328: *
329: * @return Returns the graph.
330: */
331: public JGraph getGraph() {
332: return graph;
333: }
334:
335: /**
336: * Returns the diagram.
337: *
338: * @return Returns the cacheNode.
339: */
340: public JGraphEditorDiagram getDiagram() {
341: return diagram;
342: }
343:
344: /**
345: * Returns true if the rulers use metric units.
346: *
347: * @return Returns the isMetric.
348: */
349: public boolean isMetric() {
350: return isMetric;
351: }
352:
353: /**
354: * Specifies if the rulers should use metric units. Fires a property change
355: * event for {@link #PROPERTY_METRIC}.
356: *
357: * @param isMetric
358: * The isMetric to set.
359: */
360: public void setMetric(boolean isMetric) {
361: boolean oldValue = this .isMetric;
362: this .isMetric = isMetric;
363: getVerticalRuler().setMetric(isMetric);
364: getHorizontalRuler().setMetric(isMetric);
365: firePropertyChange(PROPERTY_METRIC, oldValue, isMetric);
366: }
367:
368: /**
369: * Returns true if the rulers are to be displayed.
370: *
371: * @return Returns the isRulersVisible.
372: */
373: public boolean isRulersVisible() {
374: return isRulersVisible;
375: }
376:
377: /**
378: * Sets if the rulers are to be displayed. Fires a property change event for
379: * {@link #PROPERTY_RULERSVISIBLE}.
380: *
381: * @param isRulersVisible
382: * The isRulersVisible to set.
383: */
384: public void setRulersVisible(boolean isRulersVisible) {
385: boolean oldValue = this .isRulersVisible;
386: this .isRulersVisible = isRulersVisible;
387: if (isRulersVisible()) {
388: setColumnHeaderView(getHorizontalRuler());
389: setRowHeaderView(getVerticalRuler());
390: } else {
391: setColumnHeaderView(null);
392: setRowHeaderView(null);
393: }
394: firePropertyChange(PROPERTY_RULERSVISIBLE, oldValue,
395: isRulersVisible);
396: }
397:
398: /**
399: * Returns the vertical ruler.
400: *
401: * @return Returns the verticalRuler.
402: */
403: public JGraphEditorRuler getVerticalRuler() {
404: return verticalRuler;
405: }
406:
407: /**
408: * Returns the horizontal ruler.
409: *
410: * @return Returns the horizontalRuler.
411: */
412: public JGraphEditorRuler getHorizontalRuler() {
413: return horizontalRuler;
414: }
415:
416: /**
417: * Returns the background image.
418: *
419: * @return Returns the backgroundImage.
420: */
421: public ImageIcon getBackgroundImage() {
422: return backgroundImage;
423: }
424:
425: /**
426: * Sets the background image. Fires a property change event for
427: * {@link #PROPERTY_BACKGROUNDIMAGE}.
428: *
429: * @param backgroundImage
430: * The backgroundImage to set.
431: */
432: public void setBackgroundImage(ImageIcon backgroundImage) {
433: ImageIcon oldValue = this .backgroundImage;
434: this .backgroundImage = backgroundImage;
435: firePropertyChange(PROPERTY_BACKGROUNDIMAGE, oldValue,
436: backgroundImage);
437: }
438:
439: /**
440: * Returns the autoscale policy.
441: *
442: * @return Returns the autoScalePolicy.
443: */
444: public int getAutoScalePolicy() {
445: return autoScalePolicy;
446: }
447:
448: /**
449: * Sets the autoscale policy. Possible values is one of:
450: * {@link #AUTOSCALE_POLICY_NONE},{@link #AUTOSCALE_POLICY_PAGE},
451: * {@link #AUTOSCALE_POLICY_PAGEWIDTH} or {@link #AUTOSCALE_POLICY_WINDOW}.
452: * Fires a property change event for {@link #PROPERTY_AUTOSCALEPOLICY}.
453: *
454: * @param autoScalePolicy
455: * The autoScalePolicy to set.
456: */
457: public void setAutoScalePolicy(int autoScalePolicy) {
458: int oldValue = this .autoScalePolicy;
459: this .autoScalePolicy = autoScalePolicy;
460: if (autoScalePolicy == AUTOSCALE_POLICY_NONE)
461: getGraph().setScale(1.0);
462: updateScale();
463: firePropertyChange(PROPERTY_AUTOSCALEPOLICY, oldValue,
464: autoScalePolicy);
465: }
466:
467: /**
468: * Returns true if the background page is visible.
469: *
470: * @return Returns the isPageVisible.
471: */
472: public boolean isPageVisible() {
473: return isPageVisible;
474: }
475:
476: /**
477: * Sets if the background page should be visible.Fires a property change
478: * event for {@link #PROPERTY_PAGEVISIBLE}.
479: *
480: * @param isPageVisible
481: * The isPageVisible to set.
482: */
483: public void setPageVisible(boolean isPageVisible) {
484: boolean oldValue = this .isPageVisible;
485: this .isPageVisible = isPageVisible;
486: updateMinimumSize();
487: updateScale();
488: firePropertyChange(PROPERTY_PAGEVISIBLE, oldValue,
489: isPageVisible);
490: }
491:
492: /**
493: * Returns the page format of the background page.
494: *
495: * @return Returns the pageFormat.
496: */
497: public PageFormat getPageFormat() {
498: return pageFormat;
499: }
500:
501: /**
502: * Sets the page format of the background page.Fires a property change event
503: * for {@link #PROPERTY_PAGEFORMAT}.
504: *
505: * @param pageFormat
506: * The pageFormat to set.
507: */
508: public void setPageFormat(PageFormat pageFormat) {
509: Object oldValue = this .pageFormat;
510: this .pageFormat = pageFormat;
511: updateMinimumSize();
512: firePropertyChange(PROPERTY_PAGEFORMAT, oldValue, pageFormat);
513: }
514:
515: /**
516: * Returns the scale of the page metrics.
517: *
518: * @return Returns the pageScale.
519: */
520: public double getPageScale() {
521: return pageScale;
522: }
523:
524: /**
525: * Sets the scale of the page metrics.Fires a property change event for
526: * {@link #PROPERTY_PAGESCALE}.
527: *
528: * @param pageScale
529: * The pageScale to set.
530: */
531: public void setPageScale(double pageScale) {
532: double oldValue = this .pageScale;
533: this .pageScale = pageScale;
534: firePropertyChange(PROPERTY_PAGESCALE, oldValue, pageScale);
535: }
536:
537: /**
538: * Updates the minimum size of the graph according to the current state of
539: * the background page: if the page is not visible then the minimum size is
540: * set to <code>null</code>, otherwise the minimum size is set to the
541: * smallest area of pages containing the graph.
542: */
543: protected void updateMinimumSize() {
544: if (isPageVisible() && pageFormat != null) {
545: Rectangle2D bounds = graph.getCellBounds(graph.getRoots());
546: Dimension size = (bounds != null) ? new Dimension(
547: (int) (bounds.getX() + bounds.getWidth()),
548: (int) (bounds.getY() + bounds.getHeight()))
549: : new Dimension(1, 1);
550: int w = (int) (pageFormat.getWidth() * pageScale);
551: int h = (int) (pageFormat.getHeight() * pageScale);
552: int cols = (int) Math.ceil((double) (size.width - 5)
553: / (double) w);
554: int rows = (int) Math.ceil((double) (size.height - 5)
555: / (double) h);
556: size = new Dimension(Math.max(cols, 1) * w + 5, Math.max(
557: rows, 1)
558: * h + 5);
559: graph.setMinimumSize(size);
560:
561: // Updates the active region in the rulers.
562: horizontalRuler.setActiveOffset((int) (pageFormat
563: .getImageableX()));
564: verticalRuler.setActiveOffset((int) (pageFormat
565: .getImageableY()));
566: horizontalRuler.setActiveLength((int) (pageFormat
567: .getImageableWidth()));
568: verticalRuler.setActiveLength((int) (pageFormat
569: .getImageableHeight()));
570: } else {
571: graph.setMinimumSize(null);
572: }
573: graph.revalidate();
574: }
575:
576: /**
577: * Updates the scale based on the autoscale policy. This implementation
578: * makes sure the {@link #autoScaleResetListener} does not trigger an
579: * autoscale policy reset be temporary removing the listener from the graph
580: * while the scale is updated. The scale is computed using one of
581: * {@link #computeWindowScale(int)}, {@link #computePageScale()} and
582: * {@link #computePageWidthScale(int)}.
583: */
584: protected void updateScale() {
585: double scale = 0;
586: if (getAutoScalePolicy() == AUTOSCALE_POLICY_WINDOW)
587: scale = computeWindowScale(10);
588: else if (getAutoScalePolicy() == AUTOSCALE_POLICY_PAGE)
589: scale = computePageScale();
590: else if (getAutoScalePolicy() == AUTOSCALE_POLICY_PAGEWIDTH)
591: scale = computePageWidthScale(20);
592: if (scale > 0) {
593: graph.removePropertyChangeListener(autoScaleResetListener);
594: graph.setScale(Math.max(Math.min(scale, 128), .01));
595: graph.addPropertyChangeListener(autoScaleResetListener);
596: }
597: }
598:
599: /**
600: * Computes the scale for the window autoscale policy.
601: *
602: * @param border
603: * The border to use.
604: * @return Returns the scale to use for the graph.
605: */
606: protected double computeWindowScale(int border) {
607: Dimension size = getViewport().getExtentSize();
608: Rectangle2D p = getGraph().getCellBounds(getGraph().getRoots());
609: if (p != null) {
610: return Math.min((double) size.getWidth()
611: / (p.getX() + p.getWidth() + border), (double) size
612: .getHeight()
613: / (p.getY() + p.getHeight() + border));
614: }
615: return 0;
616: }
617:
618: /**
619: * Computes the scale for the page autoscale policy.
620: *
621: * @return Returns the scale to use for the graph.
622: */
623: protected double computePageScale() {
624: Dimension size = getViewport().getExtentSize();
625: Dimension p = getGraph().getMinimumSize();
626: if (p != null && (p.getWidth() != 0 || p.getHeight() != 0)) {
627: return Math.min((double) size.getWidth()
628: / (double) p.getWidth(), (double) size.getHeight()
629: / (double) p.getHeight());
630: }
631: return 0;
632: }
633:
634: /**
635: * Computes the scale for the pagewidth autoscale policy.
636: *
637: * @param border
638: * The border to use.
639: * @return Returns the scale to use for the graph.
640: */
641: protected double computePageWidthScale(int border) {
642: Dimension size = getViewport().getExtentSize();
643: Dimension p = getGraph().getMinimumSize();
644: if (p != null && (p.getWidth() != 0 || p.getHeight() != 0)) {
645: size.width = size.width - border;
646: return (double) size.getWidth() / (double) p.getWidth();
647: }
648: return 0;
649: }
650:
651: /**
652: * Prints the specified page on the specified graphics using
653: * <code>pageForm</code> for the page format.
654: *
655: * @param g
656: * The graphics to paint the graph on.
657: * @param printFormat
658: * The page format to use for printing.
659: * @param page
660: * The page to print
661: * @return Returns {@link Printable#PAGE_EXISTS} or
662: * {@link Printable#NO_SUCH_PAGE}.
663: */
664: public int print(Graphics g, PageFormat printFormat, int page) {
665: Dimension pSize = graph.getPreferredSize();
666: int w = (int) (printFormat.getWidth() * pageScale);
667: int h = (int) (printFormat.getHeight() * pageScale);
668: int cols = (int) Math.max(Math.ceil((double) (pSize.width - 5)
669: / (double) w), 1);
670: int rows = (int) Math.max(Math.ceil((double) (pSize.height - 5)
671: / (double) h), 1);
672: if (page < cols * rows) {
673:
674: // Configures graph for printing
675: getGraph().removePropertyChangeListener(
676: autoScaleResetListener);
677: RepaintManager currentManager = RepaintManager
678: .currentManager(this );
679: currentManager.setDoubleBufferingEnabled(false);
680: double oldScale = getGraph().getScale();
681: getGraph().setScale(1 / pageScale);
682: int dx = (int) ((page % cols) * printFormat.getWidth());
683: int dy = (int) ((page % rows) * printFormat.getHeight());
684: g.translate(-dx, -dy);
685: g.setClip(dx, dy, (int) (dx + printFormat.getWidth()),
686: (int) (dy + printFormat.getHeight()));
687:
688: // Prints the graph on the graphics.
689: getGraph().paint(g);
690:
691: // Restores graph
692: g.translate(dx, dy);
693: graph.setScale(oldScale);
694: getGraph()
695: .addPropertyChangeListener(autoScaleResetListener);
696: currentManager.setDoubleBufferingEnabled(true);
697: return PAGE_EXISTS;
698: } else {
699: return NO_SUCH_PAGE;
700: }
701: }
702:
703: /**
704: * Viewport for diagram panes that is in charge of painting the background
705: * image or page.
706: */
707: public class Viewport extends JViewport {
708:
709: /**
710: * Paints the background.
711: *
712: * @param g
713: * The graphics object to paint the background on.
714: */
715: public void paint(Graphics g) {
716: if (isPageVisible())
717: paintBackgroundPages((Graphics2D) g);
718: else
719: setBackground(graph.getBackground());
720: if (getBackgroundImage() != null)
721: paintBackgroundImage((Graphics2D) g);
722: setOpaque(!isPageVisible() && getBackgroundImage() == null);
723: super .paint(g);
724: setOpaque(true);
725: }
726:
727: /**
728: * Hook for subclassers to paint the background image.
729: *
730: * @param g2
731: * The graphics object to paint the image on.
732: */
733: protected void paintBackgroundImage(Graphics2D g2) {
734: // Clears the background
735: if (!isPageVisible()) {
736: g2.setColor(graph.getBackground());
737: g2.fillRect(0, 0, graph.getWidth(), graph.getHeight());
738: }
739: // Paints the image
740: AffineTransform tmp = g2.getTransform();
741: Point offset = getViewPosition();
742: g2.translate(-offset.x, -offset.y);
743: g2.scale(graph.getScale(), graph.getScale());
744: Image img = getBackgroundImage().getImage();
745: g2.drawImage(img, 0, 0, graph);
746: g2.setTransform(tmp);
747: }
748:
749: /**
750: * Hook for subclassers to paint the background page(s).
751: *
752: * @param g2
753: * The graphics object to paint the background page(s) on.
754: */
755: protected void paintBackgroundPages(Graphics2D g2) {
756: Point2D p = graph.toScreen(new Point2D.Double(pageFormat
757: .getWidth(), pageFormat.getHeight()));
758: Dimension pSize = graph.getPreferredSize();
759: int w = (int) (p.getX() * pageScale);
760: int h = (int) (p.getY() * pageScale);
761: int cols = (int) Math.max(Math
762: .ceil((double) (pSize.width - 5) / (double) w), 1);
763: int rows = (int) Math.max(Math
764: .ceil((double) (pSize.height - 5) / (double) h), 1);
765: g2.setColor(graph.getHandleColor());
766:
767: // Draws the pages.
768: Point offset = getViewPosition();
769: g2.translate(-offset.x, -offset.y);
770: g2.fillRect(0, 0, graph.getWidth(), graph.getHeight());
771: g2.setColor(Color.darkGray);
772: g2.fillRect(3, 3, cols * w, rows * h);
773: g2.setColor(getGraph().getBackground());
774: g2.fillRect(1, 1, cols * w - 1, rows * h - 1);
775:
776: // Draws the pagebreaks.
777: Stroke previousStroke = g2.getStroke();
778: g2.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
779: BasicStroke.JOIN_MITER, 10.0f,
780: new float[] { 1, 2 }, 0));
781: g2.setColor(Color.darkGray);
782: for (int i = 1; i < cols; i++)
783: g2.drawLine(i * w, 1, i * w, rows * h - 1);
784: for (int i = 1; i < rows; i++)
785: g2.drawLine(1, i * h, cols * w - 1, i * h);
786:
787: // Restores the graphics.
788: g2.setStroke(previousStroke);
789: g2.translate(offset.x, offset.y);
790: g2.clipRect(0, 0, cols * w - 1 - offset.x, rows * h - 1
791: - offset.y);
792: }
793:
794: }
795:
796: /**
797: * Returns the parent diagram pane of the specified component, or the
798: * component itself if it is a editor diagram pane.
799: *
800: * @return Returns the parent editor diagram pane of <code>component</code>.
801: */
802: public static JGraphEditorDiagramPane getParentDiagramPane(
803: Component component) {
804: while (component != null) {
805: if (component instanceof JGraphEditorDiagramPane)
806: return (JGraphEditorDiagramPane) component;
807: component = component.getParent();
808: }
809: return null;
810: }
811:
812: }
|