--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.g2d.elementclass.connection;\r
+\r
+import java.awt.Shape;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.element.ElementClass;\r
+import org.simantics.g2d.element.ElementHints;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.handler.BendsHandler;\r
+import org.simantics.g2d.element.handler.InternalSize;\r
+import org.simantics.g2d.element.handler.LifeCycle;\r
+import org.simantics.g2d.element.handler.Move;\r
+import org.simantics.g2d.element.handler.Outline;\r
+import org.simantics.g2d.element.handler.Parent;\r
+import org.simantics.g2d.element.handler.Transform;\r
+import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;\r
+import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;\r
+import org.simantics.g2d.element.handler.impl.FillColorImpl;\r
+import org.simantics.g2d.element.handler.impl.ParentImpl;\r
+import org.simantics.g2d.element.handler.impl.ShapePick;\r
+import org.simantics.g2d.element.handler.impl.SimpleElementLayers;\r
+import org.simantics.g2d.elementclass.PlainElementPropertySetter;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
+\r
+/**\r
+ * @author Toni Kalajainen\r
+ */\r
+public class EdgeClass {\r
+\r
+ /**\r
+ * A {@link Transform} and {@link Move} implementation suitable for edges\r
+ * which are connected to nodes and cannot be moved by themselves.\r
+ * \r
+ * <p>\r
+ * The {@link Transform} implementation is a simple one with support for a\r
+ * parent element through {@link ElementHints#KEY_PARENT_ELEMENT}. The\r
+ * {@link Move} implementation in turn is a stub which does nothing to make\r
+ * edges immovable.\r
+ * \r
+ * <p>\r
+ * FIXME: The more correct solution would be not to have a {@link Move}\r
+ * handler at all but much the current participant code is very highly\r
+ * dependent on having a single {@link Move} handler available that this\r
+ * workaround seems better at this point.\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+ public static class FixedTransform implements Transform, Move {\r
+\r
+ private static final long serialVersionUID = 2287402413442694915L;\r
+\r
+ public static final FixedTransform INSTANCE = new FixedTransform();\r
+ public static final AffineTransform IDENTITY = new AffineTransform();\r
+\r
+ @Override\r
+ public AffineTransform getTransform(IElement e) {\r
+ AffineTransform local = e.getHint(ElementHints.KEY_TRANSFORM);\r
+ if (local == null)\r
+ local = IDENTITY;\r
+\r
+ Parent p = e.getElementClass().getAtMostOneItemOfClass(Parent.class);\r
+ if (p == null)\r
+ return local;\r
+\r
+ IElement parentElement = p.getParent(e);\r
+ if (parentElement == null)\r
+ return local;\r
+\r
+ AffineTransform parentTransform = parentElement.getElementClass().getSingleItem(Transform.class).getTransform(parentElement);\r
+ if (parentTransform.isIdentity())\r
+ return local;\r
+\r
+ AffineTransform result = new AffineTransform(local);\r
+ result.preConcatenate(parentTransform);\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public void setTransform(IElement e, AffineTransform at) {\r
+ assert at != null;\r
+ e.setHint(ElementHints.KEY_TRANSFORM, at);\r
+ }\r
+\r
+ @Override\r
+ public Point2D getPosition(IElement e) {\r
+ AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM);\r
+ if (at == null)\r
+ return new Point2D.Double();\r
+ return new Point2D.Double(at.getTranslateX(), at.getTranslateY());\r
+ }\r
+\r
+ @Override\r
+ public void moveTo(IElement e, double x, double y) {\r
+ // Don't allow moving.\r
+ }\r
+ }\r
+\r
+ // TODO scale, rotate, move, transform\r
+ public static final ElementClass STRAIGHT =\r
+ ElementClass.compile(\r
+ EdgeSceneGraph.INSTANCE,\r
+ EdgeHandler.INSTANCE,\r
+ ConfigurableEdgeVisuals.DEFAULT,\r
+ FillColorImpl.RED,\r
+ FixedTransform.INSTANCE,\r
+ ShapePick.INSTANCE,\r
+ ConnectionSelectionOutline.INSTANCE,\r
+ SimpleElementLayers.INSTANCE,\r
+ ParentImpl.INSTANCE,\r
+ new PlainElementPropertySetter(EdgeSceneGraph.KEY_SG_NODE)\r
+ ).setId("EdgeClass.STRAIGHT");\r
+\r
+\r
+ public static class EdgeHandler implements BendsHandler, Outline, LifeCycle, InternalSize {\r
+\r
+ private static final long serialVersionUID = -5949432471958957382L;\r
+\r
+ public static final EdgeHandler INSTANCE = new EdgeHandler();\r
+\r
+ public static final Key KEY_PATH = new KeyOf(Path2D.class, "PATH");\r
+ public static final Key KEY_BOUNDS = new KeyOf(Rectangle2D.class, "BOUNDS");\r
+ public static final Key KEY_ANGLETYPE = new KeyOf(AngleType.class);\r
+ public static final Key KEY_BENDS = new KeyOf(ArrayList.class, "BENDS");\r
+\r
+ public static class BendImpl implements Bend {\r
+ Point2D pos;\r
+ }\r
+\r
+ /**\r
+ * Reads bends, builds path, and writes it to KEY_PATH\r
+ * @param e\r
+ */\r
+ /*\r
+ private void buildPath(IElement e)\r
+ {\r
+ ArrayList<Point2D> points = new ArrayList<Point2D>();\r
+ ElementUtils.getBends(e, points);\r
+ Path2D path = GeometryUtils.buildPath(points);\r
+ e.setHint(KEY_PATH, path);\r
+ e.setHint(KEY_BOUNDS, path.getBounds2D());\r
+ }*/\r
+\r
+ @Override\r
+ public void setAngleType(IElement e, AngleType angleType) {\r
+ e.setHint(KEY_ANGLETYPE, angleType);\r
+ }\r
+\r
+ @Override\r
+ public AngleType getAngleType(IElement e) {\r
+ return e.getHint(KEY_ANGLETYPE);\r
+ }\r
+\r
+ @Override\r
+ public Shape getElementShape(IElement e) {\r
+ // Path2DOutlineShape no longer needed with ConnectionSelectionOutline\r
+ // that uses Stroke.createStrokedShape.\r
+ //return new Path2DOutlineShape((Path2D)e.getHint(KEY_PATH));\r
+ return e.getHint(KEY_PATH);\r
+ }\r
+/*\r
+ @Override\r
+ public AffineTransform getTransform(IElement e) {\r
+ AffineTransform at = GeometryUtils.IDENTITY;\r
+ assert(at.isIdentity());\r
+ return at;\r
+ }\r
+\r
+ @Override\r
+ public void setTransform(IElement e, AffineTransform at) {\r
+ Path2D path = e.getHint(KEY_PATH);\r
+ if (path==null) return;\r
+ ArrayList<BendImpl> list = e.getHint(KEY_BENDS);\r
+ for (BendImpl bi : list)\r
+ at.transform(bi.pos, bi.pos);\r
+ buildPath(e);\r
+ }\r
+\r
+ @Override\r
+ public Point2D getPosition(IElement e) {\r
+ return new Point2D.Double(0, 0);\r
+ }\r
+\r
+ @Override\r
+ public void moveTo(IElement e, double x, double y) {\r
+ AffineTransform at = new AffineTransform();\r
+ at.setToTranslation(x, y);\r
+ setTransform(e, at);\r
+ }\r
+\r
+ @Override\r
+ public double getAngle(IElement e, ICanvasContext ctx) {\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ public void rotate(IElement e, ICanvasContext ctx, double theta, Point2D origo) {\r
+ AffineTransform at = new AffineTransform();\r
+ at.setToRotation(theta, origo.getX(), origo.getY());\r
+ setTransform(e, at);\r
+ }\r
+\r
+ @Override\r
+ public Double getFixedAspectRatio(IElement e) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public Point2D getMaximumScale(IElement e) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public Point2D getMinimumScale(IElement e) {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public Point2D getScale(IElement e) {\r
+ return new Point2D.Double(1, 1);\r
+ }\r
+\r
+ @Override\r
+ public void setScale(IElement e, Point2D newScale) {\r
+ AffineTransform at = new AffineTransform();\r
+ at.setToScale(newScale.getX(), newScale.getY());\r
+ setTransform(e, at);\r
+ }\r
+ */\r
+ @Override\r
+ public void onElementActivated(IDiagram d, IElement e) {\r
+ update(e);\r
+ }\r
+\r
+ @Override\r
+ public void onElementCreated(IElement e) {\r
+ e.setHint(KEY_PATH, new Path2D.Double(Path2D.WIND_NON_ZERO, 2));\r
+ e.setHint(KEY_ANGLETYPE, AngleType.RightAngled);\r
+ e.setHint(KEY_BOUNDS, new Rectangle2D.Double());\r
+ e.setHint(KEY_BENDS, new ArrayList<BendImpl>(2));\r
+ }\r
+ @Override\r
+ public void onElementDestroyed(IElement e) {\r
+// EdgeSGNode sg = e.getElementClass().getAtMostOneItemOfClass(EdgeSGNode.class);\r
+// if(sg != null)\r
+// sg.cleanup(e);\r
+ }\r
+\r
+ private void update(IElement e) {\r
+ EdgeSceneGraph sg = e.getElementClass().getAtMostOneItemOfClass(EdgeSceneGraph.class);\r
+ if(sg != null)\r
+ sg.update(e);\r
+ }\r
+\r
+ @Override\r
+ public void onElementDeactivated(IDiagram d, IElement e) {\r
+ }\r
+\r
+ @Override\r
+ public Rectangle2D getBounds(IElement e, Rectangle2D size) {\r
+ Rectangle2D rect = e.getHint(KEY_BOUNDS);\r
+ if (size==null) size = new Rectangle2D.Double();\r
+ if (rect != null)\r
+ size.setFrame(rect);\r
+ return rect;\r
+ }\r
+\r
+ @Override\r
+ public Bend addBend(IElement e, int index, Point2D pos) {\r
+ ArrayList<BendImpl> list = e.getHint(KEY_BENDS);\r
+ BendImpl b = new BendImpl();\r
+ b.pos = new Point2D.Double(pos.getX(), pos.getY());\r
+ list.add(index, b);\r
+// buildPath(e);\r
+ update(e);\r
+ return b;\r
+ }\r
+\r
+ @Override\r
+ public void getBendPosition(IElement e, Bend b, Point2D pos) {\r
+ pos.setLocation( ((BendImpl)b).pos );\r
+ }\r
+\r
+ @Override\r
+ public int getBendsCount(IElement e) {\r
+ ArrayList<BendImpl> list = e.getHint(KEY_BENDS);\r
+ return list.size();\r
+ }\r
+\r
+ @Override\r
+ public void getBends(IElement e, List<Bend> bends) {\r
+ ArrayList<BendImpl> list = e.getHint(KEY_BENDS);\r
+ bends.addAll(list);\r
+ }\r
+\r
+ @Override\r
+ public boolean removeBend(IElement e, Bend b) {\r
+ ArrayList<BendImpl> list = e.getHint(KEY_BENDS);\r
+ if (!list.remove(b)) return false;\r
+// buildPath(e);\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public Path2D getPath(IElement e) {\r
+ return e.getHint(KEY_PATH);\r
+ }\r
+\r
+ @Override\r
+ public void setPath(IElement e, Path2D path) {\r
+ e.setHint(KEY_PATH, path);\r
+ e.setHint(KEY_BOUNDS, path.getBounds2D());\r
+ /*\r
+ ArrayList<BendImpl> list = e.getHint(KEY_BENDS);\r
+ ArrayList<Point2D> positions = new ArrayList<Point2D>();\r
+ GeometryUtils.getPoints(path, positions);\r
+ list.clear();\r
+ for (Point2D p : positions) {\r
+ BendImpl bi = new BendImpl();\r
+ bi.pos = p;\r
+ list.add(bi);\r
+ }*/\r
+ update(e);\r
+ }\r
+\r
+ @Override\r
+ public void moveBend(IElement e, Bend b, Point2D pos) {\r
+ BendImpl bi = ((BendImpl)b);\r
+ if (bi.pos.equals(pos)) return;\r
+ bi.pos.setLocation(pos);\r
+// buildPath(e);\r
+ update(e);\r
+ }\r
+ }\r
+\r
+ public static class ControlPointKey extends Key {\r
+ public final int index;\r
+ final int hash;\r
+ public ControlPointKey(int index)\r
+ {\r
+ super();\r
+ this.index = index;\r
+ hash = getClass().hashCode() ^ index ^ 54392439;\r
+ }\r
+ @Override\r
+ public boolean isValueAccepted(Object value) {\r
+ return IElement.class.isInstance(value);\r
+ }\r
+ @Override\r
+ public int hashCode() {\r
+ return hash;\r
+ }\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (obj == null)\r
+ return false;\r
+ if (getClass() != obj.getClass())\r
+ return false;\r
+ ControlPointKey other = (ControlPointKey) obj;\r
+ return other.index == index;\r
+ }\r
+ }\r
+\r
+}\r