0001: /*
0002: * @(#)JGraph.java
0003: *
0004: * Copyright (c) 2001-2006 Gaudenz Alder
0005: *
0006: */
0007: package org.jgraph;
0008:
0009: import java.awt.AlphaComposite;
0010: import java.awt.Color;
0011: import java.awt.Component;
0012: import java.awt.Dimension;
0013: import java.awt.Font;
0014: import java.awt.Graphics;
0015: import java.awt.Graphics2D;
0016: import java.awt.GraphicsConfiguration;
0017: import java.awt.Image;
0018: import java.awt.Rectangle;
0019: import java.awt.Transparency;
0020: import java.awt.event.MouseEvent;
0021: import java.awt.geom.Dimension2D;
0022: import java.awt.geom.Point2D;
0023: import java.awt.geom.Rectangle2D;
0024: import java.awt.image.BufferedImage;
0025: import java.awt.image.VolatileImage;
0026: import java.io.IOException;
0027: import java.io.ObjectInputStream;
0028: import java.io.ObjectOutputStream;
0029: import java.io.Serializable;
0030: import java.util.ArrayList;
0031: import java.util.Hashtable;
0032: import java.util.List;
0033: import java.util.Map;
0034: import java.util.Vector;
0035:
0036: import javax.accessibility.Accessible;
0037: import javax.swing.BorderFactory;
0038: import javax.swing.ImageIcon;
0039: import javax.swing.JComponent;
0040: import javax.swing.JViewport;
0041: import javax.swing.Scrollable;
0042: import javax.swing.SwingConstants;
0043:
0044: import org.jgraph.event.GraphSelectionEvent;
0045: import org.jgraph.event.GraphSelectionListener;
0046: import org.jgraph.graph.AbstractCellView;
0047: import org.jgraph.graph.AttributeMap;
0048: import org.jgraph.graph.BasicMarqueeHandler;
0049: import org.jgraph.graph.CellView;
0050: import org.jgraph.graph.ConnectionSet;
0051: import org.jgraph.graph.DefaultCellViewFactory;
0052: import org.jgraph.graph.DefaultEdge;
0053: import org.jgraph.graph.DefaultGraphCell;
0054: import org.jgraph.graph.DefaultGraphModel;
0055: import org.jgraph.graph.DefaultGraphSelectionModel;
0056: import org.jgraph.graph.GraphConstants;
0057: import org.jgraph.graph.GraphLayoutCache;
0058: import org.jgraph.graph.GraphModel;
0059: import org.jgraph.graph.GraphSelectionModel;
0060: import org.jgraph.graph.PortView;
0061: import org.jgraph.plaf.GraphUI;
0062: import org.jgraph.plaf.basic.BasicGraphUI;
0063:
0064: /**
0065: * A control that displays a network of related objects using the well-known
0066: * paradigm of a graph.
0067: * <p>
0068: * A JGraph object doesn't actually contain your data; it simply provides a view
0069: * of the data. Like any non-trivial Swing component, the graph gets data by
0070: * querying its data model.
0071: * <p>
0072: * JGraph displays its data by drawing individual elements. Each element
0073: * displayed by the graph contains exactly one item of data, which is called a
0074: * cell. A cell may either be a vertex or an edge. Vertices may have neighbours
0075: * or not, and edges may have source and target vertices or not, depending on
0076: * whether they are connected.
0077: * <p>
0078: * <strong>Creating a Graph </strong>
0079: * <p>
0080: * The following code creates a JGraph object:
0081: * <p>
0082: * JGraph graph = new JGraph(); <br>
0083: * ... <br>
0084: * JScrollPane graphLayoutCache = new JScrollPane(graph)
0085: * <p>
0086: * The code creates an instance of JGraph and puts it in a scroll pane. JGraphs
0087: * constructor is called with no arguments in this example, which causes the
0088: * constructor to create a sample model.
0089: * <p>
0090: * <strong>Editing </strong>
0091: * <p>
0092: * Outmoved, cloned, resized, and shaped, or connected/disconnected to or from
0093: * other cells.
0094: * <p>
0095: * <strong>Keyboard Bindings </strong>
0096: * <p>
0097: * JGraph defines the following set of keyboard bindings:
0098: * <p>
0099: * <ul>
0100: * <li>Alt-Click forces marquee selection if over a cell.
0101: * <li>Shift- or Ctrl-Select extends or toggles the selection.
0102: * <li>Shift-Drag constrains the offset to one direction.
0103: * <li>Ctrl-Drag clones the selection.
0104: * <li>Doubleclick/F2 starts editing a cell.
0105: * </ul>
0106: * You can change the number of clicks that triggers editing using
0107: * setEditClickCount().
0108: * <p>
0109: * <strong>Customization </strong>
0110: * <p>
0111: * There are a number of additional methods that customize JGraph. For example,
0112: * setMinimumMove() defines the minimum amount of pixels before a move operation
0113: * is initiated. setSnapSize() defines the maximum distance for a cell to be
0114: * selected. setFloatEnabled() enables/disables port floating.
0115: * <p>
0116: * With setDisconnectOnMove() you can indicate if the selected subgraph should
0117: * be disconnected from the unselected rest when a move operation is initiated.
0118: * setDragEnabled() enables/disables the use of Drag And Drop, and
0119: * setDropEnabled() sets if the graph accepts Drops from external sources.
0120: * <p>
0121: * <strong>Customizing a graphs display </strong>
0122: * <p>
0123: * JGraph performs some look-and-feel specific painting. You can customize this
0124: * painting in a limited way. For example, you can modify the grid using
0125: * setGridColor() and setGridSize(), and you can change the handle colors using
0126: * setHandleColor() and setLockedHandleColor().
0127: * <p>
0128: * If you want finer control over the rendering, you can subclass one of the
0129: * default renderers, and extend its paint()-method. A renderer is a
0130: * Component-extension that paints a cell based on its attributes. Thus, neither
0131: * the JGraph nor its look-and-feel-specific implementation actually contain the
0132: * code that paints the cell. Instead, the graph uses the cell renderers
0133: * painting code.
0134: * <p>
0135: * <strong>Selection </strong>
0136: * <p>
0137: * Apart from the single-cell and marquee-selection, JGraphs selection model
0138: * also allows to "step-into" groups, and select children. This feature can be
0139: * disabled using the setAllowsChildSelection() method of the selection model
0140: * instance.
0141: * <p>
0142: * If you are interested in knowing when the selection changes implement the
0143: * <code>GraphSelectionListener</code> interface and add the instance using
0144: * the method <code>addGraphSelectionListener</code>.
0145: * <code>valueChanged</code> will be invoked when the selection changes, that
0146: * is if the user clicks twice on the same vertex <code>valueChanged</code>
0147: * will only be invoked once.
0148: * <p>
0149: * <strong>Change Notification </strong>
0150: * <p>
0151: * For detection of double-clicks or when a user clicks on a cell, regardless of
0152: * whether or not it was selected, I recommend you implement a MouseListener and
0153: * use <code>getFirstCellForLocation</code>.
0154: * <p>
0155: * <strong>Undo Support </strong>
0156: * <p>
0157: * To enable Undo-Support, a <code>GraphUndoManager</code> must be added using
0158: * <code>addGraphSelectionListener</code>. The GraphUndoManager is an
0159: * extension of Swing's <code>GraphUndoManager</code> that maintains a command
0160: * history in the context of multiple views. In this setup, a cell may have a
0161: * set of attributes in each view attached to the model.
0162: * <p>
0163: * For example, consider a position that is stored separately in each view. If a
0164: * node is inserted, the change will be visible in all attached views, resulting
0165: * in a new node that pops-up at the initial position. If the node is
0166: * subsequently moved, say, in view1, this does not constitute a change in
0167: * view2. If view2 does an "undo", the move <i>and </i> the insertion must be
0168: * undone, whereas an "undo" in view1 will only undo the previous move
0169: * operation.
0170: * <p>
0171: * Like all <code>JComponent</code> classes, you can use
0172: * {@link javax.swing.InputMap}and {@link javax.swing.ActionMap}to associate
0173: * an {@link javax.swing.Action}object with a {@link javax.swing.KeyStroke}and
0174: * execute the action under specified conditions.
0175: *
0176: * @author Gaudenz Alder
0177: * @version 2.1 16/03/03
0178: *
0179: */
0180: public class JGraph
0181: //DO NOT REMOVE OR MODIFY THIS LINE!
0182: extends JComponent // JAVA13:
0183: // org.jgraph.plaf.basic.TransferHandler.JAdapterComponent
0184: implements Scrollable, Accessible, Serializable {
0185:
0186: public static final String VERSION = "JGraph (v5.10.1.5)";
0187:
0188: public static final int DOT_GRID_MODE = 0;
0189:
0190: public static final int CROSS_GRID_MODE = 1;
0191:
0192: public static final int LINE_GRID_MODE = 2;
0193:
0194: // Turn off XOR painting on MACs since it doesn't work
0195: public static boolean IS_MAC = false;
0196:
0197: static {
0198: try {
0199: String osName = System.getProperty("os.name");
0200: if (osName != null) {
0201: IS_MAC = osName.toLowerCase().startsWith("mac os x");
0202: }
0203: String javaVersion = System.getProperty("java.version");
0204: if (javaVersion.startsWith("1.4")
0205: || javaVersion.startsWith("1.5")) {
0206: // TODO different double buffering for 1.6 JVM?
0207: }
0208: } catch (Exception e) {
0209: // ignore
0210: }
0211: }
0212:
0213: /**
0214: * @see #getUIClassID
0215: * @see #readObject
0216: */
0217: private static final String uiClassID = "GraphUI";
0218:
0219: /** Creates a new event and passes it off the <code>selectionListeners</code>. */
0220: protected transient GraphSelectionRedirector selectionRedirector;
0221:
0222: //
0223: // Bound Properties
0224: //
0225: /**
0226: * The model that defines the graph displayed by this object. Bound
0227: * property.
0228: */
0229: transient protected GraphModel graphModel;
0230:
0231: /**
0232: * The view that defines the display properties of the model. Bound
0233: * property.
0234: */
0235: transient protected GraphLayoutCache graphLayoutCache;
0236:
0237: /** Models the set of selected objects in this graph. Bound property. */
0238: transient protected GraphSelectionModel selectionModel;
0239:
0240: /** Handler for marquee selection. */
0241: transient protected BasicMarqueeHandler marquee;
0242:
0243: /** Off screen image for double buffering */
0244: protected transient Image offscreen;
0245:
0246: /** Whether or not the current background image is correct */
0247: protected transient boolean offscreenValid = false;
0248:
0249: /** The bounds of the offscreen buffer */
0250: protected transient Rectangle2D offscreenBounds;
0251:
0252: /** The offset of the offscreen buffer */
0253: protected transient Point2D offscreenOffset;
0254:
0255: /** Graphics object of off screen image */
0256: protected transient Graphics offgraphics;
0257:
0258: /**
0259: * The buffer around the offscreen graphics object that provides the
0260: * specified distance of scrolling before the buffer has to be redrawn.
0261: * Increasing the value means fewer redraws but more memory is required.
0262: */
0263: protected transient int offscreenBuffer = 0;
0264:
0265: /**
0266: * Whether or not to try to use a volatile offscreen buffer for double
0267: * buffering. Volatile
0268: */
0269: protected boolean volatileOffscreen = false;
0270:
0271: /** Holds the background image. */
0272: protected ImageIcon backgroundImage;
0273:
0274: /** A Component responsible for drawing the background image, if any */
0275: protected Component backgroundComponent;
0276:
0277: /** Whether or not the background image is scaled on zooming */
0278: protected boolean backgroundScaled = true;
0279:
0280: /** Scale of the graph. Default is 1. Bound property. */
0281: protected double scale = 1.0;
0282:
0283: /** True if the graph is anti-aliased. Default is false. Bound property. */
0284: protected boolean antiAliased = false;
0285:
0286: /** True if the graph allows editing the value of a cell. Bound property. */
0287: protected boolean editable = true;
0288:
0289: /** True if the graph allows editing of non-leaf cells. Bound property. */
0290: protected boolean groupsEditable = false;
0291:
0292: /**
0293: * True if the graph allows selection of cells. Note: You must also disable
0294: * selectNewCells if you disable this. Bound property.
0295: */
0296: protected boolean selectionEnabled = true;
0297:
0298: /**
0299: * True if the graph allows invalid null ports during previews (aka flip
0300: * back edges). Default is true.
0301: */
0302: protected boolean previewInvalidNullPorts = true;
0303:
0304: /** True if the grid is visible. Bound property. */
0305: protected boolean gridVisible = false;
0306:
0307: /** The size of the grid in points. Default is 10. Bound property. */
0308: protected double gridSize = 10;
0309:
0310: /** The style of the grid. Use one of the _GRID_MODE constants. */
0311: protected int gridMode = DOT_GRID_MODE;
0312:
0313: /** True if the ports are visible. Bound property. */
0314: protected boolean portsVisible = false;
0315:
0316: /** True if the ports are scaled. Bound property. */
0317: protected boolean portsScaled = true;
0318:
0319: /** True if the graph allows to move cells below zero. */
0320: protected boolean moveBelowZero = false;
0321:
0322: /** True if the graph allows to move cells beyond the graph bounds */
0323: protected boolean moveBeyondGraphBounds = true;
0324:
0325: /** True if the labels on edges may be moved. */
0326: protected boolean edgeLabelsMovable = true;
0327:
0328: /**
0329: * True if the graph should be auto resized when cells are moved below the
0330: * bottom right corner. Default is true.
0331: */
0332: protected boolean autoResizeGraph = true;
0333:
0334: //
0335: // Look-And-Feel dependent
0336: //
0337: /** Highlight Color. Changes when the Look-and-Feel changes. */
0338: protected Color highlightColor = Color.green;
0339:
0340: /**
0341: * Color of the handles and locked handles. Changes when the Look-and-Feel
0342: * changes.
0343: */
0344: protected Color handleColor, lockedHandleColor;
0345:
0346: /** Color of the marquee. Changes when the Look-and-Feel changes. */
0347: protected Color marqueeColor;
0348:
0349: /** The color of the grid. Changes when the Look-and-Feel changes. */
0350: protected Color gridColor;
0351:
0352: //
0353: // Datatransfer
0354: //
0355: /**
0356: * True if Drag-and-Drop should be used for move operations. Default is
0357: * false due to a JDK bug.
0358: */
0359: protected boolean dragEnabled = false;
0360:
0361: /**
0362: * True if the graph accepts transfers from other components (graphs). This
0363: * also affects the clipboard. Default is true.
0364: */
0365: protected boolean dropEnabled = true;
0366:
0367: /**
0368: * True if the graph accepts transfers from other components (graphs). This
0369: * also affects the clipboard. Default is true.
0370: */
0371: protected boolean xorEnabled = !IS_MAC;
0372:
0373: //
0374: // Unbound Properties
0375: //
0376: /** Number of clicks for editing to start. Default is 2 clicks. */
0377: protected int editClickCount = 2;
0378:
0379: /** True if the graph allows interactions. Default is true. */
0380: protected boolean enabled = true;
0381:
0382: /** True if the snap method should be active (snap to grid). */
0383: protected boolean gridEnabled = false;
0384:
0385: /** Size of a handle. Default is 3 pixels. */
0386: protected int handleSize = 3;
0387:
0388: /** Maximum distance between a cell and the mousepointer. Default is 4. */
0389: protected int tolerance = 4;
0390:
0391: /** Minimum amount of pixels to start a move transaction. Default is 5. */
0392: protected int minimumMove = 5;
0393:
0394: /**
0395: * True if getPortViewAt should return the default port if no other port is
0396: * found. Default is false.
0397: */
0398: protected boolean isJumpToDefaultPort = false;
0399:
0400: /**
0401: * Specifies if cells should be added to a group when moved over the group's
0402: * area. Default is false.
0403: */
0404: protected boolean isMoveIntoGroups = false;
0405:
0406: /**
0407: * Specifies if cells should be removed from groups when removed from the
0408: * group area. Default is false.
0409: */
0410: protected boolean isMoveOutOfGroups = false;
0411:
0412: /**
0413: * True if selected edges are disconnected from unselected vertices on move.
0414: * Default is false.
0415: */
0416: protected boolean disconnectOnMove = false;
0417:
0418: /** True if the graph allows move operations. Default is true. */
0419: protected boolean moveable = true;
0420:
0421: /** True if the graph allows "ctrl-drag" operations. Default is false. */
0422: protected boolean cloneable = false;
0423:
0424: /** True if the graph allows cells to be resized. Default is true. */
0425: protected boolean sizeable = true;
0426:
0427: /**
0428: * True if the graph allows points to be modified/added/removed. Default is
0429: * true.
0430: */
0431: protected boolean bendable = true;
0432:
0433: /**
0434: * True if the graph allows new connections to be established. Default is
0435: * true.
0436: */
0437: protected boolean connectable = true;
0438:
0439: /**
0440: * True if the graph allows existing connections to be removed. Default is
0441: * true.
0442: */
0443: protected boolean disconnectable = true;
0444:
0445: /**
0446: * If true, when editing is to be stopped by way of selection changing, data
0447: * in graph changing or other means <code>stopCellEditing</code> is
0448: * invoked, and changes are saved. If false, <code>cancelCellEditing</code>
0449: * is invoked, and changes are discarded.
0450: */
0451: protected boolean invokesStopCellEditing;
0452:
0453: //
0454: // Bound propery names
0455: //
0456: /**
0457: * Bound property name for <code>graphModel</code>.
0458: */
0459: public final static String GRAPH_MODEL_PROPERTY = "model";
0460:
0461: /**
0462: * Bound property name for <code>graphModel</code>.
0463: */
0464: public final static String GRAPH_LAYOUT_CACHE_PROPERTY = "view";
0465:
0466: /**
0467: * Bound property name for <code>graphModel</code>.
0468: */
0469: public final static String MARQUEE_HANDLER_PROPERTY = "marquee";
0470:
0471: /**
0472: * Bound property name for <code>editable</code>.
0473: */
0474: public final static String EDITABLE_PROPERTY = "editable";
0475:
0476: /**
0477: * Bound property name for <code>selectionEnabled</code>.
0478: */
0479: public final static String SELECTIONENABLED_PROPERTY = "selectionEnabled";
0480:
0481: /**
0482: * Bound property name for <code>scale</code>.
0483: */
0484: public final static String SCALE_PROPERTY = "scale";
0485:
0486: /**
0487: * Bound property name for <code>antiAliased</code>.
0488: */
0489: public final static String ANTIALIASED_PROPERTY = "antiAliased";
0490:
0491: /**
0492: * Bound property name for <code>gridSize</code>.
0493: */
0494: public final static String GRID_SIZE_PROPERTY = "gridSize";
0495:
0496: /**
0497: * Bound property name for <code>gridVisible</code>.
0498: */
0499: public final static String GRID_VISIBLE_PROPERTY = "gridVisible";
0500:
0501: /**
0502: * Bound property name for <code>gridColor</code>.
0503: */
0504: public final static String GRID_COLOR_PROPERTY = "gridColor";
0505:
0506: /**
0507: * Bound property name for <code>gridColor</code>.
0508: */
0509: public final static String HANDLE_COLOR_PROPERTY = "handleColor";
0510:
0511: /**
0512: * Bound property name for <code>gridColor</code>.
0513: */
0514: public final static String HANDLE_SIZE_PROPERTY = "handleSize";
0515:
0516: /**
0517: * Bound property name for <code>gridColor</code>.
0518: */
0519: public final static String LOCKED_HANDLE_COLOR_PROPERTY = "lockedHandleColor";
0520:
0521: /**
0522: * Bound property name for <code>gridVisible</code>.
0523: */
0524: public final static String PORTS_VISIBLE_PROPERTY = "portsVisible";
0525:
0526: /**
0527: * Bound property name for <code>portsScaled</code>.
0528: */
0529: public final static String PORTS_SCALED_PROPERTY = "portsScaled";
0530:
0531: /**
0532: * Bound property name for <code>selectionModel</code>.
0533: */
0534: public final static String SELECTION_MODEL_PROPERTY = "selectionModel";
0535:
0536: /**
0537: * Bound property name for <code>messagesStopCellEditing</code>.
0538: */
0539: public final static String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
0540:
0541: /**
0542: * Bound property name for <code>backgroundImage</code>.
0543: */
0544: public final static String PROPERTY_BACKGROUNDIMAGE = "backgroundImage";
0545:
0546: /**
0547: * Creates and returns a sample <code>GraphModel</code>. Used primarily
0548: * for beanbuilders to show something interesting.
0549: */
0550: public static void addSampleData(GraphModel model) {
0551: ConnectionSet cs = new ConnectionSet();
0552: Map attributes = new Hashtable();
0553: // Styles For Implement/Extend/Aggregation
0554: AttributeMap implementStyle = new AttributeMap();
0555: GraphConstants.setLineBegin(implementStyle,
0556: GraphConstants.ARROW_TECHNICAL);
0557: GraphConstants.setBeginSize(implementStyle, 10);
0558: GraphConstants.setDashPattern(implementStyle, new float[] { 3,
0559: 3 });
0560: if (GraphConstants.DEFAULTFONT != null) {
0561: GraphConstants.setFont(implementStyle,
0562: GraphConstants.DEFAULTFONT.deriveFont(10));
0563: }
0564: AttributeMap extendStyle = new AttributeMap();
0565: GraphConstants.setLineBegin(extendStyle,
0566: GraphConstants.ARROW_TECHNICAL);
0567: GraphConstants.setBeginFill(extendStyle, true);
0568: GraphConstants.setBeginSize(extendStyle, 10);
0569: if (GraphConstants.DEFAULTFONT != null) {
0570: GraphConstants.setFont(extendStyle,
0571: GraphConstants.DEFAULTFONT.deriveFont(10));
0572: }
0573: AttributeMap aggregateStyle = new AttributeMap();
0574: GraphConstants.setLineBegin(aggregateStyle,
0575: GraphConstants.ARROW_DIAMOND);
0576: GraphConstants.setBeginFill(aggregateStyle, true);
0577: GraphConstants.setBeginSize(aggregateStyle, 6);
0578: GraphConstants.setLineEnd(aggregateStyle,
0579: GraphConstants.ARROW_SIMPLE);
0580: GraphConstants.setEndSize(aggregateStyle, 8);
0581: GraphConstants.setLabelPosition(aggregateStyle,
0582: new Point2D.Double(500, 0));
0583: if (GraphConstants.DEFAULTFONT != null) {
0584: GraphConstants.setFont(aggregateStyle,
0585: GraphConstants.DEFAULTFONT.deriveFont(10));
0586: }
0587: //
0588: // The Swing MVC Pattern
0589: //
0590: // Model Column
0591: DefaultGraphCell gm = new DefaultGraphCell("GraphModel");
0592: attributes.put(gm, createBounds(new AttributeMap(), 20, 100,
0593: Color.blue));
0594: gm.addPort(null, "GraphModel/Center");
0595: DefaultGraphCell dgm = new DefaultGraphCell("DefaultGraphModel");
0596: attributes.put(dgm, createBounds(new AttributeMap(), 20, 180,
0597: Color.blue));
0598: dgm.addPort(null, "DefaultGraphModel/Center");
0599: DefaultEdge dgmImplementsGm = new DefaultEdge("implements");
0600: cs
0601: .connect(dgmImplementsGm, gm.getChildAt(0), dgm
0602: .getChildAt(0));
0603: attributes.put(dgmImplementsGm, implementStyle);
0604: DefaultGraphCell modelGroup = new DefaultGraphCell("ModelGroup");
0605: modelGroup.add(gm);
0606: modelGroup.add(dgm);
0607: modelGroup.add(dgmImplementsGm);
0608: // JComponent Column
0609: DefaultGraphCell jc = new DefaultGraphCell("JComponent");
0610: attributes.put(jc, createBounds(new AttributeMap(), 180, 20,
0611: Color.green));
0612: jc.addPort(null, "JComponent/Center");
0613: DefaultGraphCell jg = new DefaultGraphCell("JGraph");
0614: attributes.put(jg, createBounds(new AttributeMap(), 180, 100,
0615: Color.green));
0616: jg.addPort(null, "JGraph/Center");
0617: DefaultEdge jgExtendsJc = new DefaultEdge("extends");
0618: cs.connect(jgExtendsJc, jc.getChildAt(0), jg.getChildAt(0));
0619: attributes.put(jgExtendsJc, extendStyle);
0620: // UI Column
0621: DefaultGraphCell cu = new DefaultGraphCell("ComponentUI");
0622: attributes.put(cu, createBounds(new AttributeMap(), 340, 20,
0623: Color.red));
0624: cu.addPort(null, "ComponentUI/Center");
0625: DefaultGraphCell gu = new DefaultGraphCell("GraphUI");
0626: attributes.put(gu, createBounds(new AttributeMap(), 340, 100,
0627: Color.red));
0628: gu.addPort(null, "GraphUI/Center");
0629: DefaultGraphCell dgu = new DefaultGraphCell("BasicGraphUI");
0630: attributes.put(dgu, createBounds(new AttributeMap(), 340, 180,
0631: Color.red));
0632: dgu.addPort(null, "BasicGraphUI/Center");
0633: DefaultEdge guExtendsCu = new DefaultEdge("extends");
0634: cs.connect(guExtendsCu, cu.getChildAt(0), gu.getChildAt(0));
0635: attributes.put(guExtendsCu, extendStyle);
0636: DefaultEdge dguImplementsDu = new DefaultEdge("implements");
0637: cs
0638: .connect(dguImplementsDu, gu.getChildAt(0), dgu
0639: .getChildAt(0));
0640: attributes.put(dguImplementsDu, implementStyle);
0641: DefaultGraphCell uiGroup = new DefaultGraphCell("UIGroup");
0642: uiGroup.add(cu);
0643: uiGroup.add(gu);
0644: uiGroup.add(dgu);
0645: uiGroup.add(dguImplementsDu);
0646: uiGroup.add(guExtendsCu);
0647: // Aggregations
0648: DefaultEdge jgAggregatesGm = new DefaultEdge("model");
0649: cs.connect(jgAggregatesGm, jg.getChildAt(0), gm.getChildAt(0));
0650: attributes.put(jgAggregatesGm, aggregateStyle);
0651: DefaultEdge jcAggregatesCu = new DefaultEdge("ui");
0652: cs.connect(jcAggregatesCu, jc.getChildAt(0), cu.getChildAt(0));
0653: attributes.put(jcAggregatesCu, aggregateStyle);
0654: // Insert Cells into model
0655: Object[] cells = new Object[] { jgAggregatesGm, jcAggregatesCu,
0656: modelGroup, jc, jg, jgExtendsJc, uiGroup };
0657: model.insert(cells, attributes, cs, null, null);
0658: }
0659:
0660: /**
0661: * Returns an attributeMap for the specified position and color.
0662: */
0663: public static Map createBounds(AttributeMap map, int x, int y,
0664: Color c) {
0665: GraphConstants.setBounds(map, map.createRect(x, y, 90, 30));
0666: GraphConstants.setBorder(map, BorderFactory
0667: .createRaisedBevelBorder());
0668: GraphConstants.setBackground(map, c.darker().darker());
0669: GraphConstants.setGradientColor(map, c.brighter().brighter()
0670: .brighter());
0671: GraphConstants.setForeground(map, Color.white);
0672: if (GraphConstants.DEFAULTFONT != null) {
0673: GraphConstants.setFont(map, GraphConstants.DEFAULTFONT
0674: .deriveFont(Font.BOLD, 12));
0675: }
0676: GraphConstants.setOpaque(map, true);
0677: return map;
0678: }
0679:
0680: /**
0681: * Returns a <code>JGraph</code> with a sample model.
0682: */
0683: public JGraph() {
0684: this ((GraphModel) null);
0685: }
0686:
0687: /**
0688: * Returns an instance of <code>JGraph</code> which displays the the
0689: * specified data model.
0690: *
0691: * @param model
0692: * the <code>GraphModel</code> to use as the data model
0693: */
0694: public JGraph(GraphModel model) {
0695: this (model, (GraphLayoutCache) null);
0696: }
0697:
0698: /**
0699: * Returns an instance of <code>JGraph</code> which displays the data
0700: * model using the specified view.
0701: *
0702: * @param cache
0703: * the <code>GraphLayoutCache</code> to use as the view
0704: */
0705: public JGraph(GraphLayoutCache cache) {
0706: this ((cache != null) ? cache.getModel() : null, cache);
0707: }
0708:
0709: /**
0710: * Returns an instance of <code>JGraph</code> which displays the specified
0711: * data model using the specified view.
0712: *
0713: * @param model
0714: * the <code>GraphModel</code> to use as the data model
0715: * @param cache
0716: * the <code>GraphLayoutCache</code> to use as the cache
0717: */
0718: public JGraph(GraphModel model, GraphLayoutCache cache) {
0719: this (model, cache, new BasicMarqueeHandler());
0720: }
0721:
0722: /**
0723: * Returns an instance of <code>JGraph</code> which displays the specified
0724: * data model and assigns the specified marquee handler
0725: *
0726: * @param model
0727: * the <code>GraphModel</code> to use as the data model
0728: * @param mh
0729: * the <code>BasicMarqueeHandler</code> to use as the marquee
0730: * handler
0731: */
0732: public JGraph(GraphModel model, BasicMarqueeHandler mh) {
0733: this (model, null, mh);
0734: }
0735:
0736: /**
0737: * Returns an instance of <code>JGraph</code> which displays the specified
0738: * data model using the specified view and assigns the specified marquee
0739: * handler
0740: *
0741: * @param model
0742: * the <code>GraphModel</code> to use as the data model
0743: * @param layoutCache
0744: * the <code>GraphLayoutCache</code> to use as the cache
0745: * @param mh
0746: * the <code>BasicMarqueeHandler</code> to use as the marquee
0747: * handler
0748: */
0749: public JGraph(GraphModel model, GraphLayoutCache layoutCache,
0750: BasicMarqueeHandler mh) {
0751: setDoubleBuffered(true);
0752: selectionModel = new DefaultGraphSelectionModel(this );
0753: setLayout(null);
0754: marquee = mh;
0755: if (model == null) {
0756: model = new DefaultGraphModel();
0757: setModel(model);
0758: addSampleData(model);
0759: } else
0760: setModel(model);
0761: if (layoutCache == null)
0762: layoutCache = new GraphLayoutCache(model,
0763: new DefaultCellViewFactory());
0764: setGraphLayoutCache(layoutCache);
0765: updateUI();
0766: }
0767:
0768: //
0769: // UI-delegate (GraphUI)
0770: //
0771: /**
0772: * Returns the L&F object that renders this component.
0773: *
0774: * @return the GraphUI object that renders this component
0775: */
0776: public GraphUI getUI() {
0777: return (GraphUI) ui;
0778: }
0779:
0780: /**
0781: * Sets the L&F object that renders this component.
0782: *
0783: * @param ui
0784: * the GraphUI L&F object
0785: * @see javax.swing.UIDefaults#getUI(JComponent)
0786: *
0787: */
0788: public void setUI(GraphUI ui) {
0789: if ((GraphUI) this .ui != ui) {
0790: super .setUI(ui);
0791: }
0792: }
0793:
0794: /**
0795: * Notification from the <code>UIManager</code> that the L&F has changed.
0796: * Replaces the current UI object with the latest version from the
0797: * <code>UIManager</code>. Subclassers can override this to support
0798: * different GraphUIs.
0799: *
0800: * @see JComponent#updateUI
0801: *
0802: */
0803: public void updateUI() {
0804: setUI(new org.jgraph.plaf.basic.BasicGraphUI());
0805: invalidate();
0806: }
0807:
0808: /**
0809: * Returns the name of the L&F class that renders this component.
0810: *
0811: * @return the string "GraphUI"
0812: * @see JComponent#getUIClassID
0813: *
0814: */
0815: public String getUIClassID() {
0816: return uiClassID;
0817: }
0818:
0819: //
0820: // Content
0821: //
0822: /**
0823: * Returns all root cells (cells that have no parent) that the model
0824: * contains.
0825: */
0826: public Object[] getRoots() {
0827: return DefaultGraphModel.getRoots(graphModel);
0828: }
0829:
0830: /**
0831: * Returns all cells that intersect the given rectangle.
0832: */
0833: public Object[] getRoots(Rectangle clip) {
0834: CellView[] views = graphLayoutCache.getRoots(clip);
0835: Object[] cells = new Object[views.length];
0836: for (int i = 0; i < views.length; i++)
0837: cells[i] = views[i].getCell();
0838: return cells;
0839: }
0840:
0841: /**
0842: * Returns all <code>cells</code> including all descendants in the passed
0843: * in order of cells.
0844: */
0845: public Object[] getDescendants(Object[] cells) {
0846: return DefaultGraphModel.getDescendants(getModel(), cells)
0847: .toArray();
0848: }
0849:
0850: /**
0851: * Returns all <code>cells</code> including all descendants ordered using
0852: * the current layering data stored by the model.
0853: */
0854: public Object[] order(Object[] cells) {
0855: return DefaultGraphModel.order(getModel(), cells);
0856: }
0857:
0858: /**
0859: * Returns a map of (cell, clone)-pairs for all <code>cells</code> and
0860: * their children. Special care is taken to replace the anchor references
0861: * between ports. (Iterative implementation.)
0862: */
0863: public Map cloneCells(Object[] cells) {
0864: return graphModel.cloneCells(cells);
0865: }
0866:
0867: /**
0868: * Returns the topmost cell view at the specified location using the view's
0869: * bounds on non-leafs to check for containment. If reverse is true this
0870: * will return the innermost view.
0871: */
0872: public CellView getTopmostViewAt(double x, double y,
0873: boolean reverse, boolean leafsOnly) {
0874: Rectangle2D r = new Rectangle2D.Double(x, y, 1, 1);
0875: Object[] cells = getDescendants(getRoots());
0876: for (int i = (reverse) ? cells.length - 1 : 0; i >= 0
0877: && i < cells.length; i += (reverse) ? -1 : +1) {
0878: CellView view = getGraphLayoutCache().getMapping(cells[i],
0879: false);
0880: if (view != null
0881: && (!leafsOnly || view.isLeaf())
0882: && ((view.isLeaf() && view.intersects(this , r)) || (!view
0883: .isLeaf() && view.getBounds()
0884: .contains(x, y))))
0885: return view;
0886: }
0887: return null;
0888: }
0889:
0890: /**
0891: * Returns the topmost cell at the specified location.
0892: *
0893: * @param x
0894: * an integer giving the number of pixels horizontally from the
0895: * left edge of the display area, minus any left margin
0896: * @param y
0897: * an integer giving the number of pixels vertically from the top
0898: * of the display area, minus any top margin
0899: * @return the topmost cell at the specified location
0900: */
0901: public Object getFirstCellForLocation(double x, double y) {
0902: return getNextCellForLocation(null, x, y);
0903: }
0904:
0905: /**
0906: * Returns the cell at the specified location that is "behind" the
0907: * <code>current</code> cell. Returns the topmost cell if there are no
0908: * more cells behind <code>current</code>. Note: This does only return
0909: * visible cells.
0910: */
0911: public Object getNextCellForLocation(Object current, double x,
0912: double y) {
0913: CellView cur = graphLayoutCache.getMapping(current, false);
0914: CellView cell = getNextViewAt(cur, x, y);
0915: if (cell != null)
0916: return cell.getCell();
0917: return null;
0918: }
0919:
0920: /**
0921: * Returns the bounding rectangle of the specified cell.
0922: */
0923: public Rectangle2D getCellBounds(Object cell) {
0924: CellView view = graphLayoutCache.getMapping(cell, false);
0925: if (view != null)
0926: return view.getBounds();
0927: return null;
0928: }
0929:
0930: /**
0931: * Returns the bounding rectangle of the specified cells.
0932: */
0933: public Rectangle2D getCellBounds(Object[] cells) {
0934: if (cells != null && cells.length > 0) {
0935: Rectangle2D r = getCellBounds(cells[0]);
0936: Rectangle2D ret = (r != null) ? (Rectangle2D) r.clone()
0937: : null;
0938: for (int i = 1; i < cells.length; i++) {
0939: r = getCellBounds(cells[i]);
0940: if (r != null) {
0941: if (ret == null)
0942: ret = (r != null) ? (Rectangle2D) r.clone()
0943: : null;
0944: else
0945: Rectangle2D.union(ret, r, ret);
0946: }
0947: }
0948: return ret;
0949: }
0950: return null;
0951: }
0952:
0953: /**
0954: * Returns the next view at the specified location wrt. <code>current</code>.
0955: * This is used to iterate overlapping cells, and cells that are grouped.
0956: * The current selection affects this method. <br>
0957: * Note: This returns the next <i>selectable </i> view. <br>
0958: * Note: Arguments are not expected to be scaled (they are scaled in here).
0959: */
0960: public CellView getNextViewAt(CellView current, double x, double y) {
0961: return getNextViewAt(current, x, y, false);
0962: }
0963:
0964: /**
0965: * Returns the next view at the specified location wrt. <code>current</code>.
0966: * This is used to iterate overlapping cells, and cells that are grouped.
0967: * The current selection affects this method. <br>
0968: * Note: This returns the next <i>selectable </i> view. <br>
0969: * Note: Arguments are not expected to be scaled (they are scaled in here).
0970: */
0971: public CellView getNextViewAt(CellView current, double x, double y,
0972: boolean leafsOnly) {
0973: CellView[] cells = AbstractCellView
0974: .getDescendantViews(getGraphLayoutCache().getRoots());
0975: return getNextViewAt(cells, current, x, y, leafsOnly);
0976: }
0977:
0978: /**
0979: * Note: Arguments are not expected to be scaled (they are scaled in here).
0980: */
0981: public CellView getNextSelectableViewAt(CellView current, double x,
0982: double y) {
0983: CellView[] selectables = getGraphLayoutCache().getMapping(
0984: getSelectionModel().getSelectables(), false);
0985: return getNextViewAt(selectables, current, x, y);
0986: }
0987:
0988: /**
0989: * Returns the next view at the specified location wrt. <code>c</code> in
0990: * the specified array of views. The views must be in order, as returned,
0991: * for example, by GraphLayoutCache.order(Object[]).
0992: */
0993: public CellView getNextViewAt(CellView[] cells, CellView c,
0994: double x, double y) {
0995: return getNextViewAt(cells, c, x, y, false);
0996: }
0997:
0998: /**
0999: * Returns the next view at the specified location wrt. <code>c</code> in
1000: * the specified array of views. The views must be in order, as returned,
1001: * for example, by GraphLayoutCache.order(Object[]).
1002: */
1003: public CellView getNextViewAt(CellView[] cells, CellView c,
1004: double x, double y, boolean leafsOnly) {
1005: if (cells != null) {
1006: Rectangle2D r = fromScreen(new Rectangle2D.Double(x
1007: - tolerance, y - tolerance, 2 * tolerance,
1008: 2 * tolerance));
1009: // Iterate through cells and switch to active
1010: // if current is traversed. Cache first cell.
1011: CellView first = null;
1012: boolean active = (c == null);
1013: for (int i = 0; i < cells.length; i++) {
1014: if (cells[i] != null
1015: && (!leafsOnly || cells[i].isLeaf())
1016: && cells[i].intersects(this , r)) {
1017: // TODO: This behaviour is specific to selection and
1018: // should be parametrized (it only returns a group with
1019: // selected children if no other portview is available)
1020: if (active
1021: && !selectionModel
1022: .isChildrenSelected(cells[i]
1023: .getCell())) {
1024: return cells[i];
1025: } else if (first == null)
1026: first = cells[i];
1027: active = active | (cells[i] == c);
1028: }
1029: }
1030: return first;
1031: }
1032: return null;
1033: }
1034:
1035: /**
1036: * Returns the next view at the specified location wrt. <code>c</code> in
1037: * the specified array of views. The views must be in order, as returned,
1038: * for example, by GraphLayoutCache.order(Object[]).
1039: */
1040: public CellView getLeafViewAt(double x, double y) {
1041: return getNextViewAt(null, x, y, true);
1042: }
1043:
1044: /**
1045: * Convenience method to return the port at the specified location.
1046: */
1047: public Object getPortForLocation(double x, double y) {
1048: PortView view = getPortViewAt(x, y, tolerance);
1049: if (view != null)
1050: return view.getCell();
1051: return null;
1052: }
1053:
1054: /**
1055: * Returns the portview at the specified location. <br>
1056: * Note: Arguments are not expected to be scaled (they are scaled in here).
1057: */
1058: public PortView getPortViewAt(double x, double y) {
1059: return getPortViewAt(x, y, tolerance);
1060: }
1061:
1062: /**
1063: * Returns the portview at the specified location. <br>
1064: * Note: Arguments are not expected to be scaled (they are scaled in here).
1065: */
1066: public PortView getPortViewAt(double x, double y, int tolerance) {
1067: double sx = x / scale;
1068: double sy = y / scale;
1069: Rectangle2D r = new Rectangle2D.Double(sx - tolerance, sy
1070: - tolerance, 2 * tolerance, 2 * tolerance);
1071: PortView[] ports = graphLayoutCache.getPorts();
1072: if (ports != null) {
1073: for (int i = ports.length - 1; i >= 0; i--)
1074: if (ports[i] != null && ports[i].intersects(this , r))
1075: return ports[i];
1076: if (isJumpToDefaultPort()) {
1077: CellView cellView = getNextViewAt(null, x, y, true);
1078:
1079: // Finds a non-edge cell under the mousepointer
1080: if (cellView != null
1081: && graphModel.isEdge(cellView.getCell())) {
1082: CellView nextView = getNextViewAt(cellView, x, y,
1083: true);
1084: while (nextView != cellView
1085: && graphModel.isEdge(nextView.getCell())) {
1086: nextView = getNextViewAt(nextView, x, y, true);
1087: }
1088: cellView = nextView;
1089: }
1090: if (cellView != null) {
1091: PortView defaultPort = getDefaultPortForCell(cellView
1092: .getCell());
1093: return defaultPort;
1094: }
1095: }
1096: }
1097: return null;
1098: }
1099:
1100: /**
1101: * Returns the default portview for the specified cell. The default
1102: * implementation returns the first floating port (ie. the first port that
1103: * does not define an offset) or <b>the </b> port, if there is only one
1104: * port.
1105: *
1106: * @param cell
1107: * the cell whose port is to be obtained
1108: * @return the port view of the specified cell
1109: */
1110: public PortView getDefaultPortForCell(Object cell) {
1111: if (cell != null && !getModel().isEdge(cell)) {
1112: int childCount = getModel().getChildCount(cell);
1113: for (int i = 0; i < childCount; i++) {
1114: Object childCell = getModel().getChild(cell, i);
1115: CellView child = getGraphLayoutCache().getMapping(
1116: childCell, false);
1117: if (child instanceof PortView) {
1118: Point2D offset = GraphConstants.getOffset(child
1119: .getAllAttributes());
1120: if (offset == null || childCount == 1)
1121: return (PortView) child;
1122: }
1123: }
1124: }
1125: return null;
1126: }
1127:
1128: /**
1129: * Converts the specified value to string. If the value is an instance of
1130: * CellView then the corresponding value or cell is used.
1131: */
1132: public String convertValueToString(Object value) {
1133: if (value instanceof CellView)
1134: value = ((CellView) value).getCell();
1135: return String.valueOf(value);
1136: }
1137:
1138: //
1139: // Grid and Scale
1140: //
1141: /**
1142: * Returns the given point applied to the grid.
1143: *
1144: * @param p
1145: * a point in screen coordinates.
1146: * @return the same point applied to the grid.
1147: */
1148: public Point2D snap(Point2D p) {
1149: if (gridEnabled && p != null) {
1150: double sgs = gridSize * getScale();
1151: p.setLocation(Math.round(Math.round(p.getX() / sgs) * sgs),
1152: Math.round(Math.round(p.getY() / sgs) * sgs));
1153: }
1154: return p;
1155: }
1156:
1157: /**
1158: * Returns the given rectangle applied to the grid.
1159: *
1160: * @param r
1161: * a rectangle in screen coordinates.
1162: * @return the same rectangle applied to the grid.
1163: */
1164: public Rectangle2D snap(Rectangle2D r) {
1165: if (gridEnabled && r != null) {
1166: double sgs = gridSize * getScale();
1167: r
1168: .setFrame(Math.round(Math.round(r.getX() / sgs)
1169: * sgs), Math.round(Math.round(r.getY()
1170: / sgs)
1171: * sgs), 1 + Math.round(Math.round(r
1172: .getWidth()
1173: / sgs)
1174: * sgs), 1 + Math.round(Math.round(r
1175: .getHeight()
1176: / sgs)
1177: * sgs));
1178: }
1179: return r;
1180: }
1181:
1182: /**
1183: * Returns the given dimension applied to the grid.
1184: *
1185: * @param d
1186: * a dimension in screen coordinates to snap to.
1187: * @return the same dimension applied to the grid.
1188: */
1189: public Dimension2D snap(Dimension2D d) {
1190: if (gridEnabled && d != null) {
1191: double sgs = gridSize * getScale();
1192: d.setSize(1 + Math.round(Math.round(d.getWidth() / sgs)
1193: * sgs), 1 + Math.round(Math.round(d.getHeight()
1194: / sgs)
1195: * sgs));
1196: }
1197: return d;
1198: }
1199:
1200: /**
1201: * Upscale the given point in place, using the given instance.
1202: *
1203: * @param p
1204: * the point to be upscaled
1205: * @return the upscaled point instance
1206: */
1207: public Point2D toScreen(Point2D p) {
1208: if (p == null)
1209: return null;
1210: p.setLocation(Math.round(p.getX() * scale), Math.round(p.getY()
1211: * scale));
1212: return p;
1213: }
1214:
1215: /**
1216: * Downscale the given point in place, using the given instance.
1217: *
1218: * @param p
1219: * the point to be downscaled
1220: * @return the downscaled point instance
1221: */
1222: public Point2D fromScreen(Point2D p) {
1223: if (p == null)
1224: return null;
1225: p.setLocation(Math.round(p.getX() / scale), Math.round(p.getY()
1226: / scale));
1227: return p;
1228: }
1229:
1230: /**
1231: * Upscale the given rectangle in place, using the given instance.
1232: *
1233: * @param rect
1234: * the rectangle to be upscaled
1235: * @return the upscaled rectangle instance
1236: */
1237: public Rectangle2D toScreen(Rectangle2D rect) {
1238: if (rect == null)
1239: return null;
1240: rect.setFrame(rect.getX() * scale, rect.getY() * scale, rect
1241: .getWidth()
1242: * scale, rect.getHeight() * scale);
1243: return rect;
1244: }
1245:
1246: /**
1247: * Downscale the given rectangle in place, using the given instance.
1248: *
1249: * @param rect
1250: * the rectangle to be downscaled
1251: * @return the down-scaled rectangle instance
1252: */
1253: public Rectangle2D fromScreen(Rectangle2D rect) {
1254: if (rect == null)
1255: return null;
1256: rect.setFrame(rect.getX() / scale, rect.getY() / scale, rect
1257: .getWidth()
1258: / scale, rect.getHeight() / scale);
1259: return rect;
1260: }
1261:
1262: /**
1263: * Computes and updates the size for <code>view</code>.
1264: */
1265: public void updateAutoSize(CellView view) {
1266: if (view != null && !isEditing()) {
1267: Rectangle2D bounds = (view.getAttributes() != null) ? GraphConstants
1268: .getBounds(view.getAttributes())
1269: : null;
1270: AttributeMap attrs = getModel().getAttributes(
1271: view.getCell());
1272: if (bounds == null)
1273: bounds = GraphConstants.getBounds(attrs);
1274: if (bounds != null) {
1275: boolean autosize = GraphConstants.isAutoSize(view
1276: .getAllAttributes());
1277: boolean resize = GraphConstants.isResize(view
1278: .getAllAttributes());
1279: if (autosize || resize) {
1280: Dimension2D d = getUI()
1281: .getPreferredSize(this , view);
1282: bounds.setFrame(bounds.getX(), bounds.getY(), d
1283: .getWidth(), d.getHeight());
1284: // Remove resize attribute
1285: snap(bounds);
1286: if (resize) {
1287: if (view.getAttributes() != null)
1288: view.getAttributes().remove(
1289: GraphConstants.RESIZE);
1290: attrs.remove(GraphConstants.RESIZE);
1291: }
1292: view.refresh(getGraphLayoutCache(),
1293: getGraphLayoutCache(), false);
1294: }
1295: }
1296: }
1297: }
1298:
1299: /**
1300: * Returns the attributes for the specified cell. If the layout cache
1301: * returns a view for the cell then this method returns allAttributes,
1302: * otherwise the method returns model.getAttributes(cell).
1303: */
1304: public AttributeMap getAttributes(Object cell) {
1305: AttributeMap attrs;
1306: CellView cellView = getGraphLayoutCache().getMapping(cell,
1307: false);
1308: if (cellView != null) {
1309: attrs = cellView.getAllAttributes();
1310: } else {
1311: attrs = getModel().getAttributes(cell);
1312: }
1313: return attrs;
1314: }
1315:
1316: //
1317: // Unbound Properties
1318: //
1319: /**
1320: * Returns the number of clicks for editing to start.
1321: */
1322: public int getEditClickCount() {
1323: return editClickCount;
1324: }
1325:
1326: /**
1327: * Sets the number of clicks for editing to start.
1328: */
1329: public void setEditClickCount(int count) {
1330: editClickCount = count;
1331: }
1332:
1333: /**
1334: * Returns true if the graph accepts drops/pastes from external sources.
1335: */
1336: public boolean isDropEnabled() {
1337: return dropEnabled;
1338: }
1339:
1340: /**
1341: * Sets if the graph accepts drops/pastes from external sources.
1342: */
1343: public void setDropEnabled(boolean flag) {
1344: dropEnabled = flag;
1345: }
1346:
1347: /**
1348: * Returns true if the graph accepts drops/pastes from external sources.
1349: */
1350: public boolean isXorEnabled() {
1351: return xorEnabled;
1352: }
1353:
1354: /**
1355: * Sets if the graph accepts drops/pastes from external sources.
1356: */
1357: public void setXorEnabled(boolean flag) {
1358: xorEnabled = flag;
1359: }
1360:
1361: /**
1362: * Returns true if the graph uses Drag-and-Drop to move cells.
1363: */
1364: public boolean isDragEnabled() {
1365: return dragEnabled;
1366: }
1367:
1368: /**
1369: * Sets if the graph uses Drag-and-Drop to move cells.
1370: */
1371: public void setDragEnabled(boolean flag) {
1372: dragEnabled = flag;
1373: }
1374:
1375: /*
1376: * Returns true if the graph allows movement of cells.
1377: */
1378: public boolean isMoveable() {
1379: return moveable;
1380: }
1381:
1382: /**
1383: * Sets if the graph allows movement of cells.
1384: */
1385: public void setMoveable(boolean flag) {
1386: moveable = flag;
1387: }
1388:
1389: /**
1390: * Returns true if the graph allows adding/removing/modifying points.
1391: */
1392: public boolean isBendable() {
1393: return bendable;
1394: }
1395:
1396: /**
1397: * Sets if the graph allows adding/removing/modifying points.
1398: */
1399: public void setBendable(boolean flag) {
1400: bendable = flag;
1401: }
1402:
1403: /**
1404: * Returns true if the graph allows new connections to be established.
1405: */
1406: public boolean isConnectable() {
1407: return connectable;
1408: }
1409:
1410: /**
1411: * Setse if the graph allows new connections to be established.
1412: */
1413: public void setConnectable(boolean flag) {
1414: connectable = flag;
1415: }
1416:
1417: /**
1418: * Returns true if the graph allows existing connections to be removed.
1419: */
1420: public boolean isDisconnectable() {
1421: return disconnectable;
1422: }
1423:
1424: /**
1425: * Sets if the graph allows existing connections to be removed.
1426: */
1427: public void setDisconnectable(boolean flag) {
1428: disconnectable = flag;
1429: }
1430:
1431: /**
1432: * Returns true if cells are cloned on CTRL-Drag operations.
1433: */
1434: public boolean isCloneable() {
1435: return cloneable;
1436: }
1437:
1438: /**
1439: * Sets if cells are cloned on CTRL-Drag operations.
1440: */
1441: public void setCloneable(boolean flag) {
1442: cloneable = flag;
1443: }
1444:
1445: /**
1446: * Returns true if the graph allows cells to be resized.
1447: */
1448: public boolean isSizeable() {
1449: return sizeable;
1450: }
1451:
1452: /**
1453: * Sets if the graph allows cells to be resized.
1454: */
1455: public void setSizeable(boolean flag) {
1456: sizeable = flag;
1457: }
1458:
1459: /**
1460: * Sets if selected edges should be disconnected from unselected vertices
1461: * when they are moved.
1462: */
1463: public void setDisconnectOnMove(boolean flag) {
1464: disconnectOnMove = flag;
1465: }
1466:
1467: /**
1468: * Returns true if selected edges should be disconnected from unselected
1469: * vertices when they are moved.
1470: */
1471: public boolean isDisconnectOnMove() {
1472: return disconnectOnMove && disconnectable;
1473: }
1474:
1475: /**
1476: * Sets if getPortViewAt should return the default port if no other port is
1477: * found.
1478: */
1479: public void setJumpToDefaultPort(boolean flag) {
1480: isJumpToDefaultPort = flag;
1481: }
1482:
1483: /**
1484: * Returns true if getPortViewAt should return the default port if no other
1485: * port is found.
1486: */
1487: public boolean isJumpToDefaultPort() {
1488: return isJumpToDefaultPort;
1489: }
1490:
1491: /**
1492: * Specifies if cells should be added to groups when moved over the group's
1493: * area.
1494: */
1495: public void setMoveIntoGroups(boolean flag) {
1496: isMoveIntoGroups = flag;
1497: }
1498:
1499: /**
1500: * Returns true if cells should be added to groups when moved over the
1501: * group's area.
1502: */
1503: public boolean isMoveIntoGroups() {
1504: return isMoveIntoGroups;
1505: }
1506:
1507: /**
1508: * Specifies if cells should be removed from groups when removed from the
1509: * group's area.
1510: */
1511: public void setMoveOutOfGroups(boolean flag) {
1512: isMoveOutOfGroups = flag;
1513: }
1514:
1515: /**
1516: * Returns true if cells should be removed from groups when removed from the
1517: * group's area.
1518: */
1519: public boolean isMoveOutOfGroups() {
1520: return isMoveOutOfGroups;
1521: }
1522:
1523: /**
1524: * Returns true if the grid is active.
1525: *
1526: * @see #snap(Point2D)
1527: *
1528: */
1529: public boolean isGridEnabled() {
1530: return gridEnabled;
1531: }
1532:
1533: /**
1534: * If set to true, the grid will be active.
1535: *
1536: * @see #snap(Point2D)
1537: *
1538: */
1539: public void setGridEnabled(boolean flag) {
1540: gridEnabled = flag;
1541: }
1542:
1543: /**
1544: * Returns true if the graph allows to move cells below zero.
1545: */
1546: public boolean isMoveBelowZero() {
1547: return moveBelowZero;
1548: }
1549:
1550: /**
1551: * Sets if the graph should auto resize when cells are being moved below the
1552: * bottom right corner.
1553: */
1554: public void setMoveBelowZero(boolean moveBelowZero) {
1555: this .moveBelowZero = moveBelowZero;
1556: }
1557:
1558: /**
1559: * @return the moveBeyondGraphBounds
1560: */
1561: public boolean isMoveBeyondGraphBounds() {
1562: return moveBeyondGraphBounds;
1563: }
1564:
1565: /**
1566: * @param moveBeyondGraphBounds the moveBeyondGraphBounds to set
1567: */
1568: public void setMoveBeyondGraphBounds(boolean moveBeyondGraphBounds) {
1569: this .moveBeyondGraphBounds = moveBeyondGraphBounds;
1570: }
1571:
1572: /**
1573: * Returns true if edge labels may be dragged and dropped.
1574: *
1575: * @return whether edge labels may be dragged and dropped
1576: */
1577: public boolean getEdgeLabelsMovable() {
1578: return edgeLabelsMovable;
1579: }
1580:
1581: /**
1582: * Set if edge labels may be moved with the mouse or not.
1583: *
1584: * @param edgeLabelsMovable
1585: * true if edge labels may be dragged
1586: */
1587: public void setEdgeLabelsMovable(boolean edgeLabelsMovable) {
1588: this .edgeLabelsMovable = edgeLabelsMovable;
1589: }
1590:
1591: /**
1592: * Returns true if the graph should be automatically resized when cells are
1593: * being moved below the bottom right corner. Note if the value of
1594: * <code>moveBeyondGraphBounds</code> if <code>false</code> auto resizing
1595: * is automatically disabled
1596: */
1597: public boolean isAutoResizeGraph() {
1598: if (!moveBeyondGraphBounds) {
1599: return false;
1600: }
1601: return autoResizeGraph;
1602: }
1603:
1604: /**
1605: * Sets whether or not the graph should be automatically resize when cells
1606: * are being moved below the bottom right corner
1607: */
1608: public void setAutoResizeGraph(boolean autoResizeGraph) {
1609: this .autoResizeGraph = autoResizeGraph;
1610: }
1611:
1612: /**
1613: * Returns the maximum distance between the mousepointer and a cell to be
1614: * selected.
1615: */
1616: public int getTolerance() {
1617: return tolerance;
1618: }
1619:
1620: /**
1621: * Sets the maximum distance between the mousepointer and a cell to be
1622: * selected.
1623: */
1624: public void setTolerance(int size) {
1625: if (size < 1) {
1626: size = 1;
1627: }
1628: tolerance = size;
1629: }
1630:
1631: /**
1632: * Returns the size of the handles.
1633: */
1634: public int getHandleSize() {
1635: return handleSize;
1636: }
1637:
1638: /**
1639: * Sets the size of the handles.
1640: */
1641: public void setHandleSize(int size) {
1642: int oldValue = handleSize;
1643: handleSize = size;
1644: firePropertyChange(HANDLE_SIZE_PROPERTY, oldValue, size);
1645: }
1646:
1647: /**
1648: * Returns the miminum amount of pixels for a move operation.
1649: */
1650: public int getMinimumMove() {
1651: return minimumMove;
1652: }
1653:
1654: /**
1655: * Sets the miminum amount of pixels for a move operation.
1656: */
1657: public void setMinimumMove(int pixels) {
1658: minimumMove = pixels;
1659: }
1660:
1661: //
1662: // Laf-Specific color scheme. These colors are changed
1663: // by BasicGraphUI when the laf changes.
1664: //
1665: /**
1666: * Returns the current grid color.
1667: */
1668: public Color getGridColor() {
1669: return gridColor;
1670: }
1671:
1672: /**
1673: * Sets the current grid color.
1674: */
1675: public void setGridColor(Color newColor) {
1676: Color oldValue = gridColor;
1677: gridColor = newColor;
1678: firePropertyChange(GRID_COLOR_PROPERTY, oldValue, newColor);
1679: }
1680:
1681: /**
1682: * Returns the current handle color.
1683: */
1684: public Color getHandleColor() {
1685: return handleColor;
1686: }
1687:
1688: /**
1689: * Sets the current handle color.
1690: */
1691: public void setHandleColor(Color newColor) {
1692: Color oldValue = handleColor;
1693: handleColor = newColor;
1694: firePropertyChange(HANDLE_COLOR_PROPERTY, oldValue, newColor);
1695: }
1696:
1697: /**
1698: * Returns the current second handle color.
1699: */
1700: public Color getLockedHandleColor() {
1701: return lockedHandleColor;
1702: }
1703:
1704: /**
1705: * Sets the current second handle color.
1706: */
1707: public void setLockedHandleColor(Color newColor) {
1708: Color oldValue = lockedHandleColor;
1709: lockedHandleColor = newColor;
1710: firePropertyChange(LOCKED_HANDLE_COLOR_PROPERTY, oldValue,
1711: newColor);
1712: }
1713:
1714: /**
1715: * Returns the current marquee color.
1716: */
1717: public Color getMarqueeColor() {
1718: return marqueeColor;
1719: }
1720:
1721: /**
1722: * Sets the current marquee color.
1723: */
1724: public void setMarqueeColor(Color newColor) {
1725: marqueeColor = newColor;
1726: }
1727:
1728: /**
1729: * Returns the current highlight color.
1730: */
1731: public Color getHighlightColor() {
1732: return highlightColor;
1733: }
1734:
1735: /**
1736: * Sets the current selection highlight color.
1737: */
1738: public void setHighlightColor(Color newColor) {
1739: highlightColor = newColor;
1740: }
1741:
1742: //
1743: // Bound properties
1744: //
1745: /**
1746: * Returns the current scale.
1747: *
1748: * @return the current scale as a double
1749: */
1750: public double getScale() {
1751: return scale;
1752: }
1753:
1754: /**
1755: * Sets the current scale.
1756: * <p>
1757: * Fires a property change for the SCALE_PROPERTY.
1758: *
1759: * @param newValue
1760: * the new scale
1761: */
1762: public void setScale(double newValue) {
1763: Point2D centerPoint = getCenterPoint();
1764: setScale(newValue, centerPoint);
1765: }
1766:
1767: /**
1768: * Sets the current scale and centers the graph to the specified point
1769: *
1770: * @param newValue
1771: * the new scale
1772: * @param center
1773: * the center of the graph
1774: */
1775: public void setScale(double newValue, Point2D center) {
1776: if (newValue > 0 && newValue != this .scale) {
1777: Rectangle2D view = getViewPortBounds();
1778: double oldValue = this .scale;
1779: scale = newValue;
1780: boolean zoomIn = true;
1781: Rectangle newView = null;
1782: if (view != null) {
1783: double scaleRatio = newValue / oldValue;
1784: int newCenterX = (int) (center.getX() * scaleRatio);
1785: int newCenterY = (int) (center.getY() * scaleRatio);
1786: int newX = (int) (newCenterX - view.getWidth() / 2.0);
1787: int newY = (int) (newCenterY - view.getHeight() / 2.0);
1788: newView = new Rectangle(newX, newY, (int) view
1789: .getWidth(), (int) view.getHeight());
1790: // When zooming out scroll before revalidation otherwise
1791: // revalidation causes one scroll and scrollRectToVisible
1792: // another
1793: if (scaleRatio < 1.0) {
1794: scrollRectToVisible(newView);
1795: zoomIn = false;
1796: }
1797: }
1798: offscreenValid = false;
1799: firePropertyChange(SCALE_PROPERTY, oldValue, newValue);
1800: // When zooming in, do it after the revalidation otherwise
1801: // it intermittently moves to the old co-ordinate system
1802: if (zoomIn && newView != null) {
1803: scrollRectToVisible(newView);
1804: }
1805: }
1806: }
1807:
1808: /**
1809: * Returns the center of the component relative to the parent viewport's
1810: * position.
1811: */
1812: public Point2D getCenterPoint() {
1813: Rectangle2D viewBounds = getViewPortBounds();
1814: if (viewBounds != null) {
1815: return new Point2D.Double(viewBounds.getCenterX(),
1816: viewBounds.getCenterY());
1817: }
1818: viewBounds = getBounds();
1819: return new Point2D.Double(viewBounds.getCenterX(), viewBounds
1820: .getCenterY());
1821: }
1822:
1823: /**
1824: * Return the bounds of the parent viewport, if one exists. If one does not
1825: * exist, null is returned
1826: *
1827: * @return the bounds of the parent viewport
1828: */
1829: public Rectangle2D getViewPortBounds() {
1830: if (getParent() instanceof JViewport) {
1831: return ((JViewport) getParent()).getViewRect();
1832: }
1833: return null;
1834: }
1835:
1836: /**
1837: * Returns the size of the grid in pixels.
1838: *
1839: * @return the size of the grid as an int
1840: */
1841: public double getGridSize() {
1842: return gridSize;
1843: }
1844:
1845: /**
1846: * Returns the current grid view mode.
1847: */
1848: public int getGridMode() {
1849: return gridMode;
1850: }
1851:
1852: /**
1853: * Sets the size of the grid.
1854: * <p>
1855: * Fires a property change for the GRID_SIZE_PROPERTY.
1856: *
1857: * @param newSize
1858: * the new size of the grid in pixels
1859: */
1860: public void setGridSize(double newSize) {
1861: double oldValue = this .gridSize;
1862: this .gridSize = newSize;
1863: firePropertyChange(GRID_SIZE_PROPERTY, oldValue, newSize);
1864: }
1865:
1866: /**
1867: * Sets the current grid view mode.
1868: *
1869: * @param mode
1870: * The current grid view mode. Valid values are <CODE>
1871: * DOT_GRID_MODE</CODE>,<CODE>CROSS_GRID_MODE</CODE>, and
1872: * <CODE>LINE_GRID_MODE</CODE>.
1873: */
1874: public void setGridMode(int mode) {
1875: if (mode == DOT_GRID_MODE || mode == CROSS_GRID_MODE
1876: || mode == LINE_GRID_MODE) {
1877: gridMode = mode;
1878: repaint();
1879: }
1880: }
1881:
1882: /**
1883: * Returns true if the grid will be visible.
1884: *
1885: * @return true if the grid is visible
1886: */
1887: public boolean isGridVisible() {
1888: return gridVisible;
1889: }
1890:
1891: /**
1892: * If set to true, the grid will be visible.
1893: * <p>
1894: * Fires a property change for the GRID_VISIBLE_PROPERTY.
1895: */
1896: public void setGridVisible(boolean flag) {
1897: boolean oldValue = gridVisible;
1898: gridVisible = flag;
1899: firePropertyChange(GRID_VISIBLE_PROPERTY, oldValue, flag);
1900: }
1901:
1902: /**
1903: * Returns true if the ports will be visible.
1904: *
1905: * @return true if the ports are visible
1906: */
1907: public boolean isPortsVisible() {
1908: return portsVisible;
1909: }
1910:
1911: /**
1912: * If set to true, the ports will be visible.
1913: * <p>
1914: * Fires a property change for the PORTS_VISIBLE_PROPERTY.
1915: */
1916: public void setPortsVisible(boolean flag) {
1917: boolean oldValue = portsVisible;
1918: portsVisible = flag;
1919: firePropertyChange(PORTS_VISIBLE_PROPERTY, oldValue, flag);
1920: }
1921:
1922: /**
1923: * Returns true if the ports will be scaled.
1924: *
1925: * @return true if the ports are visible
1926: */
1927: public boolean isPortsScaled() {
1928: return portsScaled;
1929: }
1930:
1931: /**
1932: * If set to true, the ports will be scaled.
1933: * <p>
1934: * Fires a property change for the PORTS_SCALED_PROPERTY.
1935: */
1936: public void setPortsScaled(boolean flag) {
1937: boolean oldValue = portsScaled;
1938: portsScaled = flag;
1939: firePropertyChange(PORTS_SCALED_PROPERTY, oldValue, flag);
1940: }
1941:
1942: /**
1943: * Returns true if the graph will be anti aliased.
1944: *
1945: * @return true if the graph is anti aliased
1946: */
1947: public boolean isAntiAliased() {
1948: return antiAliased;
1949: }
1950:
1951: /**
1952: * Sets antialiasing on or off based on the boolean value.
1953: * <p>
1954: * Fires a property change for the ANTIALIASED_PROPERTY.
1955: *
1956: * @param newValue
1957: * whether to turn antialiasing on or off
1958: */
1959: public void setAntiAliased(boolean newValue) {
1960: boolean oldValue = this .antiAliased;
1961: this .antiAliased = newValue;
1962: firePropertyChange(ANTIALIASED_PROPERTY, oldValue, newValue);
1963: }
1964:
1965: /**
1966: * Returns true if the graph is editable (if it allows cells to be edited).
1967: *
1968: * @return true if the graph is editable
1969: */
1970: public boolean isEditable() {
1971: return editable;
1972: }
1973:
1974: /**
1975: * Determines whether the graph is editable. Fires a property change event
1976: * if the new setting is different from the existing setting.
1977: * <p>
1978: * Note: Editable determines whether the graph allows editing. This is not
1979: * to be confused with enabled, which allows the graph to handle mouse
1980: * events (including editing).
1981: *
1982: * @param flag
1983: * a boolean value, true if the graph is editable
1984: */
1985: public void setEditable(boolean flag) {
1986: boolean oldValue = this .editable;
1987: this .editable = flag;
1988: firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
1989: }
1990:
1991: /**
1992: * @return the groupsEditable
1993: */
1994: public boolean isGroupsEditable() {
1995: return groupsEditable;
1996: }
1997:
1998: /**
1999: * @param groupsEditable the groupsEditable to set
2000: */
2001: public void setGroupsEditable(boolean groupsEditable) {
2002: this .groupsEditable = groupsEditable;
2003: }
2004:
2005: /**
2006: * Returns true if the cell selection is enabled
2007: *
2008: * @return true if the cell selection is enabled
2009: */
2010: public boolean isSelectionEnabled() {
2011: return selectionEnabled;
2012: }
2013:
2014: /**
2015: * Determines whether cell selection is enabled. Fires a property change
2016: * event if the new setting is different from the existing setting.
2017: *
2018: * @param flag
2019: * a boolean value, true if cell selection is enabled
2020: */
2021: public void setSelectionEnabled(boolean flag) {
2022: boolean oldValue = this .selectionEnabled;
2023: this .selectionEnabled = flag;
2024: firePropertyChange(SELECTIONENABLED_PROPERTY, oldValue, flag);
2025: }
2026:
2027: /**
2028: * Returns true if graph allows invalid null ports during previews
2029: *
2030: * @return true if the graph allows invalid null ports during previews
2031: */
2032: public boolean isPreviewInvalidNullPorts() {
2033: return previewInvalidNullPorts;
2034: }
2035:
2036: /**
2037: * Determines whether the graph allows invalid null ports during previews
2038: *
2039: * @param flag
2040: * a boolean value, true if the graph allows invalid null ports
2041: * during previews
2042: */
2043: public void setPreviewInvalidNullPorts(boolean flag) {
2044: this .previewInvalidNullPorts = flag;
2045: }
2046:
2047: /**
2048: * Returns the current double buffering graphics object. Checks to see if
2049: * the graph bounds has changed since the last time the off screen image was
2050: * created and if so, creates a new image.
2051: *
2052: * @return the off screen graphics
2053: */
2054: public Graphics getOffgraphics() {
2055: if (!isDoubleBuffered()) {
2056: // If double buffering is not enabled
2057: return null;
2058: }
2059: // Get the bounds of the entire graph
2060: Rectangle2D graphBounds = getBounds();
2061: // Get the visible area if in a scroll pane
2062: Rectangle2D viewPortBounds = null;//getViewPortBounds();
2063: // The result can be null if not a view port.
2064: if (viewPortBounds == null) {
2065: viewPortBounds = graphBounds;
2066: }
2067: // Find the size of the double buffer in the JVM
2068: int x = Math.max(0, (int) viewPortBounds.getX()
2069: - (int) offscreenBuffer);
2070: int y = Math.max(0, (int) viewPortBounds.getY()
2071: - (int) offscreenBuffer);
2072: int width = (int) viewPortBounds.getWidth()
2073: + (int) offscreenBuffer * 2;
2074: int height = (int) viewPortBounds.getHeight()
2075: + (int) offscreenBuffer * 2;
2076: // Code below used to work out max possible screen size, might not
2077: // be needed in JVM 1.6?
2078: // try {
2079: // Rectangle virtualBounds = new Rectangle();
2080: // GraphicsEnvironment ge = GraphicsEnvironment
2081: // .getLocalGraphicsEnvironment();
2082: // GraphicsDevice[] devices = ge.getScreenDevices();
2083: // for (int i = 0; i < devices.length; i++) {
2084: // GraphicsConfiguration gc = devices[i].getDefaultConfiguration();
2085: // virtualBounds = virtualBounds.union(gc.getBounds());
2086: // }
2087: // doubleBufferSize = new Dimension(virtualBounds.width,
2088: // virtualBounds.height);
2089: // } catch (HeadlessException e) {
2090: // doubleBufferSize = new Dimension((int) graphBounds.getWidth(), (int)
2091: // graphBounds
2092: // .getHeight());
2093: // }
2094: // If the screen size exceed either of the graph dimensions, limit them
2095: // to the graph's
2096: if (width > graphBounds.getWidth()) {
2097: width = (int) graphBounds.getWidth();
2098: }
2099: if (height > graphBounds.getHeight()) {
2100: height = (int) graphBounds.getHeight();
2101: }
2102:
2103: Rectangle2D newOffscreenBuffer = new Rectangle2D.Double(0, 0,
2104: width, height);
2105: // Check whether the visible area is completely contained within the
2106: // buffer. If not, the buffer need to be re-generated
2107: if ((offscreen == null || offgraphics == null || offscreenBounds == null)
2108: || !(offscreenBounds.contains(newOffscreenBuffer))) {
2109: if (offscreen != null) {
2110: offscreen.flush();
2111: }
2112: if (offgraphics != null) {
2113: offgraphics.dispose();
2114: }
2115: offscreen = null;
2116: offgraphics = null;
2117: if (offscreen == null && volatileOffscreen) {
2118: try {
2119: offscreen = createVolatileImage(width, height);
2120: } catch (OutOfMemoryError e) {
2121: offscreen = null;
2122: offgraphics = null;
2123: }
2124: }
2125: if (offscreen == null) {
2126: // Probably running in headless mode, try to create a buffered
2127: // image.
2128: createBufferedImage(width, height);
2129: }
2130: if (offscreen == null) {
2131: // TODO assume the graph is too large and only buffer part
2132: // of it, might also be faster to calculate in
2133: // advance whether they is enough memory to create image
2134: // rather than let it try and throw error.
2135: return null;
2136: }
2137: setupOffScreen(x, y, width, height, newOffscreenBuffer);
2138: } else if (offscreen instanceof VolatileImage) {
2139: int valCode = ((VolatileImage) offscreen)
2140: .validate(getGraphicsConfiguration());
2141: if (!volatileOffscreen) {
2142: offscreen.flush();
2143: offgraphics.dispose();
2144: offscreen = null;
2145: offgraphics = null;
2146: createBufferedImage(width, height);
2147: setupOffScreen(x, y, width, height, newOffscreenBuffer);
2148: } else if (valCode == VolatileImage.IMAGE_INCOMPATIBLE) {
2149: offscreen.flush();
2150: offgraphics.dispose();
2151: try {
2152: offscreen = createVolatileImage(width, height);
2153: } catch (OutOfMemoryError e) {
2154: offscreen = null;
2155: offgraphics = null;
2156: return null;
2157: }
2158: setupOffScreen(x, y, width, height, newOffscreenBuffer);
2159: } else if (valCode == VolatileImage.IMAGE_RESTORED) {
2160: setOffscreenValid(false);
2161: }
2162: }
2163: if (!isOffscreenValid()) {
2164: offgraphics.setColor(getBackground());
2165: offgraphics.setPaintMode();
2166: offgraphics.fillRect(0, 0, (int) graphBounds.getWidth(),
2167: (int) graphBounds.getHeight());
2168: ((BasicGraphUI) getUI()).drawGraph(offgraphics, null);
2169: offscreenValid = true;
2170: }
2171: return offgraphics;
2172: }
2173:
2174: /**
2175: * Utility method to create a standard buffered image
2176: * @param width
2177: * @param height
2178: */
2179: protected void createBufferedImage(int width, int height) {
2180: GraphicsConfiguration graphicsConfig = getGraphicsConfiguration();
2181: if (graphicsConfig != null) {
2182: try {
2183: offscreen = graphicsConfig.createCompatibleImage(width,
2184: height, Transparency.OPAQUE);
2185: } catch (OutOfMemoryError e) {
2186: offscreen = null;
2187: offgraphics = null;
2188: }
2189: } else {
2190: try {
2191: offscreen = new BufferedImage(width, height,
2192: BufferedImage.TYPE_INT_RGB);
2193: } catch (OutOfMemoryError e) {
2194: offscreen = null;
2195: offgraphics = null;
2196: }
2197: }
2198: }
2199:
2200: /**
2201: * Utility method that initialises the offscreen graphics area
2202: * @param x
2203: * @param y
2204: * @param width
2205: * @param height
2206: * @param newOffscreenBuffer
2207: */
2208: protected void setupOffScreen(int x, int y, int width, int height,
2209: Rectangle2D newOffscreenBuffer) {
2210: offgraphics = offscreen.getGraphics();
2211: setOffscreenValid(false);
2212: offgraphics.setColor(getBackground());
2213: offgraphics.setPaintMode();
2214: offgraphics.fillRect(0, 0, width, height);
2215: offscreenBounds = newOffscreenBuffer;
2216: offscreenOffset = new Point2D.Double(x, y);
2217: }
2218:
2219: /**
2220: * @return the offscreen
2221: */
2222: public Image getOffscreen() {
2223: return offscreen;
2224: }
2225:
2226: /**
2227: * Utility method to draw the off screen buffer
2228: *
2229: * @param dx1
2230: * the <i>x</i> coordinate of the first corner of the
2231: * destination rectangle.
2232: * @param dy1
2233: * the <i>y</i> coordinate of the first corner of the
2234: * destination rectangle.
2235: * @param dx2
2236: * the <i>x</i> coordinate of the second corner of the
2237: * destination rectangle.
2238: * @param dy2
2239: * the <i>y</i> coordinate of the second corner of the
2240: * destination rectangle.
2241: * @param sx1
2242: * the <i>x</i> coordinate of the first corner of the source
2243: * rectangle.
2244: * @param sy1
2245: * the <i>y</i> coordinate of the first corner of the source
2246: * rectangle.
2247: * @param sx2
2248: * the <i>x</i> coordinate of the second corner of the source
2249: * rectangle.
2250: * @param sy2
2251: * the <i>y</i> coordinate of the second corner of the source
2252: * rectangle.
2253: * @return <code>true</code> if the current output representation is
2254: * complete; <code>false</code> otherwise.
2255: */
2256: public boolean drawImage(int dx1, int dy1, int dx2, int dy2,
2257: int sx1, int sy1, int sx2, int sy2) {
2258: getOffgraphics();
2259: return getGraphics().drawImage(offscreen, (int) sx1, (int) sy1,
2260: (int) sx2, (int) sy2, (int) sx1, (int) sy1, (int) sx2,
2261: (int) sy2, this );
2262: }
2263:
2264: public boolean drawImage(Graphics g) {
2265: Rectangle rect = getBounds();
2266: return getGraphics()
2267: .drawImage(offscreen, rect.x, rect.y,
2268: rect.x + rect.width, rect.y + rect.height,
2269: rect.x, rect.y, rect.x + rect.width,
2270: rect.y + rect.height, this );
2271: }
2272:
2273: /**
2274: * Returns the background image.
2275: *
2276: * @return Returns the backgroundImage.
2277: */
2278: public ImageIcon getBackgroundImage() {
2279: return backgroundImage;
2280: }
2281:
2282: /**
2283: * Sets the background image. Fires a property change event for
2284: * {@link #PROPERTY_BACKGROUNDIMAGE}.
2285: *
2286: * @param backgroundImage
2287: * The backgroundImage to set.
2288: */
2289: public void setBackgroundImage(ImageIcon backgroundImage) {
2290: ImageIcon oldValue = this .backgroundImage;
2291: this .backgroundImage = backgroundImage;
2292: firePropertyChange(PROPERTY_BACKGROUNDIMAGE, oldValue,
2293: backgroundImage);
2294: }
2295:
2296: /**
2297: * @return the backgroundScaled
2298: */
2299: public boolean isBackgroundScaled() {
2300: return backgroundScaled;
2301: }
2302:
2303: /**
2304: * @return the offscreenValid
2305: */
2306: public boolean isOffscreenValid() {
2307: return offscreenValid;
2308: }
2309:
2310: /**
2311: * @param offscreenValid
2312: * the offscreenValid to set
2313: */
2314: public void setOffscreenValid(boolean offscreenValid) {
2315: this .offscreenValid = offscreenValid;
2316: }
2317:
2318: /**
2319: * @return the offscreenOffset
2320: */
2321: public Point2D getOffscreenOffset() {
2322: return offscreenOffset;
2323: }
2324:
2325: /**
2326: * @param offscreenOffset the offscreenOffset to set
2327: */
2328: public void setOffscreenOffset(Point2D offscreenOffset) {
2329: this .offscreenOffset = offscreenOffset;
2330: }
2331:
2332: /**
2333: * @return the volatileOffscreen
2334: */
2335: public boolean isVolatileOffscreen() {
2336: return volatileOffscreen;
2337: }
2338:
2339: /**
2340: * @param volatileOffscreen the volatileOffscreen to set
2341: */
2342: public void setVolatileOffscreen(boolean volatileOffscreen) {
2343: this .volatileOffscreen = volatileOffscreen;
2344: }
2345:
2346: /**
2347: * @param backgroundScaled
2348: * the backgroundScaled to set
2349: */
2350: public void setBackgroundScaled(boolean backgroundScaled) {
2351: this .backgroundScaled = backgroundScaled;
2352: }
2353:
2354: /**
2355: * @return the backgroundComponent
2356: */
2357: public Component getBackgroundComponent() {
2358: return backgroundComponent;
2359: }
2360:
2361: /**
2362: * @param backgroundComponent
2363: * the backgroundComponent to set
2364: */
2365: public void setBackgroundComponent(Component backgroundComponent) {
2366: this .backgroundComponent = backgroundComponent;
2367: }
2368:
2369: /**
2370: * Returns the <code>GraphModel</code> that is providing the data.
2371: *
2372: * @return the model that is providing the data
2373: */
2374: public GraphModel getModel() {
2375: return graphModel;
2376: }
2377:
2378: /**
2379: * Sets the <code>GraphModel</code> that will provide the data. Note:
2380: * Updates the current GraphLayoutCache's model using setModel if the
2381: * GraphLayoutCache points to a different model.
2382: * <p>
2383: * Fires a property change for the GRAPH_MODEL_PROPERTY.
2384: *
2385: * @param newModel
2386: * the <code>GraphModel</code> that is to provide the data
2387: */
2388: public void setModel(GraphModel newModel) {
2389: GraphModel oldModel = graphModel;
2390: graphModel = newModel;
2391: firePropertyChange(GRAPH_MODEL_PROPERTY, oldModel, graphModel);
2392: // FIX: Use Listener
2393: if (graphLayoutCache != null
2394: && graphLayoutCache.getModel() != graphModel)
2395: graphLayoutCache.setModel(graphModel);
2396: clearSelection();
2397: invalidate();
2398: }
2399:
2400: /**
2401: * Returns the <code>GraphLayoutCache</code> that is providing the
2402: * view-data.
2403: *
2404: * @return the view that is providing the view-data
2405: */
2406: public GraphLayoutCache getGraphLayoutCache() {
2407: return graphLayoutCache;
2408: }
2409:
2410: /**
2411: * Sets the <code>GraphLayoutCache</code> that will provide the view-data.
2412: * <p>
2413: * Note: Updates the graphs's model using using the model from the layout
2414: * cache.
2415: * <p>
2416: * Fires a property change for the GRAPH_LAYOUT_CACHE_PROPERTY.
2417: *
2418: * @param newLayoutCache
2419: * the <code>GraphLayoutCache</code> that is to provide the
2420: * view-data
2421: */
2422: public void setGraphLayoutCache(GraphLayoutCache newLayoutCache) {
2423: GraphLayoutCache oldLayoutCache = graphLayoutCache;
2424: graphLayoutCache = newLayoutCache;
2425: firePropertyChange(GRAPH_LAYOUT_CACHE_PROPERTY, oldLayoutCache,
2426: graphLayoutCache);
2427: if (graphLayoutCache != null
2428: && graphLayoutCache.getModel() != getModel())
2429: setModel(graphLayoutCache.getModel());
2430: invalidate();
2431: }
2432:
2433: /**
2434: * Returns the <code>MarqueeHandler</code> that will handle marquee
2435: * selection.
2436: */
2437: public BasicMarqueeHandler getMarqueeHandler() {
2438: return marquee;
2439: }
2440:
2441: /**
2442: * Sets the <code>MarqueeHandler</code> that will handle marquee
2443: * selection.
2444: *
2445: * @param newMarquee
2446: * the <code>BasicMarqueeHandler</code> that is to provide
2447: * marquee handling
2448: */
2449: public void setMarqueeHandler(BasicMarqueeHandler newMarquee) {
2450: BasicMarqueeHandler oldMarquee = marquee;
2451: marquee = newMarquee;
2452: firePropertyChange(MARQUEE_HANDLER_PROPERTY, oldMarquee,
2453: newMarquee);
2454: invalidate();
2455: }
2456:
2457: /**
2458: * Determines what happens when editing is interrupted by selecting another
2459: * cell in the graph, a change in the graph's data, or by some other means.
2460: * Setting this property to <code>true</code> causes the changes to be
2461: * automatically saved when editing is interrupted.
2462: * <p>
2463: * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
2464: *
2465: * @param newValue
2466: * true means that <code>stopCellEditing</code> is invoked when
2467: * editing is interruped, and data is saved; false means that
2468: * <code>cancelCellEditing</code> is invoked, and changes are
2469: * lost
2470: */
2471: public void setInvokesStopCellEditing(boolean newValue) {
2472: boolean oldValue = invokesStopCellEditing;
2473: invokesStopCellEditing = newValue;
2474: firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY,
2475: oldValue, newValue);
2476: }
2477:
2478: /**
2479: * Returns the indicator that tells what happens when editing is
2480: * interrupted.
2481: *
2482: * @return the indicator that tells what happens when editing is interrupted
2483: * @see #setInvokesStopCellEditing
2484: *
2485: */
2486: public boolean getInvokesStopCellEditing() {
2487: return invokesStopCellEditing;
2488: }
2489:
2490: /**
2491: * Returns <code>true</code> if the graph and the cell are editable. This
2492: * is invoked from the UI before editing begins to ensure that the given
2493: * cell can be edited.
2494: *
2495: * @return true if the specified cell is editable
2496: * @see #isEditable
2497: *
2498: */
2499: public boolean isCellEditable(Object cell) {
2500: if (cell != null) {
2501: CellView view = graphLayoutCache.getMapping(cell, false);
2502: if (view != null) {
2503: return isEditable()
2504: && GraphConstants.isEditable(view
2505: .getAllAttributes());
2506: }
2507: }
2508: return false;
2509: }
2510:
2511: /**
2512: * Overrides <code>JComponent</code>'s<code>getToolTipText</code>
2513: * method in order to allow the graph to create a tooltip for the topmost
2514: * cell under the mousepointer. This differs from JTree where the renderers
2515: * tooltip is used.
2516: * <p>
2517: * NOTE: For <code>JGraph</code> to properly display tooltips of its
2518: * renderers, <code>JGraph</code> must be a registered component with the
2519: * <code>ToolTipManager</code>. This can be done by invoking
2520: * <code>ToolTipManager.sharedInstance().registerComponent(graph)</code>.
2521: * This is not done automatically!
2522: *
2523: * @param e
2524: * the <code>MouseEvent</code> that initiated the
2525: * <code>ToolTip</code> display
2526: * @return a string containing the tooltip or <code>null</code> if
2527: * <code>event</code> is null
2528: */
2529: public String getToolTipText(MouseEvent e) {
2530: if (e != null) {
2531: Object cell = getFirstCellForLocation(e.getX(), e.getY());
2532: CellView view = getGraphLayoutCache().getMapping(cell,
2533: false);
2534: if (view != null) {
2535: Component c = view.getRendererComponent(this , false,
2536: false, false);
2537: if (c instanceof JComponent) {
2538: Rectangle2D rect = getCellBounds(cell);
2539: Point2D where = fromScreen(e.getPoint());
2540: // Pass the event to the renderer in graph coordinates;
2541: // the renderer is ignorant of screen scaling
2542: e = new MouseEvent(c, e.getID(), e.getWhen(), e
2543: .getModifiers(), (int) (where.getX() - rect
2544: .getX()),
2545: (int) (where.getY() - rect.getY()), e
2546: .getClickCount(), e
2547: .isPopupTrigger());
2548: return ((JComponent) c).getToolTipText(e);
2549: }
2550: }
2551: }
2552: return super .getToolTipText(e);
2553: }
2554:
2555: //
2556: // The following are convenience methods that get forwarded to the
2557: // current GraphSelectionModel.
2558: //
2559: /**
2560: * Sets the graph's selection model. When a <code>null</code> value is
2561: * specified an emtpy <code>selectionModel</code> is used, which does not
2562: * allow selections.
2563: *
2564: * @param selectionModel
2565: * the <code>GraphSelectionModel</code> to use, or
2566: * <code>null</code> to disable selections
2567: * @see GraphSelectionModel
2568: *
2569: */
2570: public void setSelectionModel(GraphSelectionModel selectionModel) {
2571: if (selectionModel == null)
2572: selectionModel = EmptySelectionModel.sharedInstance();
2573: GraphSelectionModel oldValue = this .selectionModel;
2574: // Remove Redirector From Old Selection Model
2575: if (this .selectionModel != null && selectionRedirector != null)
2576: this .selectionModel
2577: .removeGraphSelectionListener(selectionRedirector);
2578: this .selectionModel = selectionModel;
2579: // Add Redirector To New Selection Model
2580: if (selectionRedirector != null)
2581: this .selectionModel
2582: .addGraphSelectionListener(selectionRedirector);
2583: firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue,
2584: this .selectionModel);
2585: }
2586:
2587: /**
2588: * Returns the model for selections. This should always return a non-
2589: * <code>null</code> value. If you don't want to allow anything to be
2590: * selected set the selection model to <code>null</code>, which forces an
2591: * empty selection model to be used.
2592: *
2593: * @return the current selection model
2594: * @see #setSelectionModel
2595: *
2596: */
2597: public GraphSelectionModel getSelectionModel() {
2598: return selectionModel;
2599: }
2600:
2601: /**
2602: * Clears the selection.
2603: */
2604: public void clearSelection() {
2605: getSelectionModel().clearSelection();
2606: }
2607:
2608: /**
2609: * Returns true if the selection is currently empty.
2610: *
2611: * @return true if the selection is currently empty
2612: */
2613: public boolean isSelectionEmpty() {
2614: return getSelectionModel().isSelectionEmpty();
2615: }
2616:
2617: /**
2618: * Adds a listener for <code>GraphSelection</code> events.
2619: *
2620: * @param tsl
2621: * the <code>GraphSelectionListener</code> that will be
2622: * notified when a cell is selected or deselected (a "negative
2623: * selection")
2624: */
2625: public void addGraphSelectionListener(GraphSelectionListener tsl) {
2626: listenerList.add(GraphSelectionListener.class, tsl);
2627: if (listenerList.getListenerCount(GraphSelectionListener.class) != 0
2628: && selectionRedirector == null) {
2629: selectionRedirector = new GraphSelectionRedirector();
2630: selectionModel
2631: .addGraphSelectionListener(selectionRedirector);
2632: }
2633: }
2634:
2635: /**
2636: * Removes a <code>GraphSelection</code> listener.
2637: *
2638: * @param tsl
2639: * the <code>GraphSelectionListener</code> to remove
2640: */
2641: public void removeGraphSelectionListener(GraphSelectionListener tsl) {
2642: listenerList.remove(GraphSelectionListener.class, tsl);
2643: if (listenerList.getListenerCount(GraphSelectionListener.class) == 0
2644: && selectionRedirector != null) {
2645: selectionModel
2646: .removeGraphSelectionListener(selectionRedirector);
2647: selectionRedirector = null;
2648: }
2649: }
2650:
2651: /**
2652: * Notifies all listeners that have registered interest for notification on
2653: * this event type. The event instance is lazily created using the
2654: * parameters passed into the fire method.
2655: *
2656: * @param e
2657: * the <code>GraphSelectionEvent</code> generated by the
2658: * <code>GraphSelectionModel</code> when a cell is selected or
2659: * deselected
2660: * @see javax.swing.event.EventListenerList
2661: *
2662: */
2663: protected void fireValueChanged(GraphSelectionEvent e) {
2664: // Guaranteed to return a non-null array
2665: Object[] listeners = listenerList.getListenerList();
2666: // Process the listeners last to first, notifying
2667: // those that are interested in this event
2668: for (int i = listeners.length - 2; i >= 0; i -= 2) {
2669: if (listeners[i] == GraphSelectionListener.class) {
2670: ((GraphSelectionListener) listeners[i + 1])
2671: .valueChanged(e);
2672: }
2673: }
2674: }
2675:
2676: /**
2677: * Selects the specified cell.
2678: *
2679: * @param cell
2680: * the <code>Object</code> specifying the cell to select
2681: */
2682: public void setSelectionCell(Object cell) {
2683: getSelectionModel().setSelectionCell(cell);
2684: }
2685:
2686: /**
2687: * Selects the specified cells.
2688: *
2689: * @param cells
2690: * an array of objects that specifies the cells to select
2691: */
2692: public void setSelectionCells(Object[] cells) {
2693: getSelectionModel().setSelectionCells(cells);
2694: }
2695:
2696: /**
2697: * Adds the cell identified by the specified <code>Object</code> to the
2698: * current selection.
2699: *
2700: * @param cell
2701: * the cell to be added to the selection
2702: */
2703: public void addSelectionCell(Object cell) {
2704: getSelectionModel().addSelectionCell(cell);
2705: }
2706:
2707: /**
2708: * Adds each cell in the array of cells to the current selection.
2709: *
2710: * @param cells
2711: * an array of objects that specifies the cells to add
2712: */
2713: public void addSelectionCells(Object[] cells) {
2714: getSelectionModel().addSelectionCells(cells);
2715: }
2716:
2717: /**
2718: * Removes the cell identified by the specified Object from the current
2719: * selection.
2720: *
2721: * @param cell
2722: * the cell to be removed from the selection
2723: */
2724: public void removeSelectionCell(Object cell) {
2725: getSelectionModel().removeSelectionCell(cell);
2726: }
2727:
2728: /**
2729: * Returns the first selected cell.
2730: *
2731: * @return the <code>Object</code> for the first selected cell, or
2732: * <code>null</code> if nothing is currently selected
2733: */
2734: public Object getSelectionCell() {
2735: return getSelectionModel().getSelectionCell();
2736: }
2737:
2738: /**
2739: * Returns all selected cells.
2740: *
2741: * @return an array of objects representing the selected cells, or
2742: * <code>null</code> if nothing is currently selected
2743: */
2744: public Object[] getSelectionCells() {
2745: return getSelectionModel().getSelectionCells();
2746: }
2747:
2748: /**
2749: * Returns all selected cells in <code>cells</code>.
2750: */
2751: public Object[] getSelectionCells(Object[] cells) {
2752: if (cells != null) {
2753: List selected = new ArrayList(cells.length);
2754: for (int i = 0; i < cells.length; i++) {
2755: if (isCellSelected(cells[i]))
2756: selected.add(cells[i]);
2757: }
2758: return selected.toArray();
2759: }
2760: return null;
2761: }
2762:
2763: /**
2764: * Returns the selection cell at the specified location.
2765: *
2766: * @return Returns the selection cell for <code>pt</code>.
2767: */
2768: public Object getSelectionCellAt(Point2D pt) {
2769: pt = fromScreen((Point2D) pt.clone());
2770: Object[] cells = getSelectionCells();
2771: if (cells != null) {
2772: for (int i = 0; i < cells.length; i++)
2773: if (getCellBounds(cells[i]).contains(pt.getX(),
2774: pt.getY()))
2775: return cells[i];
2776: }
2777: return null;
2778: }
2779:
2780: /**
2781: * Returns the number of cells selected.
2782: *
2783: * @return the number of cells selected
2784: */
2785: public int getSelectionCount() {
2786: return getSelectionModel().getSelectionCount();
2787: }
2788:
2789: /**
2790: * Returns true if the cell is currently selected.
2791: *
2792: * @param cell
2793: * an object identifying a cell
2794: * @return true if the cell is selected
2795: */
2796: public boolean isCellSelected(Object cell) {
2797: return getSelectionModel().isCellSelected(cell);
2798: }
2799:
2800: /**
2801: * Scrolls to the specified cell. Only works when this <code>JGraph</code>
2802: * is contained in a <code>JScrollPane</code>.
2803: *
2804: * @param cell
2805: * the object identifying the cell to bring into view
2806: */
2807: public void scrollCellToVisible(Object cell) {
2808: Rectangle2D bounds = getCellBounds(cell);
2809: if (bounds != null) {
2810: Rectangle2D b2 = toScreen((Rectangle2D) bounds.clone());
2811: scrollRectToVisible(new Rectangle((int) b2.getX(), (int) b2
2812: .getY(), (int) b2.getWidth(), (int) b2.getHeight()));
2813: }
2814: }
2815:
2816: /**
2817: * Makes sure the specified point is visible.
2818: *
2819: * @param p
2820: * the point that should be visible
2821: */
2822: public void scrollPointToVisible(Point2D p) {
2823: if (p != null)
2824: scrollRectToVisible(new Rectangle((int) p.getX(), (int) p
2825: .getY(), 1, 1));
2826: }
2827:
2828: /**
2829: * Returns true if the graph is being edited. The item that is being edited
2830: * can be obtained using <code>getEditingCell</code>.
2831: *
2832: * @return true if the user is currently editing a cell
2833: * @see #getSelectionCell
2834: *
2835: */
2836: public boolean isEditing() {
2837: GraphUI graph = getUI();
2838: if (graph != null)
2839: return graph.isEditing(this );
2840: return false;
2841: }
2842:
2843: /**
2844: * Ends the current editing session. (The
2845: * <code>DefaultGraphCellEditor</code> object saves any edits that are
2846: * currently in progress on a cell. Other implementations may operate
2847: * differently.) Has no effect if the tree isn't being edited. <blockquote>
2848: * <b>Note: </b> <br>
2849: * To make edit-saves automatic whenever the user changes their position in
2850: * the graph, use {@link #setInvokesStopCellEditing}. </blockquote>
2851: *
2852: * @return true if editing was in progress and is now stopped, false if
2853: * editing was not in progress
2854: */
2855: public boolean stopEditing() {
2856: GraphUI graph = getUI();
2857: if (graph != null)
2858: return graph.stopEditing(this );
2859: return false;
2860: }
2861:
2862: /**
2863: * Cancels the current editing session. Has no effect if the graph isn't
2864: * being edited.
2865: */
2866: public void cancelEditing() {
2867: GraphUI graph = getUI();
2868: if (graph != null)
2869: graph.cancelEditing(this );
2870: }
2871:
2872: /**
2873: * Selects the specified cell and initiates editing. The edit-attempt fails
2874: * if the <code>CellEditor</code> does not allow editing for the specified
2875: * item.
2876: */
2877: public void startEditingAtCell(Object cell) {
2878: GraphUI graph = getUI();
2879: if (graph != null)
2880: graph.startEditingAtCell(this , cell);
2881: }
2882:
2883: /**
2884: * Returns the cell that is currently being edited.
2885: *
2886: * @return the cell being edited
2887: */
2888: public Object getEditingCell() {
2889: GraphUI graph = getUI();
2890: if (graph != null)
2891: return graph.getEditingCell(this );
2892: return null;
2893: }
2894:
2895: /**
2896: * Messaged when the graph has changed enough that we need to resize the
2897: * bounds, but not enough that we need to remove the cells (e.g cells were
2898: * inserted into the graph). You should never have to invoke this, the UI
2899: * will invoke this as it needs to. (Note: This is invoked by GraphUI, eg.
2900: * after moving.)
2901: */
2902: public void graphDidChange() {
2903: revalidate();
2904: repaint();
2905: }
2906:
2907: /**
2908: * You should not call this method directly on a JGraph if you are using
2909: * double buffering.
2910: *
2911: * @see javax.swing.JComponent#repaint(long, int, int, int, int)
2912: */
2913: public void repaint(long tm, int x, int y, int width, int height) {
2914: super .repaint(tm, x, y, width, height);
2915: }
2916:
2917: /*
2918: * (non-Javadoc)
2919: *
2920: * @see javax.swing.JComponent#repaint(java.awt.Rectangle)
2921: */
2922: public void repaint(Rectangle r) {
2923: offscreenValid = false;
2924: super .repaint(r);
2925: }
2926:
2927: /*
2928: * (non-Javadoc)
2929: *
2930: * @see java.awt.Component#repaint()
2931: */
2932: public void repaint() {
2933: offscreenValid = false;
2934: super .repaint();
2935: }
2936:
2937: /*
2938: * (non-Javadoc)
2939: *
2940: * @see java.awt.Component#repaint(int, int, int, int)
2941: */
2942: public void repaint(int x, int y, int width, int height) {
2943: offscreenValid = false;
2944: super .repaint(x, y, width, height);
2945: }
2946:
2947: /*
2948: * (non-Javadoc)
2949: *
2950: * @see java.awt.Component#repaint(long)
2951: */
2952: public void repaint(long tm) {
2953: offscreenValid = false;
2954: super .repaint(tm);
2955: }
2956:
2957: // /* (non-Javadoc)
2958: // * @see javax.swing.JComponent#isOptimizedDrawingEnabled()
2959: // */
2960: // @Override
2961: // public boolean isOptimizedDrawingEnabled() {
2962: // return true;
2963: // }
2964:
2965: /**
2966: * Returns a {@link BufferedImage} for the graph using inset as an empty
2967: * border around the cells of the graph. If bg is null then a transparent
2968: * background is applied to the image, else the background is filled with
2969: * the bg color. Therefore, one should only use a null background if the
2970: * fileformat support transparency, eg. GIF and PNG. For JPG, you can use
2971: * <code>Color.WHITE</code> for example.
2972: *
2973: * @return Returns an image of the graph.
2974: */
2975: public BufferedImage getImage(Color bg, int inset) {
2976: // TODO, this method could just use the offscreen if available
2977: Object[] cells = getRoots();
2978: Rectangle2D bounds = getCellBounds(cells);
2979: if (bounds != null) {
2980: toScreen(bounds);
2981: GraphicsConfiguration graphicsConfig = getGraphicsConfiguration();
2982: BufferedImage img = null;
2983: if (graphicsConfig != null) {
2984: img = getGraphicsConfiguration().createCompatibleImage(
2985: (int) bounds.getWidth() + 2 * inset,
2986: (int) bounds.getHeight() + 2 * inset,
2987: (bg != null) ? Transparency.OPAQUE
2988: : Transparency.BITMASK);
2989: } else {
2990: img = new BufferedImage((int) bounds.getWidth() + 2
2991: * inset, (int) bounds.getHeight() + 2 * inset,
2992: (bg != null) ? BufferedImage.TYPE_INT_RGB
2993: : BufferedImage.TYPE_INT_ARGB);
2994: }
2995:
2996: Graphics2D graphics = img.createGraphics();
2997: if (bg != null) {
2998: graphics.setColor(bg);
2999: graphics
3000: .fillRect(0, 0, img.getWidth(), img.getHeight());
3001: } else {
3002: graphics.setComposite(AlphaComposite.getInstance(
3003: AlphaComposite.CLEAR, 0.0f));
3004: graphics
3005: .fillRect(0, 0, img.getWidth(), img.getHeight());
3006: graphics.setComposite(AlphaComposite.SrcOver);
3007: }
3008: graphics.translate((int) (-bounds.getX() + inset),
3009: (int) (-bounds.getY() + inset));
3010: print(graphics);
3011: graphics.dispose();
3012: return img;
3013: }
3014: return null;
3015: }
3016:
3017: /**
3018: * Serialization support.
3019: */
3020: private void writeObject(ObjectOutputStream s) throws IOException {
3021: Vector values = new Vector();
3022: s.defaultWriteObject();
3023: // Save the cellEditor, if its Serializable.
3024: if (graphModel instanceof Serializable) {
3025: values.addElement("graphModel");
3026: values.addElement(graphModel);
3027: }
3028: // Save the graphModel, if its Serializable.
3029: values.addElement("graphLayoutCache");
3030: values.addElement(graphLayoutCache);
3031:
3032: // Save the selectionModel, if its Serializable.
3033: if (selectionModel instanceof Serializable) {
3034: values.addElement("selectionModel");
3035: values.addElement(selectionModel);
3036: }
3037: // Save the marquee handler, if its Serializable.
3038: if (marquee instanceof Serializable) {
3039: values.addElement("marquee");
3040: values.addElement(marquee);
3041: }
3042: s.writeObject(values);
3043: if (getUIClassID().equals(uiClassID)) {
3044: /*
3045: * byte count = JComponent.getWriteObjCounter(this);
3046: * JComponent.setWriteObjCounter(this, --count);
3047: */
3048: if (/* count == 0 && */
3049: ui != null) {
3050: ui.installUI(this );
3051: }
3052: }
3053: }
3054:
3055: /**
3056: * Serialization support.
3057: */
3058: private void readObject(ObjectInputStream s) throws IOException,
3059: ClassNotFoundException {
3060: s.defaultReadObject();
3061: Vector values = (Vector) s.readObject();
3062: int indexCounter = 0;
3063: int maxCounter = values.size();
3064: if (indexCounter < maxCounter
3065: && values.elementAt(indexCounter).equals("graphModel")) {
3066: graphModel = (GraphModel) values.elementAt(++indexCounter);
3067: indexCounter++;
3068: }
3069: if (indexCounter < maxCounter
3070: && values.elementAt(indexCounter).equals(
3071: "graphLayoutCache")) {
3072: graphLayoutCache = (GraphLayoutCache) values
3073: .elementAt(++indexCounter);
3074: indexCounter++;
3075: }
3076: if (indexCounter < maxCounter
3077: && values.elementAt(indexCounter).equals(
3078: "selectionModel")) {
3079: selectionModel = (GraphSelectionModel) values
3080: .elementAt(++indexCounter);
3081: indexCounter++;
3082: }
3083: if (indexCounter < maxCounter
3084: && values.elementAt(indexCounter).equals("marquee")) {
3085: marquee = (BasicMarqueeHandler) values
3086: .elementAt(++indexCounter);
3087: indexCounter++;
3088: }
3089: // Reinstall the redirector.
3090: if (listenerList.getListenerCount(GraphSelectionListener.class) != 0) {
3091: selectionRedirector = new GraphSelectionRedirector();
3092: selectionModel
3093: .addGraphSelectionListener(selectionRedirector);
3094: }
3095: }
3096:
3097: /**
3098: * <code>EmptySelectionModel</code> is a <code>GraphSelectionModel</code>
3099: * that does not allow anything to be selected.
3100: * <p>
3101: * <strong>Warning: </strong> Serialized objects of this class will not be
3102: * compatible with future Swing releases. The current serialization support
3103: * is appropriate for short term storage or RMI between applications running
3104: * the same version of Swing. A future release of Swing will provide support
3105: * for long term persistence.
3106: */
3107: public static class EmptySelectionModel extends
3108: DefaultGraphSelectionModel {
3109:
3110: /** Unique shared instance. */
3111: protected static final EmptySelectionModel sharedInstance = new EmptySelectionModel();
3112:
3113: /**
3114: * A <code>null</code> implementation that constructs an
3115: * EmptySelectionModel.
3116: */
3117: public EmptySelectionModel() {
3118: super (null);
3119: }
3120:
3121: /** Returns a shared instance of an empty selection model. */
3122: static public EmptySelectionModel sharedInstance() {
3123: return sharedInstance;
3124: }
3125:
3126: /** A <code>null</code> implementation that selects nothing. */
3127: public void setSelectionCells(Object[] cells) {
3128: }
3129:
3130: /** A <code>null</code> implementation that adds nothing. */
3131: public void addSelectionCells(Object[] cells) {
3132: }
3133:
3134: /** A <code>null</code> implementation that removes nothing. */
3135: public void removeSelectionCells(Object[] cells) {
3136: }
3137: }
3138:
3139: /**
3140: * Handles creating a new <code>GraphSelectionEvent</code> with the
3141: * <code>JGraph</code> as the source and passing it off to all the
3142: * listeners.
3143: * <p>
3144: * <strong>Warning: </strong> Serialized objects of this class will not be
3145: * compatible with future Swing releases. The current serialization support
3146: * is appropriate for short term storage or RMI between applications running
3147: * the same version of Swing. A future release of Swing will provide support
3148: * for long term persistence.
3149: */
3150: protected class GraphSelectionRedirector implements Serializable,
3151: GraphSelectionListener {
3152:
3153: /**
3154: * Invoked by the <code>GraphSelectionModel</code> when the selection
3155: * changes.
3156: *
3157: * @param e
3158: * the <code>GraphSelectionEvent</code> generated by the
3159: * <code>GraphSelectionModel</code>
3160: */
3161: public void valueChanged(GraphSelectionEvent e) {
3162: GraphSelectionEvent newE;
3163: newE = (GraphSelectionEvent) e.cloneWithSource(JGraph.this );
3164: fireValueChanged(newE);
3165: }
3166: } // End of class JGraph.GraphSelectionRedirector
3167:
3168: //
3169: // Scrollable interface
3170: //
3171: /**
3172: * Returns the preferred display size of a <code>JGraph</code>. The
3173: * height is determined from <code>getPreferredWidth</code>.
3174: *
3175: * @return the graph's preferred size
3176: */
3177: public Dimension getPreferredScrollableViewportSize() {
3178: return getPreferredSize();
3179: }
3180:
3181: /**
3182: * Returns the amount to increment when scrolling. The amount is 4.
3183: *
3184: * @param visibleRect
3185: * the view area visible within the viewport
3186: * @param orientation
3187: * either <code>SwingConstants.VERTICAL</code> or
3188: * <code>SwingConstants.HORIZONTAL</code>
3189: * @param direction
3190: * less than zero to scroll up/left, greater than zero for
3191: * down/right
3192: * @return the "unit" increment for scrolling in the specified direction
3193: * @see javax.swing.JScrollBar#setUnitIncrement(int)
3194: *
3195: */
3196: public int getScrollableUnitIncrement(Rectangle visibleRect,
3197: int orientation, int direction) {
3198: if (orientation == SwingConstants.VERTICAL) {
3199: return 2;
3200: }
3201: return 4;
3202: }
3203:
3204: /**
3205: * Returns the amount for a block increment, which is the height or width of
3206: * <code>visibleRect</code>, based on <code>orientation</code>.
3207: *
3208: * @param visibleRect
3209: * the view area visible within the viewport
3210: * @param orientation
3211: * either <code>SwingConstants.VERTICAL</code> or
3212: * <code>SwingConstants.HORIZONTAL</code>
3213: * @param direction
3214: * less than zero to scroll up/left, greater than zero for
3215: * down/right.
3216: * @return the "block" increment for scrolling in the specified direction
3217: * @see javax.swing.JScrollBar#setBlockIncrement(int)
3218: *
3219: */
3220: public int getScrollableBlockIncrement(Rectangle visibleRect,
3221: int orientation, int direction) {
3222: return (orientation == SwingConstants.VERTICAL) ? visibleRect.height
3223: : visibleRect.width;
3224: }
3225:
3226: /**
3227: * Returns false to indicate that the width of the viewport does not
3228: * determine the width of the graph, unless the preferred width of the graph
3229: * is smaller than the viewports width. In other words: ensure that the
3230: * graph is never smaller than its viewport.
3231: *
3232: * @return false
3233: * @see Scrollable#getScrollableTracksViewportWidth
3234: *
3235: */
3236: public boolean getScrollableTracksViewportWidth() {
3237: if (getParent() instanceof JViewport) {
3238: return (((JViewport) getParent()).getWidth() > getPreferredSize().width);
3239: }
3240: return false;
3241: }
3242:
3243: /**
3244: * Returns false to indicate that the height of the viewport does not
3245: * determine the height of the graph, unless the preferred height of the
3246: * graph is smaller than the viewports height. In other words: ensure that
3247: * the graph is never smaller than its viewport.
3248: *
3249: * @return false
3250: * @see Scrollable#getScrollableTracksViewportHeight
3251: *
3252: */
3253: public boolean getScrollableTracksViewportHeight() {
3254: if (getParent() instanceof JViewport) {
3255: return (((JViewport) getParent()).getHeight() > getPreferredSize().height);
3256: }
3257: return false;
3258: }
3259:
3260: /**
3261: * Returns a string representation of this <code>JGraph</code>. This
3262: * method is intended to be used only for debugging purposes, and the
3263: * content and format of the returned string may vary between
3264: * implementations. The returned string may be empty but may not be
3265: * <code>null</code>.
3266: *
3267: * @return a string representation of this <code>JGraph</code>.
3268: */
3269: protected String paramString() {
3270: String editableString = (editable ? "true" : "false");
3271: String invokesStopCellEditingString = (invokesStopCellEditing ? "true"
3272: : "false");
3273: return super .paramString() + ",editable=" + editableString
3274: + ",invokesStopCellEditing="
3275: + invokesStopCellEditingString;
3276: }
3277:
3278: public static void main(String[] args) {
3279: System.out.println(VERSION);
3280: }
3281: }
|