/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.participant; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import org.simantics.g2d.canvas.Hints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.SGDesignation; import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.canvas.impl.HintReflection.HintListener; import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup; import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; import org.simantics.g2d.utils.GeometryUtils; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.nodes.BoundsNode; import org.simantics.utils.datastructures.hints.IHintContext; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.datastructures.hints.IHintContext.Key; /** * A canvas participant that stores the current control and canvas boundaries * and detects their changes. * * @author Tuukka Lehtonen */ public class CanvasBoundsParticipant extends AbstractCanvasParticipant { BoundsNode.ResizeListener resizeListener = new BoundsNode.ResizeListener() { @Override public void controlResized(Rectangle2D bounds) { controlBoundsUpdated(bounds); } }; @Dependency private TransformUtil util; private BoundsNode boundsNode = null; private Rectangle2D controlBounds = new Rectangle2D.Double(); private Rectangle2D canvasBounds = new Rectangle2D.Double(); @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM") public void selectionChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { // Update canvas bounds whenever canvas transform changes. AffineTransform inv = util.getInverseTransform((AffineTransform) newValue); if (inv != null) { canvasTransformUpdated(controlBounds, inv); } else { // TODO: what should be done here? Leave canvas bounds as they are or mark them invalid somehow ?? } } @Override public void addedToContext(ICanvasContext ctx) { super.addedToContext(ctx); IHintContext hctx = ctx.getDefaultHintContext(); hctx.setHint(Hints.KEY_CONTROL_BOUNDS, controlBounds); hctx.setHint(Hints.KEY_CANVAS_BOUNDS, canvasBounds); } @SGInit(designation = SGDesignation.CONTROL) public void initSG(G2DParentNode parent) { boundsNode = parent.addNode("canvasBounds", BoundsNode.class); boundsNode.setZIndex(Integer.MIN_VALUE); boundsNode.setResizeListener(resizeListener); } @SGCleanup public void cleanupSG() { if (boundsNode != null) { boundsNode.setResizeListener(null); boundsNode.remove(); boundsNode = null; } } private void controlBoundsUpdated(Rectangle2D bounds) { if (!controlBounds.equals(bounds)) { setControlBounds(bounds.getBounds()); } AffineTransform invTr = util.getInverseTransform(); if (invTr != null) canvasTransformUpdated(controlBounds, invTr); } private void canvasTransformUpdated(Rectangle2D controlBounds, AffineTransform inverseViewTr) { Shape shape = GeometryUtils.transformShape(controlBounds, inverseViewTr); Rectangle2D visibleCanvasBounds = shape.getBounds2D(); if (!canvasBounds.equals(visibleCanvasBounds)) { setCanvasBounds(visibleCanvasBounds); } } private void setControlBounds(Rectangle bounds) { //System.out.println("Set control bounds: " + bounds); this.controlBounds = bounds; IHintContext ctx = getContext().getDefaultHintContext(); ctx.setHint(Hints.KEY_CONTROL_BOUNDS, controlBounds); } private void setCanvasBounds(Rectangle2D bounds) { //System.out.println("Set canvas bounds: " + bounds); this.canvasBounds = bounds; IHintContext ctx = getContext().getDefaultHintContext(); ctx.setHint(Hints.KEY_CANVAS_BOUNDS, canvasBounds); } /** * @return control bounds. Returns internal state, do not modify. * If the current rendering surface is pixel based, the returned * {@link Rectangle2D} may also be a {@link Rectangle}. */ public Rectangle2D getControlBounds() { return controlBounds; } /** * @return visible canvas area bounds in canvas space coordinates. Returns * internal state, do not modify. */ public Rectangle2D getCanvasBounds() { return canvasBounds; } }