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