/******************************************************************************* * Copyright (c) 2016 itemis AG and others. * * 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: * Matthias Wienand (itemis AG) - initial API & implementation * *******************************************************************************/ package org.eclipse.gef.zest.fx.policies; import org.eclipse.gef.common.attributes.IAttributeStore; import org.eclipse.gef.fx.nodes.Connection; import org.eclipse.gef.fx.utils.NodeUtils; import org.eclipse.gef.geometry.convert.fx.FX2Geometry; import org.eclipse.gef.geometry.planar.Point; import org.eclipse.gef.graph.Edge; import org.eclipse.gef.graph.Node; import org.eclipse.gef.mvc.fx.operations.TransformVisualOperation; import org.eclipse.gef.mvc.fx.parts.IContentPart; import org.eclipse.gef.mvc.fx.policies.TransformPolicy; import org.eclipse.gef.zest.fx.ZestProperties; import org.eclipse.gef.zest.fx.operations.ChangeAttributeOperation; import org.eclipse.gef.zest.fx.parts.AbstractLabelPart; import javafx.scene.transform.Affine; /** * The {@link TransformLabelPolicy} is a specialization of the * {@link TransformPolicy} that chains a {@link ChangeAttributeOperation} to * affect the underlying model when transforming nodes. It is applicable to * {@link IContentPart} with {@link javafx.scene.Node} visual and {@link Node} * content. * * @author anyssen * */ public class TransformLabelPolicy extends TransformPolicy { private Point initialOffset; private IContentPart<? extends javafx.scene.Node> getFirstAnchorage() { return (IContentPart<? extends javafx.scene.Node>) getHost().getAnchoragesUnmodifiable().keySet().iterator() .next(); } @Override public AbstractLabelPart getHost() { return (AbstractLabelPart) super.getHost(); } private Point getLabelOffsetInParent() { Point labelPositionInScene = getHost().getLabelPosition(); if (labelPositionInScene == null) { return null; } Point referencePositionInScene = getLabelReferencePointInScene(getHost().getContent().getValue()); Point labelOffset = NodeUtils.sceneToLocal(getHost().getVisual().getParent(), labelPositionInScene.getTranslated(referencePositionInScene.getNegated())); return labelOffset; } /** * Retrieve the reference position for the host label in scene coordinates. * * @param labelRole * The role of the label, i.e. one of * {@link ZestProperties#EXTERNAL_LABEL__NE}, * {@link ZestProperties#LABEL__NE}, * {@link ZestProperties#SOURCE_LABEL__E}, or * {@link ZestProperties#TARGET_LABEL__E}. * @return The reference position in scene coordinates. */ // TODO: make reference position configurable via Zest properties protected Point getLabelReferencePointInScene(String labelRole) { IAttributeStore contentElement = getHost().getContent().getKey(); if (ZestProperties.EXTERNAL_LABEL__NE.equals(labelRole)) { if (contentElement instanceof Node) { // node center return NodeUtils .localToScene(getFirstAnchorage().getVisual(), FX2Geometry.toRectangle(getFirstAnchorage().getVisual().getLayoutBounds())) .getBounds().getCenter(); } else if (getHost().getContent().getKey() instanceof Edge) { // edge mid segment mid point (or middle index); ensure external // label does not collide with label (thus offset with height) // TODO: we should detect whether a label is set and use the // height of the label instead Connection connection = (Connection) getFirstAnchorage().getVisual(); return NodeUtils.localToScene(connection, connection.getCenter().getTranslated(0, getHost().getVisual().getLayoutBounds().getHeight())); } else { throw new IllegalArgumentException("Unsupported element."); } } else if (ZestProperties.LABEL__NE.equals(labelRole)) { // node do not have 'internal' labels if (contentElement instanceof Edge) { Connection connection = (Connection) getFirstAnchorage().getVisual(); return NodeUtils.localToScene(connection, connection.getCenter()); } else { throw new IllegalArgumentException("Unsupported element."); } } else if (ZestProperties.SOURCE_LABEL__E.equals(labelRole)) { Connection connection = (Connection) getFirstAnchorage().getVisual(); return NodeUtils.localToScene(connection, connection.getStartPoint()); } else if (ZestProperties.TARGET_LABEL__E.equals(labelRole)) { Connection connection = (Connection) getFirstAnchorage().getVisual(); return NodeUtils.localToScene(connection, connection.getEndPoint()); } else { throw new IllegalArgumentException("Unsupported content element."); } } @Override public void init() { super.init(); // store initial relative label position (in scene coordinates) initialOffset = getLabelOffsetInParent(); } /** * Enforce that label is preserved at its respective relative location. * * @return Whether the position was adjusted or not. */ public boolean preserveLabelOffset() { if (initialOffset == null) { return false; } Point currentLabelOffset = getLabelOffsetInParent(); Point delta = currentLabelOffset.getTranslated(initialOffset.getNegated()); TransformVisualOperation op = ((TransformVisualOperation) getOperation()); Affine newTransform = op.getNewTransform(); newTransform.setTx(op.getInitialTransform().getTx() - delta.x); newTransform.setTy(op.getInitialTransform().getTy() - delta.y); locallyExecuteOperation(); return true; } }