/*******************************************************************************
* 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 and implementation
*
*******************************************************************************/
package org.eclipse.gef.mvc.fx.providers;
import org.eclipse.gef.common.adapt.IAdaptable;
import org.eclipse.gef.fx.anchors.ChopBoxStrategy;
import org.eclipse.gef.fx.anchors.DynamicAnchor;
import org.eclipse.gef.fx.anchors.DynamicAnchor.AnchorageReferenceGeometry;
import org.eclipse.gef.fx.anchors.IAnchor;
import org.eclipse.gef.fx.anchors.IComputationStrategy;
import org.eclipse.gef.fx.anchors.OrthogonalProjectionStrategy;
import org.eclipse.gef.fx.nodes.Connection;
import org.eclipse.gef.fx.nodes.OrthogonalRouter;
import org.eclipse.gef.fx.utils.NodeUtils;
import org.eclipse.gef.geometry.planar.IGeometry;
import org.eclipse.gef.mvc.fx.parts.IVisualPart;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
/**
* The {@link DefaultAnchorProvider} can be used to provide
* {@link DynamicAnchor}s for anchored {@link IVisualPart}s depending on their
* visual. For {@link Connection} visuals with an {@link OrthogonalRouter}, a
* {@link DynamicAnchor} with an {@link OrthogonalProjectionStrategy} is used.
* Otherwise, a {@link DynamicAnchor} with a {@link ChopBoxStrategy} is used.
*/
public class DefaultAnchorProvider
extends IAdaptable.Bound.Impl<IVisualPart<? extends Node>>
implements IAnchorProvider {
private DynamicAnchor defaultAnchor;
private DynamicAnchor orthoAnchor;
/**
* Constructs a new instance of {@link DefaultAnchorProvider}.
*/
public DefaultAnchorProvider() {
}
/**
* Returns the {@link AnchorageReferenceGeometry} that is to be used for the
* given {@link DynamicAnchor}.
*
* @param anchor
* The {@link DynamicAnchor} for which to compute the
* {@link AnchorageReferenceGeometry}.
* @return The {@link AnchorageReferenceGeometry} that is to be used for the
* given {@link DynamicAnchor}.
*/
protected IGeometry computeAnchorageReferenceGeometry(
DynamicAnchor anchor) {
return NodeUtils.getShapeOutline(getAdaptable().getVisual());
}
/**
* Creates a new {@link DynamicAnchor} using the visual of the
* {@link #getAdaptable()} as its anchorage and passing-in the given
* {@link IComputationStrategy}. Also sets up the computation parameters for
* the newly constructed anchor using
* {@link #initializeComputationParameters(DynamicAnchor)}.
*
* @param strategy
* The {@link IComputationStrategy} to use.
* @return The newly constructed and set up {@link DynamicAnchor}.
*/
protected DynamicAnchor createDynamicAnchor(IComputationStrategy strategy) {
DynamicAnchor anchor = new DynamicAnchor(getAdaptable().getVisual(),
strategy);
initializeComputationParameters(anchor);
return anchor;
}
@Override
public IAnchor get(IVisualPart<? extends Node> anchoredPart) {
Node anchoredVisual = anchoredPart.getVisual();
// check if orthogonal anchor should be used
if (anchoredVisual instanceof Connection) {
Connection connection = (Connection) anchoredVisual;
if (connection.getRouter() instanceof OrthogonalRouter) {
return getOrthogonalAnchor();
}
}
// fallback to default anchor
return getDefaultAnchor();
}
@Override
public IAnchor get(IVisualPart<? extends Node> anchoredPart, String role) {
return get(anchoredPart);
}
/**
* Returns the {@link IAnchor} that is to be used when no other, more
* specific anchor is used.
*
* @return The {@link IAnchor} that is to be used when no other, more
* specific anchor is used.
*/
protected IAnchor getDefaultAnchor() {
if (defaultAnchor == null) {
defaultAnchor = createDynamicAnchor(new ChopBoxStrategy());
}
return defaultAnchor;
}
/**
* Returns the {@link IAnchor} that is to be used for orthogonal
* {@link Connection}s.
*
* @return The {@link IAnchor} that is to be used for orthogonal
* {@link Connection}s
*/
protected IAnchor getOrthogonalAnchor() {
if (orthoAnchor == null) {
orthoAnchor = createDynamicAnchor(
new OrthogonalProjectionStrategy());
}
return orthoAnchor;
}
/**
* Initializes the computation parameters for the given
* {@link DynamicAnchor}.
*
* @param anchor
* The {@link DynamicAnchor} for which to initialize computation
* parameters.
*/
protected void initializeComputationParameters(final DynamicAnchor anchor) {
anchor.getComputationParameter(AnchorageReferenceGeometry.class)
.bind(new ObjectBinding<IGeometry>() {
{
// XXX: Binding value needs to be recomputed when the
// anchorage changes or when the layout bounds of the
// respective anchorage changes.
anchor.anchorageProperty()
.addListener(new ChangeListener<Node>() {
@Override
public void changed(
ObservableValue<? extends Node> observable,
Node oldValue, Node newValue) {
if (oldValue != null) {
unbind(oldValue
.boundsInLocalProperty());
}
if (newValue != null) {
bind(newValue
.boundsInLocalProperty());
}
invalidate();
}
});
bind(anchor.getAnchorage().boundsInLocalProperty());
}
@Override
protected IGeometry computeValue() {
return computeAnchorageReferenceGeometry(anchor);
}
});
}
}