/*******************************************************************************
* Copyright (c) 2014, 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:
* Alexander Nyßen (itemis AG) - initial API and implementation
*
*******************************************************************************/
package org.eclipse.gef.mvc.fx.parts;
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.IShape;
import org.eclipse.gef.geometry.planar.Point;
import com.google.common.collect.SetMultimap;
import com.google.common.reflect.TypeToken;
import com.google.inject.Provider;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
/**
* An {@link AbstractSegmentHandlePart} is bound to a segment of a poly-bezier
* handle geometry, represented by an array of {@link BezierCurve}s. The
* segmentIndex of the {@link AbstractSegmentHandlePart} identifies that segment
* (0, 1, 2, ...). The segmentParameter specifies the position of this handle
* part on the segment (0 = start, 0.5 = mid, 1 = end).
*
* @author anyssen
*
* @param <N>
* The type of visual used by this handle. Needs to be a sub-type of
* {@link Node}.
*/
public abstract class AbstractSegmentHandlePart<N extends Node>
extends AbstractHandlePart<N>
implements Comparable<AbstractSegmentHandlePart<? extends Node>> {
private Provider<BezierCurve[]> segmentsProvider;
private BezierCurve[] segments;
private int segmentIndex = -1;
private double segmentParameter = 0.0;
@Override
public int compareTo(AbstractSegmentHandlePart<? extends Node> o) {
// if we are bound to the same anchorages, we may compare segment
// positions, otherwise we are not comparable
if (!getAnchoragesUnmodifiable()
.equals(o.getAnchoragesUnmodifiable())) {
throw new IllegalArgumentException(
"Can only compare FXSegmentHandleParts that are bound to the same anchorages.");
}
return (int) ((100 * getSegmentIndex() + 10 * getSegmentParameter())
- (100 * o.getSegmentIndex() + 10 * o.getSegmentParameter()));
}
@Override
public void doRefreshVisual(N visual) {
updateLocation(visual);
}
/**
* Returns the {@link BezierCurve} at which this handle part is anchored
* (depending on segment index), or <code>null</code> if that
* {@link BezierCurve} cannot be determined.
*
* @return The {@link BezierCurve} at which this handle part is anchored, or
* <code>null</code> if that {@link BezierCurve} cannot be
* determined.
*/
protected BezierCurve getBezierSegmentInParent() {
segments = segmentsProvider.get();
if (segmentIndex < 0 || segmentIndex > segments.length - 1) {
return null;
} else {
return (BezierCurve) NodeUtils.sceneToLocal(getVisual().getParent(),
segments[segmentIndex]);
}
}
/**
* Returns the {@link Color} that is used to fill connected handles.
*
* @return The {@link Color} that is used to fill connected handles.
*/
protected Color getConnectedFill() {
@SuppressWarnings("serial")
Provider<Color> connectedColorProvider = getViewer()
.getAdapter(AdapterKey.get(new TypeToken<Provider<Color>>() {
}, DefaultSelectionHandlePartFactory.CONNECTED_HANDLE_COLOR_PROVIDER));
return connectedColorProvider == null
? DefaultSelectionHandlePartFactory.DEFAULT_CONNECTED_HANDLE_COLOR
: connectedColorProvider.get();
}
/**
* Returns the {@link Color} that is used to fill insertion handles.
*
* @return The {@link Color} that is used to fill insertion handles.
*/
protected Color getInsertFill() {
@SuppressWarnings("serial")
Provider<Color> connectedColorProvider = getViewer()
.getAdapter(AdapterKey.get(new TypeToken<Provider<Color>>() {
}, DefaultSelectionHandlePartFactory.INSERT_HANDLE_COLOR_PROVIDER));
return connectedColorProvider == null
? DefaultSelectionHandlePartFactory.DEFAULT_INSERT_HANDLE_COLOR
: connectedColorProvider.get();
}
/**
* Returns the {@link Color} that is used to fill movement handles.
*
* @return The {@link Color} that is used to fill movement handles.
*/
protected Color getMoveFill() {
@SuppressWarnings("serial")
Provider<Color> connectedColorProvider = getViewer()
.getAdapter(AdapterKey.get(new TypeToken<Provider<Color>>() {
}, DefaultSelectionHandlePartFactory.MOVE_HANDLE_COLOR_PROVIDER));
return connectedColorProvider == null
? DefaultSelectionHandlePartFactory.DEFAULT_MOVE_HANDLE_COLOR
: connectedColorProvider.get();
}
/**
* Returns the position of this {@link AbstractSegmentHandlePart} on the
* given segment using the segment parameter that is assigned to this part.
*
* @param segment
* The {@link BezierCurve} on which the position is evaluated.
* @return The position of this part on the given segment using the segment
* parameter of this part.
*/
protected Point getPosition(BezierCurve segment) {
return segment.get(segmentParameter);
}
/**
* Returns the number of segments that are provided to this part.
*
* @return The number of segments that are provided to this part.
*/
// TODO: Make protected
public int getSegmentCount() {
return segments == null ? 0 : segments.length;
}
/**
* The segmentIndex specifies the segment of the IGeometry provided by the
* handle geometry provider on which this selection handle part is
* positioned.
*
* For a shape geometry, segments are determined by the
* {@link IShape#getOutlineSegments()} method.
*
* For a curve geometry, segments are determined by the
* {@link ICurve#toBezier()} method.
*
* The exact position on the segment is specified by the
* {@link #getSegmentParameter() segmentParameter}.
*
* @return segmentIndex
*/
public int getSegmentIndex() {
return segmentIndex;
}
/**
* The segmentParameter is a value between 0 and 1. It determines the final
* point on the segment which this selection handle part belongs to.
*
* @return segmentParameter
*/
public double getSegmentParameter() {
return segmentParameter;
}
/**
* Returns the {@link BezierCurve}s that are provided to this part in the
* coordinate system of the {@link Scene}.
*
* @return The {@link BezierCurve}s that are provided to this part in the
* coordinate system of the {@link Scene}.
*/
protected BezierCurve[] getSegmentsInScene() {
return segments;
}
/**
* Returns the {@link Color} that is used to stroke handles.
*
* @return The {@link Color} that is used to stroke handles.
*/
protected Color getStroke() {
@SuppressWarnings("serial")
Provider<Color> connectedColorProvider = getViewer()
.getAdapter(AdapterKey.get(new TypeToken<Provider<Color>>() {
}, DefaultSelectionFeedbackPartFactory.PRIMARY_SELECTION_FEEDBACK_COLOR_PROVIDER));
return connectedColorProvider == null
? DefaultSelectionFeedbackPartFactory.DEFAULT_PRIMARY_SELECTION_FEEDBACK_COLOR
: connectedColorProvider.get();
}
/**
* Sets the segment index and refreshes the visual.
*
* @param segmentIndex
* The segment index to set.
* @see #getSegmentIndex()
*/
public void setSegmentIndex(int segmentIndex) {
int oldSegmentIndex = this.segmentIndex;
this.segmentIndex = segmentIndex;
if (oldSegmentIndex != segmentIndex) {
refreshVisual();
}
}
/**
* Sets the segment parameter and refreshes the visual.
*
* @param segmentParameter
* The segment parameter to set.
* @see #getSegmentParameter()
*/
public void setSegmentParameter(double segmentParameter) {
double oldSegmentParameter = this.segmentParameter;
this.segmentParameter = segmentParameter;
if (oldSegmentParameter != segmentParameter) {
refreshVisual();
}
}
/**
* Sets the <code>Provider<BezierCurve[]></code> for this part to the
* given value.
*
* @param segmentsProvider
* The new <code>Provider<BezierCurve[]></code> for this
* part.
*/
public void setSegmentsProvider(Provider<BezierCurve[]> segmentsProvider) {
this.segmentsProvider = segmentsProvider;
}
/**
* Computes the location for this part and relocates its visual to that
* location. The visual is made invisible if this part has an invalid index
* (out of bounds), i.e. when no location can be computed.
*
* @param visual
* This part's visual for convenience.
*/
protected void updateLocation(N visual) {
// only update when bound to anchorage
SetMultimap<IVisualPart<? extends Node>, String> anchorages = getAnchoragesUnmodifiable();
if (anchorages.keySet().size() < 1) {
return;
}
BezierCurve segmentInParent = getBezierSegmentInParent();
if (segmentInParent == null) {
// hide those that have "invalid" index. (this may happen during
// life-feedback, when a way-point is removed)
visual.setVisible(false);
} else {
visual.setVisible(true);
Point positionInParent = getPosition(segmentInParent);
// transform to handle space
visual.relocate(
positionInParent.x + visual.getLayoutBounds().getMinX(),
positionInParent.y + visual.getLayoutBounds().getMinY());
}
}
}