/******************************************************************************* * 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.parts; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.eclipse.gef.common.adapt.AdapterKey; import org.eclipse.gef.fx.utils.NodeUtils; import org.eclipse.gef.geometry.planar.BezierCurve; import org.eclipse.gef.geometry.planar.ICurve; import org.eclipse.gef.geometry.planar.IGeometry; import org.eclipse.gef.geometry.planar.IShape; import org.eclipse.gef.geometry.planar.Rectangle; import org.eclipse.gef.mvc.fx.behaviors.IBehavior; import com.google.common.reflect.TypeToken; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provider; import javafx.scene.Node; /** * * @author mwienand * */ public class DefaultHoverIntentHandlePartFactory implements IHandlePartFactory { /** * The role name for the <code>Provider<IGeometry></code> that will be * used to generate hover handles. */ public static final String HOVER_INTENT_HANDLES_GEOMETRY_PROVIDER = "HOVER_INTENT_HANDLES_GEOMETRY_PROVIDER"; @Inject private Injector injector; @Override public List<IHandlePart<? extends Node>> createHandleParts( List<? extends IVisualPart<? extends Node>> targets, Map<Object, Object> contextMap) { // check that we have targets if (targets == null || targets.isEmpty()) { throw new IllegalArgumentException( "Part factory is called without targets."); } // check that only one part is hovered at a time if (targets.size() > 1) { throw new IllegalStateException( "Cannot create hover handles for more than one target."); } final IVisualPart<? extends Node> target = targets.get(0); // handle geometry is in target visual local coordinate space. @SuppressWarnings("serial") final Provider<? extends IGeometry> hoverHandlesGeometryInTargetLocalProvider = target .getAdapter(AdapterKey .get(new TypeToken<Provider<? extends IGeometry>>() { }, HOVER_INTENT_HANDLES_GEOMETRY_PROVIDER)); // generate handles from selection handles geometry IGeometry hoverHandlesGeometry = (hoverHandlesGeometryInTargetLocalProvider != null) ? hoverHandlesGeometryInTargetLocalProvider.get() : null; if (hoverHandlesGeometry == null) { return Collections.emptyList(); } // we will need a provider that returns the geometry in scene // coordinates final Provider<? extends IGeometry> hoverHandlesGeometryInSceneProvider = new Provider<IGeometry>() { @Override public IGeometry get() { return NodeUtils.localToScene(target.getVisual(), hoverHandlesGeometryInTargetLocalProvider.get()); } }; // the handle parts are located based on the segments of the handle // geometry Provider<BezierCurve[]> hoverHandlesSegmentsInSceneProvider = new Provider<BezierCurve[]>() { @Override public BezierCurve[] get() { IGeometry handleGeometry = hoverHandlesGeometryInSceneProvider .get(); if (handleGeometry instanceof IShape) { List<BezierCurve> segments = new ArrayList<>(); for (ICurve os : ((IShape) handleGeometry) .getOutlineSegments()) { segments.addAll(Arrays.asList(os.toBezier())); } return segments.toArray(new BezierCurve[] {}); } else if (handleGeometry instanceof ICurve) { return ((ICurve) handleGeometry).toBezier(); } else { throw new IllegalStateException( "Unable to determine handle position: Expected IShape or ICurve but got: " + handleGeometry); } } }; if (hoverHandlesGeometry instanceof ICurve) { // create curve handles return createHoverHandlePartsForCurve(target, contextMap, hoverHandlesSegmentsInSceneProvider); } else if (hoverHandlesGeometry instanceof IShape) { if (hoverHandlesGeometry instanceof Rectangle) { // create box handles return createHoverHandlePartsForRectangularOutline(target, contextMap, hoverHandlesSegmentsInSceneProvider); } else { // create segment handles (based on outline) return createHoverHandlePartsForPolygonalOutline(target, contextMap, hoverHandlesSegmentsInSceneProvider); } } else { throw new IllegalStateException( "Unable to generate handles for this handle geometry. Expected ICurve or IShape, but got: " + hoverHandlesGeometry); } } /** * Creates hover handle parts for a handle geometry that is an * {@link ICurve}. * * @param target * The target {@link IVisualPart} for which handles are to be * created. * @param contextMap * A map in which the state-less context {@link IBehavior}) may * place additional context information for the creation process. * It may either directly contain additional information needed * by the {@link IHandlePartFactory}, or may be passed back by * the {@link IHandlePartFactory} to the calling context * {@link IBehavior} to query such kind of information (in which * case it will allow the context {@link IBehavior} to identify * the creation context). * @param segmentsProvider * A provider for the segments of the handle geometry for which * handles are to be created. * @return A list of {@link IHandlePart}s that can be used to manipulate the * given targets. */ protected List<IHandlePart<? extends Node>> createHoverHandlePartsForCurve( IVisualPart<? extends Node> target, Map<Object, Object> contextMap, Provider<BezierCurve[]> segmentsProvider) { return Collections.emptyList(); } /** * Creates hover handle parts for a handle geometry that is an * {@link IShape} but not a {@link Rectangle}. * * @param target * The target {@link IVisualPart} for which handles are to be * created. * @param contextMap * A map in which the state-less context {@link IBehavior}) may * place additional context information for the creation process. * It may either directly contain additional information needed * by the {@link IHandlePartFactory}, or may be passed back by * the {@link IHandlePartFactory} to the calling context * {@link IBehavior} to query such kind of information (in which * case it will allow the context {@link IBehavior} to identify * the creation context). * @param segmentsProvider * A provider for the segments of the handle geometry for which * handles are to be created. * @return A list of {@link IHandlePart}s that can be used to manipulate the * given targets. */ protected List<IHandlePart<? extends Node>> createHoverHandlePartsForPolygonalOutline( IVisualPart<? extends Node> target, Map<Object, Object> contextMap, Provider<BezierCurve[]> segmentsProvider) { List<IHandlePart<? extends Node>> handleParts = new ArrayList<>(); BezierCurve[] segments = segmentsProvider.get(); for (int i = 0; i < segments.length; i++) { CircleSegmentHandlePart part = injector .getInstance(CircleSegmentHandlePart.class); part.setSegmentsProvider(segmentsProvider); part.setSegmentIndex(i); part.setSegmentParameter(0); handleParts.add(part); } return handleParts; } /** * Creates hover handle parts for a handle geometry that is a * {@link Rectangle}. * * @param target * The target {@link IVisualPart} for which handles are to be * created. * @param contextMap * A map in which the state-less context {@link IBehavior}) may * place additional context information for the creation process. * It may either directly contain additional information needed * by the {@link IHandlePartFactory}, or may be passed back by * the {@link IHandlePartFactory} to the calling context * {@link IBehavior} to query such kind of information (in which * case it will allow the context {@link IBehavior} to identify * the creation context). * @param segmentsProvider * A provider for the segments of the handle geometry for which * handles are to be created. * @return A list of {@link IHandlePart}s that can be used to manipulate the * given targets. */ protected List<IHandlePart<? extends Node>> createHoverHandlePartsForRectangularOutline( IVisualPart<? extends Node> target, Map<Object, Object> contextMap, Provider<BezierCurve[]> segmentsProvider) { return Collections.emptyList(); } }