0001: /*
0002: * @(#)EdgeView.java 1.0 03-JUL-04
0003: *
0004: * Copyright (c) 2001-2004 Gaudenz Alder
0005: *
0006: */
0007: package org.jgraph.graph;
0008:
0009: import java.awt.Cursor;
0010: import java.awt.Dimension;
0011: import java.awt.Graphics;
0012: import java.awt.Graphics2D;
0013: import java.awt.Point;
0014: import java.awt.Rectangle;
0015: import java.awt.Shape;
0016: import java.awt.event.MouseEvent;
0017: import java.awt.geom.AffineTransform;
0018: import java.awt.geom.GeneralPath;
0019: import java.awt.geom.Line2D;
0020: import java.awt.geom.Point2D;
0021: import java.awt.geom.Rectangle2D;
0022: import java.io.Serializable;
0023: import java.util.ArrayList;
0024: import java.util.List;
0025: import java.util.Map;
0026:
0027: import javax.swing.SwingUtilities;
0028:
0029: import org.jgraph.JGraph;
0030: import org.jgraph.plaf.GraphUI;
0031: import org.jgraph.plaf.basic.BasicGraphUI;
0032:
0033: /**
0034: * The default implementation of an edge view. The getEdgeRenderer method
0035: * assumes a renderer of type EdgeRenderer. If you provide a custom renderer to
0036: * a subclass, you must also override the methods that call this method, namely:
0037: * getShape, getLabelBounds, getExtraLabelBounds, intersects and getBounds.
0038: *
0039: * @version 1.0 1/1/02
0040: * @author Gaudenz Alder
0041: */
0042:
0043: public class EdgeView extends AbstractCellView {
0044:
0045: /** Renderer for the class. */
0046: public static transient EdgeRenderer renderer = new EdgeRenderer();
0047:
0048: /** List of points of the edge. May contain ports. */
0049: protected List points;
0050:
0051: /** Cached source and target portview of the edge. */
0052: protected CellView source, target;
0053:
0054: protected CellView sourceParentView, targetParentView;
0055:
0056: /** Cached label position of the edge. */
0057: protected Point2D labelPosition;
0058:
0059: protected Point2D[] extraLabelPositions;
0060:
0061: protected transient Point2D labelVector = null;
0062:
0063: /** Drawing attributes that are created on the fly */
0064: public transient Shape beginShape, endShape, lineShape;
0065:
0066: /** Shared-path tune-up. */
0067: public transient GeneralPath sharedPath = null;
0068:
0069: protected transient Rectangle2D cachedBounds = null;
0070:
0071: /**
0072: * Constructs an empty edge view.
0073: */
0074: public EdgeView() {
0075: super ();
0076: }
0077:
0078: /**
0079: * Constructs an edge view for the specified model object.
0080: *
0081: * @param cell
0082: * reference to the model object
0083: */
0084: public EdgeView(Object cell) {
0085: super (cell);
0086: }
0087:
0088: //
0089: // Data Source
0090: //
0091:
0092: /**
0093: * Overrides the parent method to udpate the cached points, source and
0094: * target port. If the source or target is removed, a point is inserted into
0095: * the array of points.
0096: */
0097: public void refresh(GraphLayoutCache cache, CellMapper mapper,
0098: boolean createDependentViews) {
0099: // Makes sure the manual control points are passed to
0100: // the router instead of the cached control points after
0101: // changes to the edge (normally manual point changes).
0102: points = null;
0103: super .refresh(cache, mapper, createDependentViews);
0104: // Re-sync source- and targetportviews
0105: GraphModel model = cache.getModel();
0106: Object modelSource = model.getSource(cell);
0107: Object modelTarget = model.getTarget(cell);
0108: setSource(mapper.getMapping(modelSource, createDependentViews));
0109: setTarget(mapper.getMapping(modelTarget, createDependentViews));
0110: if (modelSource != null && getSource() == null)
0111: sourceParentView = getVisibleParent(model, mapper,
0112: modelSource);
0113: else
0114: sourceParentView = null;
0115: if (modelTarget != null && getTarget() == null)
0116: targetParentView = getVisibleParent(model, mapper,
0117: modelTarget);
0118: else
0119: targetParentView = null;
0120: }
0121:
0122: protected CellView getVisibleParent(GraphModel model,
0123: CellMapper mapper, Object port) {
0124: CellView view = null;
0125: do {
0126: view = mapper.getMapping(port, false);
0127: port = model.getParent(port);
0128: } while (view == null && port != null);
0129: return view;
0130: }
0131:
0132: /**
0133: * Update attributes and recurse children.
0134: */
0135: public void update(GraphLayoutCache cache) {
0136: super .update(cache);
0137: // Save the reference to the points so they can be changed
0138: // in-place by use of setPoint, setSource, setTarget methods.
0139: List controlPoints = GraphConstants.getPoints(allAttributes);
0140: if (controlPoints == null) {
0141: controlPoints = new ArrayList(4);
0142: controlPoints.add(allAttributes.createPoint(10, 10));
0143: controlPoints.add(allAttributes.createPoint(20, 20));
0144: GraphConstants.setPoints(allAttributes, controlPoints);
0145: }
0146:
0147: // Uses the manual control points while the edge is being routed.
0148: // Otherwise uses the cached points (eg. for preview).
0149: if (points == null)
0150: points = controlPoints;
0151:
0152: Edge.Routing routing = GraphConstants.getRouting(allAttributes);
0153: List routedPoints = null;
0154: // Passes the current cached points to the router
0155: if (routing != null)
0156: routedPoints = routing.route(cache, this );
0157:
0158: // Shadows the manual control points with the
0159: // routed control points
0160: points = (routedPoints != null && !routedPoints.isEmpty()) ? routedPoints
0161: : controlPoints;
0162:
0163: // Overrides manual point locations with the real port views
0164: if (points == controlPoints) {
0165: if (source != null)
0166: setSource(source);
0167: if (target != null)
0168: setTarget(target);
0169: }
0170:
0171: // Checks and caches label positions
0172: checkDefaultLabelPosition();
0173: Point2D[] positions = GraphConstants
0174: .getExtraLabelPositions(allAttributes);
0175: if (positions != null) {
0176: extraLabelPositions = new Point2D[positions.length];
0177: for (int i = 0; i < positions.length; i++)
0178: extraLabelPositions[i] = positions[i];
0179: } else
0180: extraLabelPositions = null;
0181:
0182: // Clear cached shapes
0183: beginShape = null;
0184: endShape = null;
0185: lineShape = null;
0186: invalidate();
0187: }
0188:
0189: /**
0190: * Hook for subclassers to avoid default label positions.
0191: */
0192: protected void checkDefaultLabelPosition() {
0193: labelPosition = GraphConstants.getLabelPosition(allAttributes);
0194: String label = String.valueOf(getCell());
0195: if (labelPosition == null && label != null
0196: && label.length() > 0) {
0197: int center = GraphConstants.PERMILLE / 2;
0198: labelPosition = new Point(center, 0);
0199: GraphConstants.setLabelPosition(allAttributes,
0200: labelPosition);
0201: }
0202: }
0203:
0204: /**
0205: * Resets the cached values of the edge view
0206: */
0207: protected void invalidate() {
0208: labelVector = null;
0209: sharedPath = null;
0210: cachedBounds = null;
0211: }
0212:
0213: /**
0214: * Returns the shape of the view according to the last rendering state
0215: */
0216: public Shape getShape() {
0217: if (sharedPath != null)
0218: return sharedPath;
0219: else {
0220: return sharedPath = (GeneralPath) getEdgeRenderer()
0221: .createShape();
0222: }
0223: }
0224:
0225: //
0226: // View Methods
0227: //
0228:
0229: /**
0230: * Returns true if this view intersects the given rectangle.
0231: */
0232: public boolean intersects(JGraph graph, Rectangle2D rect) {
0233: boolean intersects = super .intersects(graph, rect);
0234: if (!isLeaf()) {
0235: return intersects;
0236: } else if (intersects) {
0237: Rectangle r = new Rectangle((int) rect.getX(), (int) rect
0238: .getY(), (int) rect.getWidth(), (int) rect
0239: .getHeight());
0240: return getEdgeRenderer().intersects(graph, this , r);
0241: }
0242: return false;
0243: }
0244:
0245: /**
0246: * Returns the location for this edgeview.
0247: */
0248: public Rectangle2D getBounds() {
0249: Rectangle2D rect = super .getBounds();
0250: if (rect == null) {
0251: if (cachedBounds == null) {
0252: cachedBounds = getEdgeRenderer().getBounds(this );
0253: }
0254: rect = cachedBounds;
0255: }
0256: return rect;
0257: }
0258:
0259: /**
0260: * Returns the local renderer. Do not access the renderer field directly.
0261: * Use this method instead. Note: This method is package private.
0262: */
0263: EdgeRenderer getEdgeRenderer() {
0264: return (EdgeRenderer) getRenderer();
0265: }
0266:
0267: /**
0268: * Returns a renderer for the class.
0269: */
0270: public CellViewRenderer getRenderer() {
0271: return renderer;
0272: }
0273:
0274: /**
0275: * Returns a cell handle for the view.
0276: */
0277: public CellHandle getHandle(GraphContext context) {
0278: return new EdgeHandle(this , context);
0279: }
0280:
0281: //
0282: // Cached Values
0283: //
0284:
0285: /**
0286: * Returns the CellView that represents the source of the edge.
0287: */
0288: public CellView getSource() {
0289: return source;
0290: }
0291:
0292: public CellView getSourceParentView() {
0293: return sourceParentView;
0294: }
0295:
0296: /**
0297: * Sets the <code>sourceView</code> of the edge.
0298: */
0299: public void setSource(CellView sourceView) {
0300: sourceParentView = null;
0301: source = sourceView;
0302: if (source != null)
0303: points.set(0, source);
0304: else
0305: points.set(0, getPoint(0));
0306: invalidate();
0307: }
0308:
0309: /**
0310: * Returns the CellView that represents the target of the edge.
0311: */
0312: public CellView getTarget() {
0313: return target;
0314: }
0315:
0316: public CellView getTargetParentView() {
0317: return targetParentView;
0318: }
0319:
0320: /**
0321: * Sets the <code>targetView</code> of the edge.
0322: */
0323: public void setTarget(CellView targetView) {
0324: target = targetView;
0325: targetParentView = null;
0326: int n = points.size() - 1;
0327: if (target != null)
0328: points.set(n, target);
0329: else
0330: points.set(n, getPoint(n));
0331: invalidate();
0332: }
0333:
0334: /**
0335: * Returns a point that describes the position of the label.
0336: */
0337: public Point2D getExtraLabelPosition(int index) {
0338: return extraLabelPositions[index];
0339: }
0340:
0341: /**
0342: * Returns a point that describes the position of the label.
0343: */
0344: public Point2D getLabelPosition() {
0345: return labelPosition;
0346: }
0347:
0348: /**
0349: * Sets the description of the label position.
0350: */
0351: public void setLabelPosition(Point2D pos) {
0352: labelPosition.setLocation(pos);
0353: invalidate();
0354: }
0355:
0356: /**
0357: * Sets the description of the label position.
0358: */
0359: public void setExtraLabelPosition(int index, Point2D pos) {
0360: extraLabelPositions[index].setLocation(pos);
0361: invalidate();
0362: }
0363:
0364: //
0365: // Points
0366: //
0367:
0368: /**
0369: * Returns true if the edge is a loop.
0370: */
0371: public boolean isLoop() {
0372: return (getSource() != null && getSource() == getTarget())
0373: || (sourceParentView != null && sourceParentView == targetParentView)
0374: || (sourceParentView != null && getTarget() != null && getTarget()
0375: .getParentView() == sourceParentView)
0376: || (targetParentView != null && getSource() != null && getSource()
0377: .getParentView() == targetParentView);
0378: }
0379:
0380: /**
0381: * Returns the points.
0382: *
0383: * @return List
0384: */
0385: public List getPoints() {
0386: return points;
0387: }
0388:
0389: /**
0390: * Returns the number of point for this edge.
0391: */
0392: public int getPointCount() {
0393: if (points != null) {
0394: return points.size();
0395: } else {
0396: return 0;
0397: }
0398: }
0399:
0400: /**
0401: * Returns the cached points for this edge.
0402: */
0403: public Point2D getPoint(int index) {
0404: Object obj = points.get(index);
0405: if (index == 0 && sourceParentView != null) {
0406: return sourceParentView.getPerimeterPoint(this ,
0407: getCenterPoint(sourceParentView),
0408: getNearestPoint(index == 0));
0409: } else if (index == getPointCount() - 1
0410: && targetParentView != null) {
0411: return targetParentView.getPerimeterPoint(this ,
0412: getCenterPoint(targetParentView),
0413: getNearestPoint(index == 0));
0414: } else if (obj instanceof PortView)
0415: // Port Location Seen From This Edge
0416: return ((PortView) obj).getLocation(this ,
0417: getNearestPoint(index == 0));
0418: else if (obj instanceof CellView) {
0419: // Should not happen
0420: Rectangle2D r = ((CellView) obj).getBounds();
0421: return new Point2D.Double(r.getX(), r.getY());
0422: } else if (obj instanceof Point2D)
0423: // Regular Point
0424: return (Point2D) obj;
0425: return null;
0426: }
0427:
0428: /**
0429: * Returns the nearest point wrt to the source or target. This method
0430: * returns the next or previous point or port in the points list, eg. if
0431: * source is true it returns the location of the point or port at index 1
0432: * without calling the getLocation method on any ports.<br>
0433: * Likewise, the method returns the location at index getPointCount()-2 if
0434: * source is false.
0435: */
0436: protected Point2D getNearestPoint(boolean source) {
0437: if (getPointCount() == 2) {
0438: if (source
0439: && target instanceof PortView
0440: && GraphConstants.getOffset(target
0441: .getAllAttributes()) != null) {
0442: return ((PortView) target).getLocation(this );
0443: }
0444: if (!source
0445: && this .source instanceof PortView
0446: && GraphConstants.getOffset(this .source
0447: .getAllAttributes()) != null) {
0448: return ((PortView) this .source).getLocation(this );
0449: }
0450: if (source && targetParentView != null
0451: && targetParentView.isLeaf())
0452: return getCenterPoint(targetParentView);
0453: else if (!source && sourceParentView != null
0454: && sourceParentView.isLeaf())
0455: return getCenterPoint(sourceParentView);
0456: }
0457: return getPointLocation((source) ? 1 : getPointCount() - 2);
0458: }
0459:
0460: /**
0461: * Returns the point of <code>edge</code> at <code>index</code>. Avoids
0462: * calling <code>getLocation</code> on any ports of <code>edge</code>.
0463: * <br>
0464: * This is used from within getPoint to pass the nearest point to the
0465: * portview to find it's location. This uses the center point of the parent
0466: * view to determine the port view's location to avoid infinite recursion.
0467: */
0468: protected Point2D getPointLocation(int index) {
0469: Object obj = points.get(index);
0470: if (obj instanceof Point2D)
0471: return (Point2D) obj;
0472: else if (obj instanceof PortView) {
0473: CellView vertex = ((CellView) obj).getParentView();
0474: if (vertex != null)
0475: return getCenterPoint(vertex);
0476: }
0477: return null;
0478: }
0479:
0480: /**
0481: * Sets the point at <code>index</code> to <code>p</code>.
0482: */
0483: public void setPoint(int index, Point2D p) {
0484: points.set(index, p);
0485: invalidate();
0486: }
0487:
0488: /**
0489: * Adds <code>p</code> at position <code>index</code>.
0490: */
0491: public void addPoint(int index, Point2D p) {
0492: points.add(index, p);
0493: invalidate();
0494: }
0495:
0496: /**
0497: * Removes the point at position <code>index</code>.
0498: */
0499: public void removePoint(int index) {
0500: points.remove(index);
0501: invalidate();
0502: }
0503:
0504: /**
0505: * Adds an extra label.
0506: */
0507: public void addExtraLabel(Point2D location, Object label) {
0508: Object[] extraLabels = GraphConstants
0509: .getExtraLabels(getAllAttributes());
0510: Point2D[] positions = GraphConstants
0511: .getExtraLabelPositions(getAllAttributes());
0512:
0513: // Inserts a new extra label
0514: if (extraLabels == null) {
0515: extraLabels = new Object[1];
0516: positions = new Point2D[1];
0517: } else {
0518: Object[] tmp = new Object[extraLabels.length + 1];
0519: System
0520: .arraycopy(extraLabels, 0, tmp, 0,
0521: extraLabels.length);
0522: extraLabels = tmp;
0523: Point2D[] pts = new Point2D[positions.length + 1];
0524: System.arraycopy(positions, 0, pts, 0, positions.length);
0525: positions = pts;
0526: }
0527: int newIndex = extraLabels.length - 1;
0528: extraLabels[newIndex] = label;
0529: positions[newIndex] = location;
0530: GraphConstants.setExtraLabels(getAllAttributes(), extraLabels);
0531: GraphConstants.setExtraLabelPositions(getAllAttributes(),
0532: positions);
0533: }
0534:
0535: /**
0536: * Removes the point at position <code>index</code>.
0537: */
0538: public void removeExtraLabel(int index) {
0539: Object[] labels = GraphConstants
0540: .getExtraLabels(getAllAttributes());
0541: Point2D[] pts = GraphConstants
0542: .getExtraLabelPositions(getAllAttributes());
0543: if (labels == null || labels.length > 1) {
0544: Object[] newLabels = new Object[labels.length - 1];
0545: Point2D[] newPts = new Point2D[pts.length - 1];
0546: System.arraycopy(labels, 0, newLabels, 0, index);
0547: if (index < newLabels.length)
0548: System.arraycopy(labels, index + 1, newLabels, index,
0549: newLabels.length - index);
0550: System.arraycopy(pts, 0, newPts, 0, index);
0551: if (index < newPts.length)
0552: System.arraycopy(pts, index + 1, newPts, index,
0553: newPts.length - index);
0554: GraphConstants
0555: .setExtraLabels(getAllAttributes(), newLabels);
0556: GraphConstants.setExtraLabelPositions(getAllAttributes(),
0557: newPts);
0558: } else {
0559: // TODO: Remove via REMOVEATTRIBUTES
0560: GraphConstants.setExtraLabels(getAllAttributes(),
0561: new Object[0]);
0562: GraphConstants.setExtraLabelPositions(getAllAttributes(),
0563: new Point2D[0]);
0564: }
0565: }
0566:
0567: /**
0568: * Hook to return the vector that is taken as the base vector to compute
0569: * relative label positions. Normally, the vector goes from the first to the
0570: * last point on the edge, unless these points are equal, in which case the
0571: * average distance of all points to the source point is used.
0572: */
0573: public Point2D getLabelVector() {
0574: if (labelVector == null) {
0575: Point2D p0 = getPoint(0);
0576: double dx = 0;
0577: double dy = 0;
0578: // Finds an average distance
0579: dx = 0;
0580: dy = 0;
0581: int n = getPointCount();
0582: if (isLoop()) {
0583: for (int i = 1; i < n; i++) {
0584: Point2D point = getPoint(i);
0585: dx += point.getX() - p0.getX();
0586: dy += point.getY() - p0.getY();
0587: }
0588: n /= 2;
0589: dx /= n;
0590: dy /= n;
0591: labelVector = new Point2D.Double(dx, dy);
0592: } else {
0593: Point2D point = getPoint(n - 1);
0594: dx += point.getX() - p0.getX();
0595: dy += point.getY() - p0.getY();
0596: labelVector = new Point2D.Double(dx, dy);
0597: }
0598: }
0599: return labelVector;
0600: }
0601:
0602: //
0603: // Routing
0604: //
0605:
0606: public static double getLength(CellView view) {
0607: double cost = 1;
0608: if (view instanceof EdgeView) {
0609: EdgeView edge = (EdgeView) view;
0610: Point2D last = null, current = null;
0611: for (int i = 0; i < edge.getPointCount(); i++) {
0612: current = edge.getPoint(i);
0613: if (last != null)
0614: cost += last.distance(current);
0615: last = current;
0616: }
0617: }
0618: return cost;
0619: }
0620:
0621: //
0622: // Handle
0623: //
0624:
0625: // This implementation uses the point instance to make the change. No index
0626: // is used for the current point because routing could change the index
0627: // during
0628: // the move operation.
0629: public static class EdgeHandle implements CellHandle, Serializable {
0630:
0631: protected JGraph graph;
0632:
0633: /* Pointer to the edge and its clone. */
0634: protected EdgeView edge, orig;
0635:
0636: /*
0637: * Boolean indicating whether the source, target or label is being
0638: * edited.
0639: */
0640: protected boolean label = false, source = false,
0641: target = false;
0642:
0643: /**
0644: * Holds the index of the current (editing) label or point.
0645: */
0646: protected int currentLabel = -1, currentIndex = -1;
0647:
0648: /* Pointer to the currently selected point. */
0649: protected Point2D currentPoint;
0650:
0651: /* Array of control points represented as rectangles. */
0652: protected transient Rectangle2D[] r;
0653:
0654: /* A control point for the label position. */
0655: protected transient Rectangle2D loc;
0656:
0657: protected transient Rectangle2D[] extraLabelLocations;
0658:
0659: protected boolean firstOverlayCall = true;
0660:
0661: protected boolean isEdgeConnectable = true;
0662:
0663: protected EdgeView relevantEdge = null;
0664:
0665: /**
0666: * True if the cell is being edited.
0667: */
0668: protected boolean editing = false;
0669:
0670: /**
0671: * Holds the initial location of the label.
0672: */
0673: protected Point2D initialLabelLocation = null;
0674:
0675: /**
0676: * Indicates whether the edge has been modified during the last mouse
0677: * pressed and dragged operations.
0678: */
0679: protected boolean edgeModified = false;
0680:
0681: public EdgeHandle(EdgeView edge, GraphContext ctx) {
0682: this .graph = ctx.getGraph();
0683: this .edge = edge;
0684: editing = graph.getEditingCell() == edge.getCell();
0685: loc = new Rectangle();
0686: Object[] labels = GraphConstants.getExtraLabels(edge
0687: .getAllAttributes());
0688: if (labels != null) {
0689: extraLabelLocations = new Rectangle[labels.length];
0690: for (int i = 0; i < extraLabelLocations.length; i++)
0691: extraLabelLocations[i] = new Rectangle();
0692: }
0693: orig = (EdgeView) graph.getGraphLayoutCache().getMapping(
0694: edge.getCell(), false);
0695: reloadPoints(orig);
0696: isEdgeConnectable = GraphConstants.isConnectable(edge
0697: .getAllAttributes());
0698: }
0699:
0700: protected void reloadPoints(EdgeView edge) {
0701: relevantEdge = edge;
0702: r = new Rectangle[edge.getPointCount()];
0703: for (int i = 0; i < r.length; i++)
0704: r[i] = new Rectangle();
0705: invalidate();
0706: }
0707:
0708: // Update and paint control points
0709: public void paint(Graphics g) {
0710: invalidate();
0711: if (!edge.isLeaf())
0712: return;
0713: for (int i = 0; i < r.length; i++) {
0714: if (isEdgeConnectable && !editing)
0715: g.setColor(graph.getHandleColor());
0716: else
0717: g.setColor(graph.getLockedHandleColor());
0718: g.fill3DRect((int) r[i].getX(), (int) r[i].getY(),
0719: (int) r[i].getWidth(), (int) r[i].getHeight(),
0720: true);
0721: CellView port = null;
0722: if (i == 0 && edge.getSource() != null)
0723: port = edge.getSource();
0724: else if (i == r.length - 1 && edge.getTarget() != null)
0725: port = edge.getTarget();
0726: if (port != null
0727: || (i == 0 && edge.getSourceParentView() != null)
0728: || (i == r.length - 1 && edge
0729: .getTargetParentView() != null)) {
0730: g.setColor(graph.getLockedHandleColor());
0731: Point2D tmp = (port != null) ? GraphConstants
0732: .getOffset(port.getAllAttributes()) : null;
0733: if (tmp != null) {
0734: g
0735: .drawLine((int) r[i].getX() + 1,
0736: (int) r[i].getY() + 1,
0737: (int) (r[i].getX() + r[i]
0738: .getWidth()) - 3,
0739: (int) (r[i].getY() + r[i]
0740: .getHeight()) - 3);
0741: g
0742: .drawLine((int) r[i].getX() + 1,
0743: (int) (r[i].getY() + r[i]
0744: .getHeight()) - 3,
0745: (int) (r[i].getX() + r[i]
0746: .getWidth()) - 3,
0747: (int) r[i].getY() + 1);
0748: } else
0749: g.drawRect((int) r[i].getX() + 2, (int) r[i]
0750: .getY() + 2, (int) r[i].getWidth() - 5,
0751: (int) r[i].getHeight() - 5);
0752: }
0753: }
0754: if (!graph.isXorEnabled()) {
0755: firstOverlayCall = false;
0756: overlay(g);
0757: }
0758: }
0759:
0760: public void overlay(Graphics g) {
0761: if (edge != null && !firstOverlayCall && edge.isLeaf()) {
0762: // g.setColor(graph.getBackground()); // JDK 1.3
0763: g.setColor(graph.getForeground());
0764: if (graph.isXorEnabled()) {
0765: g.setXORMode(graph.getBackground().darker());
0766: }
0767: Graphics2D g2 = (Graphics2D) g;
0768: AffineTransform oldTransform = g2.getTransform();
0769: g2.scale(graph.getScale(), graph.getScale());
0770: graph.getUI()
0771: .paintCell(g, edge, edge.getBounds(), true);
0772: g2.setTransform(oldTransform);
0773: if (isSourceEditing() && edge.getSource() != null)
0774: paintPort(g, edge.getSource());
0775: else if (isTargetEditing() && edge.getTarget() != null)
0776: paintPort(g, edge.getTarget());
0777: }
0778: firstOverlayCall = false;
0779: }
0780:
0781: protected void paintPort(Graphics g, CellView p) {
0782: boolean offset = (GraphConstants.getOffset(p
0783: .getAllAttributes()) != null);
0784: Rectangle2D r = (offset) ? p.getBounds() : p
0785: .getParentView().getBounds();
0786: r = graph.toScreen((Rectangle2D) r.clone());
0787: int s = 3;
0788: r.setFrame(r.getX() - s, r.getY() - s,
0789: r.getWidth() + 2 * s, r.getHeight() + 2 * s);
0790: graph.getUI().paintCell(g, p, r, true);
0791: }
0792:
0793: protected boolean snap(boolean source, Point2D point) {
0794: boolean connect = graph.isConnectable()
0795: && isEdgeConnectable;
0796: Object port = graph.getPortForLocation(point.getX(), point
0797: .getY());
0798: if (port != null
0799: && graph.getModel().getParent(port) == edge
0800: .getCell())
0801: port = null;
0802: if (port != null && connect) {
0803: CellView portView = graph.getGraphLayoutCache()
0804: .getMapping(port, false);
0805: Rectangle2D dirty = edge.getBounds();
0806: dirty.add(portView.getParentView().getBounds());
0807: if (GraphConstants.isConnectable(portView
0808: .getParentView().getAllAttributes())) {
0809: Object cell = edge.getCell();
0810: if (source
0811: && graph.getModel().acceptsSource(cell,
0812: port)) {
0813: if (edge.getSource() != portView) {
0814: edgeModified = true;
0815: if (graph.isXorEnabled()) {
0816: overlay(graph.getGraphics());
0817: }
0818: edge.setSource(portView);
0819: edge.update(graph.getGraphLayoutCache());
0820: if (graph.isXorEnabled()) {
0821: overlay(graph.getGraphics());
0822: } else {
0823: dirty.add(edge.getBounds());
0824: graph.repaint((int) dirty.getX(),
0825: (int) dirty.getY(), (int) dirty
0826: .getWidth(),
0827: (int) dirty.getHeight());
0828: }
0829: }
0830: return true;
0831: } else if (!source
0832: && graph.getModel().acceptsTarget(cell,
0833: port)) {
0834: if (edge.getTarget() != portView) {
0835: edgeModified = true;
0836: if (graph.isXorEnabled()) {
0837: overlay(graph.getGraphics());
0838: }
0839: edge.setTarget(portView);
0840: edge.update(graph.getGraphLayoutCache());
0841: if (graph.isXorEnabled()) {
0842: overlay(graph.getGraphics());
0843: } else {
0844: dirty.add(edge.getBounds());
0845: graph.repaint((int) dirty.getX(),
0846: (int) dirty.getY(), (int) dirty
0847: .getWidth(),
0848: (int) dirty.getHeight());
0849: }
0850: }
0851: return true;
0852: }
0853: }
0854: }
0855: return false;
0856: }
0857:
0858: public boolean isConstrainedMoveEvent(MouseEvent e) {
0859: GraphUI ui = graph.getUI();
0860: if (ui instanceof BasicGraphUI)
0861: return ((BasicGraphUI) ui).isConstrainedMoveEvent(e);
0862: return false;
0863: }
0864:
0865: /**
0866: * Returning true signifies a mouse event adds a new point to an edge.
0867: */
0868: public boolean isAddPointEvent(MouseEvent event) {
0869: return event.isPopupTrigger()
0870: || SwingUtilities.isRightMouseButton(event);
0871: }
0872:
0873: /**
0874: * Returning true signifies a mouse event removes a given point.
0875: */
0876: public boolean isRemovePointEvent(MouseEvent event) {
0877: return event.isPopupTrigger()
0878: || SwingUtilities.isRightMouseButton(event);
0879: }
0880:
0881: protected boolean isSourceEditing() {
0882: return source;
0883: }
0884:
0885: protected boolean isTargetEditing() {
0886: return target;
0887: }
0888:
0889: /*
0890: * Returns true if either the source, target, label or a point is being
0891: * edited.
0892: */
0893: protected boolean isEditing() {
0894: return source || target || label || currentLabel >= 0
0895: || currentPoint != null;
0896: }
0897:
0898: /**
0899: * Invoked when the mouse pointer has been moved on a component (with no
0900: * buttons down).
0901: */
0902: public void mouseMoved(MouseEvent event) {
0903: for (int i = 0; i < r.length; i++)
0904: if (r[i].contains(event.getPoint())) {
0905: graph
0906: .setCursor(new Cursor(
0907: Cursor.CROSSHAIR_CURSOR));
0908: event.consume();
0909: return;
0910: }
0911: if (loc.contains(event.getPoint())
0912: && graph.isMoveable()
0913: && GraphConstants.isMoveable(edge
0914: .getAllAttributes())) {
0915: graph.setCursor(new Cursor(Cursor.HAND_CURSOR));
0916: event.consume();
0917: }
0918: if (extraLabelLocations != null
0919: && graph.isMoveable()
0920: && GraphConstants.isMoveable(edge
0921: .getAllAttributes())) {
0922: for (int i = 0; i < extraLabelLocations.length; i++) {
0923: if (extraLabelLocations[i].contains(event
0924: .getPoint())) {
0925: graph.setCursor(new Cursor(Cursor.HAND_CURSOR));
0926: event.consume();
0927: }
0928: }
0929: }
0930: }
0931:
0932: // Handle mouse pressed event.
0933: public void mousePressed(MouseEvent event) {
0934: /* INV: currentPoint = null; source = target = label = false; */
0935: if (!edge.isLeaf())
0936: return;
0937: boolean bendable = graph.isBendable()
0938: && GraphConstants.isBendable(edge
0939: .getAllAttributes());
0940: int x = event.getX();
0941: int y = event.getY();
0942: // Detect hit on control point
0943: int index = 0;
0944: for (index = 0; index < r.length; index++) {
0945: if (r[index].contains(x, y)) {
0946: currentPoint = edge.getPoint(index);
0947: currentIndex = index;
0948: source = index == 0;
0949: target = index == r.length - 1;
0950: break;
0951: }
0952: }
0953: // Detect hit on label
0954: if (!isEditing()
0955: && graph.isMoveable()
0956: && GraphConstants.isMoveable(edge
0957: .getAllAttributes()) && loc != null
0958: && loc.contains(x, y) && !isAddPointEvent(event)
0959: && !isRemovePointEvent(event)
0960: && graph.getEdgeLabelsMovable()) {
0961: initialLabelLocation = (Point2D) edge
0962: .getLabelPosition().clone();
0963: label = true;
0964: }
0965: // Detect hit on extra labels
0966: else if (extraLabelLocations != null
0967: && !isEditing()
0968: && graph.isMoveable()
0969: && graph.getEdgeLabelsMovable()
0970: && GraphConstants.isMoveable(edge
0971: .getAllAttributes())) {
0972: for (int i = 0; i < extraLabelLocations.length; i++) {
0973: if (extraLabelLocations[i] != null
0974: && extraLabelLocations[i].contains(x, y)) {
0975: currentLabel = i;
0976: initialLabelLocation = (Point2D) edge
0977: .getExtraLabelPosition(currentLabel)
0978: .clone();
0979: if (isRemovePointEvent(event)) {
0980: edge.removeExtraLabel(i);
0981: edgeModified = true;
0982: mouseReleased(event);
0983: }
0984: break;
0985: }
0986: }
0987: }
0988: // Remove Point
0989: if (isRemovePointEvent(event)
0990: && currentPoint != null
0991: && !source
0992: && !target
0993: && bendable
0994: && (edge.getSource() == null || currentIndex > 0)
0995: && (edge.getTarget() == null || currentIndex < edge
0996: .getPointCount() - 1)) {
0997: edge.removePoint(index);
0998: edgeModified = true;
0999: mouseReleased(event);
1000: // Add Point
1001: } else if (isAddPointEvent(event) && !isEditing()
1002: && bendable) {
1003: int s = graph.getHandleSize();
1004: Rectangle2D rect = graph.fromScreen(new Rectangle(
1005: x - s, y - s, 2 * s, 2 * s));
1006: if (edge.intersects(graph, rect)) {
1007: Point2D point = graph.fromScreen(graph
1008: .snap(new Point(event.getPoint())));
1009: double min = Double.MAX_VALUE, dist = 0;
1010: for (int i = 0; i < edge.getPointCount() - 1; i++) {
1011: Point2D p = edge.getPoint(i);
1012: Point2D p1 = edge.getPoint(i + 1);
1013: dist = new Line2D.Double(p, p1)
1014: .ptSegDistSq(point);
1015: if (dist < min) {
1016: min = dist;
1017: index = i + 1;
1018: }
1019: }
1020: edge.addPoint(index, point);
1021: edgeModified = true;
1022: currentPoint = point;
1023: reloadPoints(edge);
1024: paint(graph.getGraphics());
1025: }
1026: }
1027: if (isEditing())
1028: event.consume();
1029: }
1030:
1031: public void mouseDragged(MouseEvent event) {
1032: Point2D p = graph.fromScreen(new Point(event.getPoint()));
1033: // Move Label
1034: if (label || currentLabel >= 0) {
1035: Rectangle2D r = edge.getBounds();
1036: if (r != null) {
1037: edgeModified = true;
1038: if (graph.isXorEnabled()) {
1039: overlay(graph.getGraphics());
1040: }
1041: if (!GraphConstants.isLabelAlongEdge(edge
1042: .getAllAttributes())) {
1043: p = getRelativeLabelPosition(edge, p);
1044: } else {
1045: double x = p.getX();
1046: double y = p.getY();
1047:
1048: Point2D p0 = edge.getPoint(0);
1049:
1050: double p0x = p0.getX();
1051: double p0y = p0.getY();
1052:
1053: Point2D vector = edge.getLabelVector();
1054: double dx = vector.getX();
1055: double dy = vector.getY();
1056:
1057: double pex = p0.getX() + dx;
1058: double pey = p0.getY() + dy;
1059:
1060: double len = Math.sqrt(dx * dx + dy * dy);
1061: if (len > 0) {
1062: double u = GraphConstants.PERMILLE;
1063: double posy = len
1064: * (-y * dx + p0y * dx + x * dy - p0x
1065: * dy)
1066: / (-pey * dy + p0y * dy - dx * pex + dx
1067: * p0x);
1068: double posx = u
1069: * (-y * pey + y * p0y + p0y * pey
1070: - p0y * p0y - pex * x + pex
1071: * p0x + p0x * x - p0x * p0x)
1072: / (-pey * dy + p0y * dy - dx * pex + dx
1073: * p0x);
1074: p = new Point2D.Double(posx, posy);
1075: } else {
1076: p = new Point2D.Double(x - p0.getX(), y
1077: - p0.getY());
1078: }
1079: }
1080: Rectangle2D dirty = edge.getBounds();
1081: if (label)
1082: edge.setLabelPosition(p);
1083: else
1084: edge.setExtraLabelPosition(currentLabel, p);
1085: edge.update(graph.getGraphLayoutCache());
1086: if (graph.isXorEnabled()) {
1087: overlay(graph.getGraphics());
1088: } else {
1089: graph.repaint((int) dirty.getX(), (int) dirty
1090: .getY(), (int) dirty.getWidth(),
1091: (int) dirty.getHeight());
1092: }
1093: }
1094: } else if (isEditing() && currentPoint != null) {
1095: boolean disconnectable = (!source && !target)
1096: || (graph.isDisconnectable() && GraphConstants
1097: .isDisconnectable(orig
1098: .getAllAttributes()));
1099: if (source)
1100: disconnectable = disconnectable
1101: && ((orig.getSource() == null && orig
1102: .getSourceParentView() == null)
1103: || (orig.getSource() != null && GraphConstants
1104: .isDisconnectable(orig
1105: .getSource()
1106: .getParentView()
1107: .getAllAttributes())) || (orig
1108: .getSourceParentView() != null && GraphConstants
1109: .isDisconnectable(orig
1110: .getSourceParentView()
1111: .getAllAttributes())));
1112: if (target)
1113: disconnectable = disconnectable
1114: && ((orig.getTarget() == null && orig
1115: .getTargetParentView() == null)
1116: || (orig.getTarget() != null && GraphConstants
1117: .isDisconnectable(orig
1118: .getTarget()
1119: .getParentView()
1120: .getAllAttributes())) || (orig
1121: .getTargetParentView() != null && GraphConstants
1122: .isDisconnectable(orig
1123: .getTargetParentView()
1124: .getAllAttributes())));
1125: // Find Source/Target Port
1126: if (!((source && snap(true, event.getPoint())) || (target && snap(
1127: false, event.getPoint())))
1128: && disconnectable) {
1129: // Else Use Point
1130: boolean acceptSource = source
1131: && (graph.getModel().acceptsSource(
1132: edge.getCell(), null) || graph
1133: .isPreviewInvalidNullPorts());
1134: boolean acceptTarget = target
1135: && (graph.getModel().acceptsTarget(
1136: edge.getCell(), null) || graph
1137: .isPreviewInvalidNullPorts());
1138: if (acceptSource || acceptTarget
1139: || !(source || target)) {
1140: edgeModified = true;
1141: Rectangle2D dirty = edge.getBounds();
1142: if (edge.getSource() != null) {
1143: dirty.add(edge.getSource().getParentView()
1144: .getBounds());
1145: }
1146: if (edge.getTarget() != null) {
1147: dirty.add(edge.getTarget().getParentView()
1148: .getBounds());
1149: }
1150: if (graph.isXorEnabled()) {
1151: overlay(graph.getGraphics());
1152: }
1153: p = graph.fromScreen(graph.snap(new Point(event
1154: .getPoint())));
1155: // Constrained movement
1156: if (isConstrainedMoveEvent(event)
1157: && currentIndex >= 0) {
1158: // Reset Initial Positions
1159: EdgeView orig = (EdgeView) graph
1160: .getGraphLayoutCache().getMapping(
1161: edge.getCell(), false);
1162: Point2D origPoint = orig
1163: .getPoint(currentIndex);
1164: double totDx = p.getX() - origPoint.getX();
1165: double totDy = p.getY() - origPoint.getY();
1166: if (Math.abs(totDx) < Math.abs(totDy))
1167: p.setLocation(origPoint.getX(), p
1168: .getY());
1169: else
1170: p.setLocation(p.getX(), origPoint
1171: .getY());
1172: }
1173: // Do not move into negative space
1174: p.setLocation(Math.max(0, p.getX()), Math.max(
1175: 0, p.getY()));
1176: currentPoint.setLocation(p);
1177: if (source) {
1178: edge.setPoint(0, p);
1179: edge.setSource(null);
1180: } else if (target) {
1181: edge.setPoint(edge.getPointCount() - 1, p);
1182: edge.setTarget(null);
1183: }
1184: edge.update(graph.getGraphLayoutCache());
1185: dirty.add(edge.getBounds());
1186: if (graph.isXorEnabled()) {
1187: overlay(graph.getGraphics());
1188: } else {
1189: if (edge.getSource() != null) {
1190: dirty.add(edge.getSource()
1191: .getParentView().getBounds());
1192: }
1193: if (edge.getTarget() != null) {
1194: dirty.add(edge.getTarget()
1195: .getParentView().getBounds());
1196: }
1197: graph.repaint((int) dirty.getX(),
1198: (int) dirty.getY(), (int) dirty
1199: .getWidth(), (int) dirty
1200: .getHeight());
1201: }
1202: }
1203: }
1204: }
1205: }
1206:
1207: protected Point2D getRelativeLabelPosition(EdgeView edge,
1208: Point2D p) {
1209: int pointCount = edge.getPointCount();
1210:
1211: double totalLength = 0;
1212: double[] segments = new double[pointCount];
1213:
1214: Point2D p0 = edge.getPoint(0);
1215: Point2D pt = p0;
1216:
1217: for (int i = 1; i < pointCount; i++) {
1218: Point2D tmp = edge.getPoint(i);
1219:
1220: if (tmp != null) {
1221: double dx = pt.getX() - tmp.getX();
1222: double dy = pt.getY() - tmp.getY();
1223:
1224: double segment = Math.sqrt(dx * dx + dy * dy);
1225:
1226: segments[i - 1] = segment;
1227: totalLength += segment;
1228: pt = tmp;
1229: }
1230: }
1231:
1232: Point2D last = edge.getPoint(1);
1233: Line2D line = new Line2D.Double(p0, last);
1234: double minDist = line.ptSegDistSq(p);
1235:
1236: int index = 0;
1237: double tmp = 0;
1238: double length = 0;
1239:
1240: for (int i = 2; i < pointCount; i++) {
1241: tmp += segments[i - 2];
1242:
1243: line = new Line2D.Double(edge.getPoint(i), last);
1244: double dist = line.ptLineDistSq(p);
1245:
1246: if (dist < minDist) {
1247: minDist = dist;
1248: index = i - 1;
1249: length = tmp;
1250: }
1251:
1252: last = edge.getPoint(i);
1253: }
1254:
1255: double seg = segments[index];
1256:
1257: pt = edge.getPoint(index);
1258:
1259: double x2 = pt.getX();
1260: double y2 = pt.getY();
1261:
1262: pt = edge.getPoint(index + 1);
1263:
1264: double x1 = pt.getX();
1265: double y1 = pt.getY();
1266:
1267: double px = p.getX();
1268: double py = p.getY();
1269:
1270: x2 -= x1;
1271: y2 -= y1;
1272:
1273: px -= x1;
1274: py -= y1;
1275:
1276: double dotprod = px * x2 + py * y2;
1277: double projlenSq = 0;
1278:
1279: px = x2 - px;
1280: py = y2 - py;
1281: dotprod = px * x2 + py * y2;
1282:
1283: if (dotprod <= 0.0) {
1284: projlenSq = 0;
1285: } else {
1286: projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2);
1287: }
1288:
1289: tmp = Math.sqrt(projlenSq);
1290: if (tmp > seg) {
1291: tmp = seg;
1292: }
1293:
1294: p0 = edge.getPoint(index);
1295: Point2D pe = edge.getPoint(index + 1);
1296:
1297: double offsetX = 0;
1298: double offsetY = 0;
1299:
1300: if (p0 != null && pe != null) {
1301: double factor = tmp / seg;
1302:
1303: double dx = pe.getX() - p0.getX();
1304: double dy = pe.getY() - p0.getY();
1305:
1306: double tx = p0.getX() + dx * factor;
1307: double ty = p0.getY() + dy * factor;
1308:
1309: offsetX = p.getX() - tx;
1310: offsetY = p.getY() - ty;
1311: }
1312:
1313: // Uses the offset constant to align the label to the point
1314: // from the relative location on the edge shape
1315: Point2D off = new Point2D.Double(offsetX, offsetY);
1316: GraphConstants.setOffset(edge.getAllAttributes(), off);
1317:
1318: // Contructs the relative point for the label
1319: Point2D result = new Point2D.Double(((((totalLength / 2
1320: - length - tmp) / totalLength) * -2) + 1)
1321: * GraphConstants.PERMILLE / 2, 0);
1322:
1323: return result;
1324: }
1325:
1326: // Handle mouse released event
1327: public void mouseReleased(MouseEvent e) {
1328: boolean clone = e.isControlDown() && graph.isCloneable();
1329: GraphModel model = graph.getModel();
1330: Object source = (edge.getSource() != null) ? edge
1331: .getSource().getCell() : null;
1332: Object target = (edge.getTarget() != null) ? edge
1333: .getTarget().getCell() : null;
1334: if (edgeModified
1335: && model.acceptsSource(edge.getCell(), source)
1336: && model.acceptsTarget(edge.getCell(), target)) {
1337:
1338: // Creates an extra label if the label was cloned
1339: if (clone && initialLabelLocation != null) {
1340:
1341: // Resets the dragging label position and adds a new label
1342: // instead. Note: label locations are modified in-place
1343: // which is why we need to clone at beginning.
1344: Object value = null;
1345: Point2D location = null;
1346: Object[] extraLabels = GraphConstants
1347: .getExtraLabels(edge.getAllAttributes());
1348: if (label) {
1349: location = (Point2D) edge.getLabelPosition()
1350: .clone();
1351: value = graph.convertValueToString(orig);
1352: edge.setLabelPosition(initialLabelLocation);
1353: } else {
1354: location = (Point2D) edge
1355: .getExtraLabelPosition(currentLabel)
1356: .clone();
1357: value = extraLabels[currentLabel];
1358: edge.setExtraLabelPosition(currentLabel,
1359: initialLabelLocation);
1360: }
1361: edge.addExtraLabel(location, value);
1362: edge.update(graph.getGraphLayoutCache());
1363: clone = false;
1364: }
1365:
1366: // Creates the data required for the edit/insert call
1367: ConnectionSet cs = createConnectionSet(edge, clone);
1368: Map nested = GraphConstants.createAttributes(
1369: new CellView[] { edge }, null);
1370:
1371: // The cached points may be different from what's
1372: // in the attribute map if the edge is routed.
1373: Map tmp = (Map) nested.get(edge.getCell());
1374: List controlPoints = GraphConstants.getPoints(tmp);
1375: List currentPoints = edge.getPoints();
1376:
1377: // Checks if we're dealing with a routing algorithm
1378: // and if we are, replaces only the source and target
1379: // in the control point list.
1380: if (controlPoints != currentPoints) {
1381: controlPoints.set(0, edge.getPoint(0));
1382: controlPoints.set(controlPoints.size() - 1, edge
1383: .getPoint(edge.getPointCount() - 1));
1384: }
1385:
1386: if (clone) {
1387: Map cellMap = graph.cloneCells(graph
1388: .getDescendants(new Object[] { edge
1389: .getCell() }));
1390: processNestedMap(nested, true);
1391: nested = GraphConstants
1392: .replaceKeys(cellMap, nested);
1393: cs = cs.clone(cellMap);
1394: Object[] cells = cellMap.values().toArray();
1395: graph.getGraphLayoutCache().insert(cells, nested,
1396: cs, null, null);
1397: } else {
1398: processNestedMap(nested, false);
1399: graph.getGraphLayoutCache().edit(nested, cs, null,
1400: null);
1401: }
1402: } else {
1403: if (graph.isXorEnabled()) {
1404: overlay(graph.getGraphics());
1405: } else {
1406: Rectangle2D dirty = edge.getBounds();
1407: graph.repaint((int) dirty.getX(), (int) dirty
1408: .getY(), (int) dirty.getWidth(),
1409: (int) dirty.getHeight());
1410: }
1411: edge.refresh(graph.getGraphLayoutCache(), graph
1412: .getGraphLayoutCache(), false);
1413: }
1414: initialLabelLocation = null;
1415: currentPoint = null;
1416: this .edgeModified = false;
1417: this .label = false;
1418: this .source = false;
1419: this .target = false;
1420: currentLabel = -1;
1421: currentIndex = -1;
1422: firstOverlayCall = true;
1423: e.consume();
1424: }
1425:
1426: protected void processNestedMap(Map nested, boolean clone) {
1427: // subclassers can override this to modify the attributes
1428: }
1429:
1430: protected ConnectionSet createConnectionSet(EdgeView view,
1431: boolean verbose) {
1432: Object edge = view.getCell();
1433: GraphModel model = graph.getModel();
1434: ConnectionSet cs = new ConnectionSet();
1435: Object sourcePort = null, targetPort = null;
1436: if (view.getSource() != null)
1437: sourcePort = view.getSource().getCell();
1438: else if (view.getSourceParentView() != null)
1439: sourcePort = model.getSource(edge);
1440: if (view.getTarget() != null)
1441: targetPort = view.getTarget().getCell();
1442: else if (view.getTargetParentView() != null)
1443: targetPort = model.getTarget(edge);
1444: if (view.getTarget() != null)
1445: targetPort = view.getTarget().getCell();
1446: if (verbose
1447: || (sourcePort != model.getSource(edge) && source))
1448: cs.connect(edge, sourcePort, true);
1449: if (verbose
1450: || (targetPort != model.getTarget(edge) && target))
1451: cs.connect(edge, targetPort, false);
1452: return cs;
1453: }
1454:
1455: // Update control points
1456: protected void invalidate() {
1457: EdgeView e = relevantEdge;
1458: int handlesize = graph.getHandleSize();
1459: EdgeRenderer er = (EdgeRenderer) edge.getRenderer();
1460: for (int i = 0; i < r.length; i++) {
1461: Point2D p = e.getPoint(i);
1462: p = graph.toScreen(new Point2D.Double(p.getX(), p
1463: .getY()));
1464: r[i].setFrame(p.getX() - handlesize, p.getY()
1465: - handlesize, 2 * handlesize, 2 * handlesize);
1466: p = graph.toScreen(er.getLabelPosition(e));
1467: Dimension d = er.getLabelSize(e, graph
1468: .convertValueToString(e));
1469: if (p != null && d != null) {
1470: Point2D s = graph.toScreen(new Point2D.Double(
1471: d.width, d.height));
1472: loc.setFrame(p.getX() - s.getX() / 2, p.getY()
1473: - s.getY() / 2, s.getX(), s.getY());
1474: }
1475: }
1476: if (extraLabelLocations != null) {
1477: for (int i = 0; i < extraLabelLocations.length; i++) {
1478: Point2D p = er.getExtraLabelPosition(e, i);
1479: if (p != null) {
1480: p = graph.toScreen((Point2D) p.clone());
1481: Dimension d = er.getExtraLabelSize(graph, e, i);
1482: if (d != null) {
1483: Point2D s = graph
1484: .toScreen(new Point2D.Double(
1485: d.width, d.height));
1486: extraLabelLocations[i].setFrame(p.getX()
1487: - s.getX() / 2, p.getY() - s.getY()
1488: / 2, s.getX(), s.getY());
1489: }
1490: }
1491: }
1492: }
1493: }
1494:
1495: }
1496:
1497: public Point2D getPerimeterPoint(EdgeView edge, Point2D source,
1498: Point2D p) {
1499: if (getPointCount() > 2)
1500: return getPoint(getPointCount() / 2);
1501: Point2D p0 = getPoint(0);
1502: Point2D pe = getPoint(getPointCount() - 1);
1503: return new Point2D.Double((pe.getX() + p0.getX()) / 2, (pe
1504: .getY() + p0.getY()) / 2);
1505: }
1506: }
|