--- /dev/null
+package org.simantics.diagram.elements;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.Map;\r
+\r
+import org.simantics.diagram.elements.ResizeNode.ResizeListener;\r
+import org.simantics.diagram.elements.ResizeNode.TranslateEdge;\r
+import org.simantics.g2d.canvas.Hints;\r
+import org.simantics.g2d.diagram.DiagramUtils;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.element.ElementHints;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.SceneGraphNodeKey;\r
+import org.simantics.g2d.element.handler.PropertySetter;\r
+import org.simantics.g2d.element.handler.SceneGraph;\r
+import org.simantics.scenegraph.g2d.G2DNode;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+\r
+/**\r
+ * Scenegraph for resizing rectangular elements. Contains an invisible ResizeNode and \r
+ * handlers for setting bounds and transforms according to resize commands.\r
+ * \r
+ * @author Teemu Lempinen\r
+ *\r
+ */\r
+public class ResizeRectangularSceneGraph implements SceneGraph {\r
+\r
+ private static final long serialVersionUID = -972741261034892976L;\r
+\r
+ public static final ResizeRectangularSceneGraph INSTANCE = new ResizeRectangularSceneGraph();\r
+\r
+ public static final Key KEY_SG_NODE = new SceneGraphNodeKey(ResizeNode.class, "RESIZE_RECTANGULAR_SG_NODE");\r
+\r
+ private Key resizedNodeKey;\r
+ private Key transformKey;\r
+ private Key boundsKey;\r
+ private TranslateEdge xTranslateEdge;\r
+ private TranslateEdge yTranslateEdge;\r
+ \r
+ \r
+ public ResizeRectangularSceneGraph() {\r
+ this(null);\r
+ }\r
+ \r
+ public ResizeRectangularSceneGraph(Key sgNode) {\r
+ this(sgNode, TranslateEdge.WEST, TranslateEdge.NORTH, ElementHints.KEY_TRANSFORM, ElementHints.KEY_BOUNDS);\r
+ }\r
+ \r
+ public ResizeRectangularSceneGraph(Key sgNode, TranslateEdge xTranslateEdge, TranslateEdge yTranslateEdge, Key transformKey, Key boundsKey) {\r
+ this.resizedNodeKey = sgNode; \r
+ this.xTranslateEdge = xTranslateEdge;\r
+ this.yTranslateEdge = yTranslateEdge;\r
+ this.transformKey = transformKey;\r
+ this.boundsKey = boundsKey;\r
+ }\r
+\r
+ @Override\r
+ public void init(final IElement e, G2DParentNode parent) {\r
+ \r
+ if(!ElementUtils.getHintOrDefault(e, ElementHints.KEY_RESIZABLE, false))\r
+ return;\r
+ \r
+ ResizeNode node = (ResizeNode) e.getHint(KEY_SG_NODE);\r
+ if (node == null) {\r
+ node = parent.addNode(ResizeNode.class);\r
+ \r
+ e.setHint(KEY_SG_NODE, node);\r
+\r
+ node.setStroke(new BasicStroke(1f));\r
+ node.setZIndex(-1500);\r
+\r
+ // Add a resize listener for updating bounds and transform information to graph after resizing\r
+ node.setResizeListener(new ResizeListener() {\r
+\r
+ @Override\r
+ public void elementResized(Rectangle2D newBounds, AffineTransform transform, boolean synchronizeToBackend) {\r
+ Map<Key, Object> hints = e.getHints();\r
+ AffineTransform at = new AffineTransform((AffineTransform) hints.get(transformKey));\r
+ at.concatenate(transform);\r
+ hints.put(transformKey, at);\r
+ hints.put(boundsKey, newBounds);\r
+ hints.put(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY);\r
+ e.setHints(hints);\r
+ if(synchronizeToBackend) {\r
+ IDiagram diagram = ElementUtils.getDiagram(e);\r
+ DiagramUtils.synchronizeHintsToBackend(diagram, e);\r
+ }\r
+ }\r
+ });\r
+ }\r
+\r
+ if(node != null) {\r
+ node.setxTranslateEdge(xTranslateEdge);\r
+ node.setYTranslateEdge(yTranslateEdge);\r
+ \r
+ AffineTransform at = ElementUtils.getTransform(e);\r
+ Rectangle2D b = null; //= (Rectangle2D)e.getHint(ElementHints.KEY_BOUNDS);\r
+ \r
+ if(b == null) {\r
+ if(resizedNodeKey != null) {\r
+ Object resizedNode = e.getHint(resizedNodeKey);\r
+ if(resizedNode != null && resizedNode instanceof G2DNode) {\r
+ G2DNode n = (G2DNode)resizedNode;\r
+ b = n.getBoundsInLocal();\r
+ AffineTransform nodeTransform = n.getTransform();\r
+ at = new AffineTransform(nodeTransform);\r
+ }\r
+ }\r
+ }\r
+ \r
+ if(b == null)\r
+ b = ElementUtils.getElementBounds(e);\r
+ \r
+ Rectangle2D bounds = new Rectangle2D.Double(b.getX(), b.getY(), b.getWidth(), b.getHeight()); \r
+ \r
+ node.setBounds(bounds);\r
+ \r
+ if(at != null)\r
+ node.setTransform(at);\r
+ }\r
+ \r
+ update(e);\r
+ }\r
+ \r
+ public void update(IElement e) {\r
+ PropertySetter setter = e.getElementClass().getSingleItem(PropertySetter.class);\r
+ setter.syncPropertiesToNode(e);\r
+ }\r
+\r
+ @Override\r
+ public void cleanup(IElement e) {\r
+ ElementUtils.removePossibleNode(e, KEY_SG_NODE);\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Set the edge for X-axis translation. If this edge\r
+ * is dragged during resize, the x-translation for\r
+ * axis is also changed keeping the other edge in place.\r
+ * \r
+ * @param xTranslateEdge TranslateEdge NONE, EAST or WEST\r
+ */\r
+ public void setXTranslateEdge(TranslateEdge xTranslateEdge) {\r
+ this.xTranslateEdge = xTranslateEdge;\r
+ }\r
+ \r
+ /**\r
+ * Set the edge for Y-axis translation. If this edge\r
+ * is dragged during resize, the y-translation for\r
+ * axis is also changed keeping the other edge in place.\r
+ * \r
+ * @param xTranslateEdge TranslateEdge NONE, NORTH or SOUTH\r
+ */\r
+ public void setYTranslateEdge(TranslateEdge yTranslateEdge) {\r
+ this.yTranslateEdge = yTranslateEdge;\r
+ }\r
+\r
+}\r