001: /*
002: * $Id: JGraphpadEdgeView.java,v 1.5 2007/03/25 13:10:43 david Exp $
003: * Copyright (c) 2001-2005, Gaudenz Alder
004: *
005: * All rights reserved.
006: *
007: * See LICENSE file for license details. If you are unable to locate
008: * this file please contact info (at) jgraph (dot) com.
009: */
010: package com.jgraph.pad.graph;
011:
012: import java.awt.event.MouseEvent;
013: import java.awt.geom.Point2D;
014: import java.awt.geom.Rectangle2D;
015: import java.util.Map;
016:
017: import javax.swing.tree.DefaultMutableTreeNode;
018:
019: import org.jgraph.graph.CellHandle;
020: import org.jgraph.graph.CellView;
021: import org.jgraph.graph.CellViewRenderer;
022: import org.jgraph.graph.EdgeRenderer;
023: import org.jgraph.graph.EdgeView;
024: import org.jgraph.graph.GraphCellEditor;
025: import org.jgraph.graph.GraphConstants;
026: import org.jgraph.graph.GraphContext;
027:
028: /**
029: * Edge view that supports moveable ports. Note: The offset for the port depends
030: * on the edge, and is therefore stored in the edge's attributes. It is not
031: * required to provide a custom port view, as the port offset is returned from
032: * within the overridden {@link #getNearestPoint(boolean)}.
033: */
034: public class JGraphpadEdgeView extends EdgeView {
035:
036: /**
037: * Holds the static editor for views of this kind.
038: */
039: public static JGraphpadRichTextEditor editor = new JGraphpadRichTextEditor();
040:
041: /**
042: * Holds the static editor for views of this kind.
043: */
044: public static JGraphpadRedirectingEditor redirector = new JGraphpadRedirectingEditor();
045:
046: /**
047: * Holds the static renderer for views of this kind.
048: */
049: public static EdgeRenderer renderer = new JGraphpadEdgeRenderer();
050:
051: /**
052: * Empty constructor for persistence.
053: */
054: public JGraphpadEdgeView() {
055: super ();
056: }
057:
058: /**
059: * Constructs a new edge view for the specified cell.
060: *
061: * @param cell
062: * The cell to constructs the edge view for.
063: */
064: public JGraphpadEdgeView(Object cell) {
065: super (cell);
066: }
067:
068: /**
069: * Overrides the parent's implementation to return a custom handle.
070: *
071: * @param ctx
072: * The context to use for the handle.
073: * @return Returns a custom handle.
074: */
075: public CellHandle getHandle(GraphContext ctx) {
076: return new JGraphpadEdgeHandle(this , ctx);
077: }
078:
079: /**
080: * Overrides the parent's implementation to return the moveable port
081: * position for this edge.
082: *
083: * @param isSource
084: * Whether to return the nearest point for the source or target.
085: * @return Returns the moveable port position for this edge.
086: */
087: protected Point2D getNearestPoint(boolean isSource) {
088: if ((isSource && getSource() != null)
089: || (!isSource && getTarget() != null)) {
090: Point2D offset = (isSource) ? offset = JGraphpadGraphConstants
091: .getSourcePortOffset(getAllAttributes())
092: : JGraphpadGraphConstants
093: .getTargetPortOffset(getAllAttributes());
094: CellView parentView = (isSource) ? getSource()
095: .getParentView() : getTarget().getParentView();
096:
097: // Computes the absolute location of the relative offset
098: // and the source or target parentview's bounds.
099: if (offset != null && parentView != null) {
100: Rectangle2D r = parentView.getBounds();
101: double x = offset.getX() * (r.getWidth() - 1)
102: / GraphConstants.PERMILLE;
103: double y = offset.getY() * (r.getHeight() - 1)
104: / GraphConstants.PERMILLE;
105: Point2D pos = new Point2D.Double(r.getX() + x, r.getY()
106: + y);
107: return pos;
108: }
109: }
110: return super .getNearestPoint(isSource);
111: }
112:
113: /**
114: * Returns {@link #editor} if the user object of the cell is a rich text
115: * value or {@link #redirector} if the user object is a component.
116: *
117: * @return Returns the editor for the cell view.
118: */
119: public GraphCellEditor getEditor() {
120: Object value = ((DefaultMutableTreeNode) getCell())
121: .getUserObject();
122: if (value instanceof JGraphpadBusinessObject) {
123: JGraphpadBusinessObject obj = (JGraphpadBusinessObject) value;
124: if (obj.isRichText())
125: return editor;
126: else if (obj.isComponent())
127: return redirector;
128: }
129: return super .getEditor();
130: }
131:
132: /**
133: * Returns the {@link #renderer}.
134: *
135: * @return Returns the renderer for the cell view.
136: */
137: public CellViewRenderer getRenderer() {
138: return renderer;
139: }
140:
141: /**
142: * Custom handle that implements moveable ports.
143: */
144: public static class JGraphpadEdgeHandle extends EdgeHandle {
145:
146: /**
147: * Specifies if the offset should be reset if the mouse is released.
148: */
149: protected boolean resetOffset = false;
150:
151: /**
152: * Constructs a new edge handle for the specified edge and context.
153: *
154: * @param edge
155: * The edge to create the handle for.
156: * @param ctx
157: * The context to use for the handle.
158: */
159: public JGraphpadEdgeHandle(EdgeView edge, GraphContext ctx) {
160: super (edge, ctx);
161: }
162:
163: /**
164: * Overrides the parent implementation to support the shift key to
165: * remove points from edges.
166: *
167: * @param event
168: * The object that describes the event.
169: * @return Returns true if the event is a remove point event.
170: */
171: public boolean isRemovePointEvent(MouseEvent event) {
172: return super .isRemovePointEvent(event)
173: || event.isShiftDown();
174: }
175:
176: /**
177: * Overrides the parent implementation to support the shift key to add
178: * points to edges.
179: *
180: * @param event
181: * The object that describes the event.
182: * @return Returns true if the event is a add point event.
183: */
184: public boolean isAddPointEvent(MouseEvent event) {
185: return super .isAddPointEvent(event) || event.isShiftDown();
186: }
187:
188: /**
189: * Overrides the parent implementation to reset the port offset if a
190: * remove event has been performed on either port.
191: *
192: * @param event
193: * The object that describes the event.
194: */
195: public void mousePressed(MouseEvent event) {
196: super .mousePressed(event);
197: if (isRemovePointEvent(event) && (source || target))
198: resetOffset = true;
199: }
200:
201: /**
202: * Overrides the parent implementation to set the port offset if the
203: * mousepointer is over the port's parent view but not over the port's
204: * non-floating location (eg center).
205: *
206: * @param isSource
207: * Whether to snap the source or target port.
208: * @param point
209: * The point that should be used for snapping.
210: */
211: protected boolean snap(boolean isSource, Point2D point) {
212:
213: // Resets the offsets in the preview edge
214: if (isSource)
215: edge.getAllAttributes().remove(
216: JGraphpadGraphConstants.SOURCEPORTOFFSET);
217: else
218: edge.getAllAttributes().remove(
219: JGraphpadGraphConstants.TARGETPORTOFFSET);
220:
221: // Gets the bounds of the parent view and of the non-floating port
222: Rectangle2D parent = null;
223: Rectangle2D port = null;
224: if (isSource && edge.getSource() != null) {
225: parent = edge.getSource().getParentView().getBounds();
226: port = edge.getSource().getBounds();
227: } else if (target && edge.getTarget() != null) {
228: parent = edge.getTarget().getParentView().getBounds();
229: port = edge.getTarget().getBounds();
230: }
231:
232: // Sets the port offset if the mousepointer is over the
233: // source or target parent view bounds, but not over the
234: // port itself.
235: int tol = graph.getTolerance();
236: if (port != null && port.contains(point)) {
237: overlay(graph.getGraphics());
238: edge.update(graph.getGraphLayoutCache());
239: overlay(graph.getGraphics());
240: } else if (parent != null
241: && parent
242: .intersects(new Rectangle2D.Double(point
243: .getX()
244: - tol, point.getY() - tol, 2 * tol,
245: 2 * tol))) {
246:
247: // Makes sure the port moving does not trigger on reconnects,
248: // but only if the port is moved over the original source or
249: // target.
250: if ((isSource && edge.getSource() == orig.getSource())
251: || (target && edge.getTarget() == orig
252: .getTarget())) {
253: Point2D offset = computeOffset(point);
254: if (offset != null) {
255: edgeModified = true;
256: overlay(graph.getGraphics());
257: if (isSource)
258: JGraphpadGraphConstants
259: .setSourcePortOffset(edge
260: .getAllAttributes(), offset);
261: else
262: JGraphpadGraphConstants
263: .setTargetPortOffset(edge
264: .getAllAttributes(), offset);
265: edge.update(graph.getGraphLayoutCache());
266: overlay(graph.getGraphics());
267: resetOffset = false;
268: }
269: return true; // exit
270: }
271: }
272:
273: // Else the offset is reset and the superclass is called.
274: resetOffset = true;
275: return super .snap(isSource, point);
276: }
277:
278: /**
279: * Overrides the parent implementation to reset the port offset if the
280: * {@link #resetOffset} flag is set.
281: *
282: * @param nested
283: * The nested attribute map that is used to change the layout
284: * cache.
285: * @param clone
286: * Whether the control key was pressed.
287: */
288: protected void processNestedMap(Map nested, boolean clone) {
289: if (resetOffset) {
290: Map attrs = (Map) nested.get(edge.getCell());
291: if (attrs != null) {
292: String[] removeAttributes = new String[] { (source) ? JGraphpadGraphConstants.SOURCEPORTOFFSET
293: : JGraphpadGraphConstants.TARGETPORTOFFSET };
294: GraphConstants.setRemoveAttributes(attrs,
295: removeAttributes);
296: }
297: }
298: }
299:
300: /**
301: * Returns the perimeter point as a relative vector in (where
302: * 100%=GraphConstants.PERMILLE) in the coordinate space of the parent
303: * view bounds for the source or target port.
304: *
305: * @param pt
306: * The point to compute the offset for.
307: * @return Point
308: */
309: private Point2D computeOffset(Point2D pt) {
310: CellView portView = (source) ? edge.getSource() : edge
311: .getTarget();
312: if (portView != null) {
313: CellView vertex = portView.getParentView();
314: pt = vertex.getPerimeterPoint(edge,
315: getCenterPoint(vertex), pt);
316:
317: // Computes the relative vector for the perimeter point
318: Rectangle2D rect = vertex.getBounds();
319: pt = new Point2D.Double((pt.getX() - rect.getX())
320: / (rect.getWidth() - 1)
321: * GraphConstants.PERMILLE, (pt.getY() - rect
322: .getY())
323: / (rect.getHeight() - 1)
324: * GraphConstants.PERMILLE);
325: }
326: return pt;
327: }
328:
329: }
330:
331: }
|