/*******************************************************************************
* Copyright (c) 2017 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.behaviors;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.gef.common.collections.CollectionUtils;
import org.eclipse.gef.mvc.fx.models.SnappingModel;
import org.eclipse.gef.mvc.fx.models.SnappingModel.SnappingLocation;
import org.eclipse.gef.mvc.fx.parts.IFeedbackPartFactory;
import org.eclipse.gef.mvc.fx.parts.IVisualPart;
import org.eclipse.gef.mvc.fx.viewer.IViewer;
import javafx.collections.ListChangeListener;
import javafx.scene.Node;
/**
* The {@link SnappingBehavior} is responsible for creating and removing
* feedback and handles in response to {@link SnappingModel} changes.
*/
public class SnappingBehavior extends AbstractBehavior {
/**
* Defines the role for the {@link IFeedbackPartFactory} that is used to
* generate snap-to feedback.
*/
public static final String SNAPPING_FEEDBACK_PART_FACTORY = "snappingFeedbackPartFactory";
private ListChangeListener<SnappingLocation> snappingLocationsObserver = new ListChangeListener<SnappingLocation>() {
@Override
public void onChanged(
javafx.collections.ListChangeListener.Change<? extends SnappingLocation> c) {
List<? extends SnappingLocation> previousContents = CollectionUtils
.getPreviousContents(c);
onSnappingLocationsChanged(previousContents, c.getList());
}
};
@Override
protected void doActivate() {
super.doActivate();
SnappingModel snappingModel = getHost().getRoot().getViewer()
.getAdapter(SnappingModel.class);
snappingModel.snappingLocationsProperty()
.addListener(snappingLocationsObserver);
}
@Override
protected void doDeactivate() {
SnappingModel snappingModel = getHost().getRoot().getViewer()
.getAdapter(SnappingModel.class);
snappingModel.snappingLocationsProperty()
.removeListener(snappingLocationsObserver);
super.doDeactivate();
}
/**
* Returns the {@link IFeedbackPartFactory} for selection feedback.
*
* @return The {@link IFeedbackPartFactory} for selection feedback.
*/
@Override
protected IFeedbackPartFactory getFeedbackPartFactory(IViewer viewer) {
return getFeedbackPartFactory(viewer, SNAPPING_FEEDBACK_PART_FACTORY);
}
/**
* Callback method that is called in response to {@link SnappingModel}
* changes.
*
* @param oldValue
* A {@link List} containing the {@link SnappingLocation}s
* previously stored in the {@link SnappingModel}.
* @param newValue
* A {@link List} containing the {@link SnappingLocation}s
* currently stored in the {@link SnappingModel}.
*/
protected void onSnappingLocationsChanged(
List<? extends SnappingLocation> oldValue,
List<? extends SnappingLocation> newValue) {
// extract parts from both lists
Set<IVisualPart<? extends Node>> oldParts = new HashSet<>();
for (SnappingLocation sl : oldValue) {
oldParts.add(sl.getPart());
}
Set<IVisualPart<? extends Node>> newParts = new HashSet<>();
for (SnappingLocation sl : newValue) {
if (sl.getPart().getRoot() == null) {
throw new IllegalStateException(
"ContentPart has no link to the RootPart!");
}
newParts.add(sl.getPart());
}
// remove feedback for all parts that were previously present
for (IVisualPart<? extends Node> part : oldParts) {
removeFeedback(part);
}
// add feedback for all parts that are currently present
for (IVisualPart<? extends Node> part : newParts) {
addFeedback(part);
}
}
}