/*******************************************************************************
* 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.examples.logo.ui.view;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.AbstractOperation;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.gef.common.adapt.AdapterKey;
import org.eclipse.gef.fx.anchors.DynamicAnchor;
import org.eclipse.gef.fx.nodes.Connection;
import org.eclipse.gef.geometry.planar.Point;
import org.eclipse.gef.mvc.examples.logo.MvcLogoExample;
import org.eclipse.gef.mvc.examples.logo.MvcLogoExampleModule;
import org.eclipse.gef.mvc.examples.logo.MvcLogoExampleViewersComposite;
import org.eclipse.gef.mvc.examples.logo.model.GeometricCurve;
import org.eclipse.gef.mvc.examples.logo.ui.MvcLogoExampleUiModule;
import org.eclipse.gef.mvc.examples.logo.ui.properties.GeometricCurvePropertySource;
import org.eclipse.gef.mvc.examples.logo.ui.properties.MvcLogoExampleViewPropertySource;
import org.eclipse.gef.mvc.fx.behaviors.SelectionBehavior;
import org.eclipse.gef.mvc.fx.models.FocusModel;
import org.eclipse.gef.mvc.fx.models.HoverModel;
import org.eclipse.gef.mvc.fx.models.SelectionModel;
import org.eclipse.gef.mvc.fx.operations.AbstractCompositeOperation;
import org.eclipse.gef.mvc.fx.operations.ForwardUndoCompositeOperation;
import org.eclipse.gef.mvc.fx.operations.ITransactionalOperation;
import org.eclipse.gef.mvc.fx.parts.IContentPart;
import org.eclipse.gef.mvc.fx.ui.actions.FitToViewportAction;
import org.eclipse.gef.mvc.fx.ui.actions.FitToViewportLockAction;
import org.eclipse.gef.mvc.fx.ui.actions.ScrollActionGroup;
import org.eclipse.gef.mvc.fx.ui.actions.ZoomActionGroup;
import org.eclipse.gef.mvc.fx.ui.parts.AbstractFXView;
import org.eclipse.gef.mvc.fx.ui.properties.SetPropertyValueOperation;
import org.eclipse.gef.mvc.fx.ui.properties.UndoablePropertySheetEntry;
import org.eclipse.gef.mvc.fx.ui.properties.UndoablePropertySheetPage;
import org.eclipse.gef.mvc.fx.viewer.IViewer;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.IPropertySource;
import com.google.inject.Guice;
import com.google.inject.util.Modules;
import javafx.scene.Node;
import javafx.scene.Scene;
public class MvcLogoExampleView extends AbstractFXView {
public static final class ChangeWayPointsOperation extends AbstractOperation
implements ITransactionalOperation {
private final GeometricCurve curve;
private final List<Point> newWayPoints;
private final List<Point> oldWayPoints;
public ChangeWayPointsOperation(String label, GeometricCurve curve,
List<Point> oldWayPoints, List<Point> newWayPoints) {
super(label);
this.curve = curve;
this.oldWayPoints = oldWayPoints;
this.newWayPoints = newWayPoints;
}
@Override
public IStatus execute(IProgressMonitor monitor, IAdaptable info) {
curve.setWayPoints(newWayPoints.toArray(new Point[] {}));
return Status.OK_STATUS;
}
@Override
public boolean isContentRelevant() {
return true;
}
@Override
public boolean isNoOp() {
return oldWayPoints == newWayPoints || (oldWayPoints != null
&& oldWayPoints.equals(newWayPoints));
}
@Override
public IStatus redo(IProgressMonitor monitor, IAdaptable info) {
return execute(monitor, info);
}
@Override
public IStatus undo(IProgressMonitor monitor, IAdaptable info) {
curve.setWayPoints(oldWayPoints.toArray(new Point[] {}));
return Status.OK_STATUS;
}
}
private UndoablePropertySheetEntry rootEntry;
private ZoomActionGroup zoomActionGroup;
private ScrollActionGroup scrollActionGroup;
private FitToViewportLockAction fitToViewportLockAction;
// TODO: create AbstractFXView via an executable extension factory
// (obtaining the injector via the bundle)
public MvcLogoExampleView() {
super(Guice.createInjector(Modules.override(new MvcLogoExampleModule())
.with(new MvcLogoExampleUiModule())));
// set initial contents
getContentViewer().getContents()
.setAll(MvcLogoExample.createContentViewerContents());
getPaletteViewer().getContents()
.setAll(MvcLogoExample.createPaletteViewerContents());
}
@Override
public void dispose() {
// clear viewer models
getContentViewer().getAdapter(SelectionModel.class).clearSelection();
getContentViewer().getAdapter(HoverModel.class).clearHover();
getContentViewer().getAdapter(FocusModel.class).setFocus(null);
getContentViewer().contentsProperty().clear();
getPaletteViewer().getAdapter(SelectionModel.class).clearSelection();
getPaletteViewer().getAdapter(HoverModel.class).clearHover();
getPaletteViewer().getAdapter(FocusModel.class).setFocus(null);
getPaletteViewer().contentsProperty().clear();
// dispose actions
if (zoomActionGroup != null) {
zoomActionGroup.dispose();
zoomActionGroup = null;
}
if (scrollActionGroup != null) {
scrollActionGroup.dispose();
scrollActionGroup = null;
}
if (fitToViewportLockAction != null) {
fitToViewportLockAction.dispose();
fitToViewportLockAction = null;
}
super.dispose();
}
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
// create actions
zoomActionGroup = new ZoomActionGroup(new FitToViewportAction());
zoomActionGroup.init(getContentViewer());
fitToViewportLockAction = new FitToViewportLockAction();
fitToViewportLockAction.init(getContentViewer());
scrollActionGroup = new ScrollActionGroup();
scrollActionGroup.init(getContentViewer());
// contribute to toolbar
IActionBars actionBars = getViewSite().getActionBars();
IToolBarManager mgr = actionBars.getToolBarManager();
zoomActionGroup.fillActionBars(actionBars);
mgr.add(new Separator());
mgr.add(fitToViewportLockAction);
mgr.add(new Separator());
scrollActionGroup.fillActionBars(actionBars);
}
/**
* The {@link UpdateSelectionHandlesOperation} can be used to update the
* selection handles for a given {@link IContentPart}. Re-selecting (i.e.
* deselect & select) is not possible in the context of a
* UndoablePropertySheetEntry#valueChanged(), because #valueChanged() is
* also called when the selection changes, and mutations from within a
* collection listener are forbidden.
*/
private static class UpdateSelectionHandlesOperation
extends AbstractOperation implements ITransactionalOperation {
private IContentPart<? extends Node> part;
public UpdateSelectionHandlesOperation(
IContentPart<? extends Node> part) {
super("UpdateHandles");
this.part = part;
}
@Override
public boolean isContentRelevant() {
return false;
}
@Override
public boolean isNoOp() {
return false;
}
@Override
public IStatus execute(IProgressMonitor monitor, IAdaptable info)
throws ExecutionException {
part.getRoot().getAdapter(SelectionBehavior.class)
.updateHandles(part, null, null);
return Status.OK_STATUS;
}
@Override
public IStatus redo(IProgressMonitor monitor, IAdaptable info)
throws ExecutionException {
return execute(monitor, info);
}
@Override
public IStatus undo(IProgressMonitor monitor, IAdaptable info)
throws ExecutionException {
return execute(monitor, info);
}
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class key) {
if (IPropertySheetPage.class.equals(key)) {
// use another UndoablePropertySheetEntry, which chains undo of
// waypoint removal
UndoablePropertySheetPage propertySheetPage = (UndoablePropertySheetPage) super.getAdapter(
key);
if (rootEntry == null) {
rootEntry = new UndoablePropertySheetEntry(this,
(IOperationHistory) getAdapter(IOperationHistory.class),
(IUndoContext) getAdapter(IUndoContext.class)) {
// FIXME: Code copied from BendConnectionPolicy (see
// #494752)
private Point computeEndHint(Connection connection) {
if (connection.getEndAnchor() instanceof DynamicAnchor
&& connection.getPointsUnmodifiable()
.size() > 1) {
Point endPoint = connection.getEndPoint();
Point neighbor = connection.getPoint(
connection.getPointsUnmodifiable().size()
- 2);
Point translated = endPoint.getTranslated(endPoint
.getDifference(neighbor).getScaled(0.5));
return translated;
}
return null;
}
// FIXME: Code copied from BendConnectionPolicy (see
// #494752)
private Point computeStartHint(Connection connection) {
if (connection.getStartAnchor() instanceof DynamicAnchor
&& connection.getPointsUnmodifiable()
.size() > 1) {
Point startPoint = connection.getStartPoint();
Point neighbor = connection.getPoint(1);
Point translated = startPoint.getTranslated(
startPoint.getDifference(neighbor)
.getScaled(0.5));
return translated;
}
return null;
}
@Override
protected void valueChanged(
UndoablePropertySheetEntry child,
ITransactionalOperation operation) {
// in case routing style is changed, clear the
// waypoints (chain into a composite operation)
if (operation instanceof SetPropertyValueOperation) {
SetPropertyValueOperation changeRoutingStyleOperation = (SetPropertyValueOperation) operation;
if (changeRoutingStyleOperation
.getPropertySource() instanceof GeometricCurvePropertySource
&& GeometricCurvePropertySource.ROUTING_STYLE_PROPERTY
.getId()
.equals(changeRoutingStyleOperation
.getPropertyId())) {
// clear way anchors using bend policy
GeometricCurvePropertySource ps = (GeometricCurvePropertySource) changeRoutingStyleOperation
.getPropertySource();
IContentPart<? extends Node> contentPart = getContentViewer()
.getContentPartMap().get(ps.getCurve());
// preserve first and last waypoint, but clear
// all intermediate points
List<Point> newWaypoints = new ArrayList<>();
List<Point> currentWaypoints = ps.getCurve()
.getWayPointsCopy();
// FIXME: Code copied from
// BendConnectionPolicy (see #494752)
newWaypoints.add(computeStartHint(
(Connection) contentPart.getVisual()));
newWaypoints.add(computeEndHint(
(Connection) contentPart.getVisual()));
ChangeWayPointsOperation clearWaypointsOperation = new ChangeWayPointsOperation(
"Clear waypoints", ps.getCurve(),
currentWaypoints, newWaypoints);
AbstractCompositeOperation c = new ForwardUndoCompositeOperation(
"Change routing style");
c.add(changeRoutingStyleOperation);
c.add(clearWaypointsOperation);
c.add(new UpdateSelectionHandlesOperation(
contentPart));
super.valueChanged(child, c);
} else {
super.valueChanged(child, operation);
}
} else {
super.valueChanged(child, operation);
}
}
};
propertySheetPage.setRootEntry(rootEntry);
}
return propertySheetPage;
} else if (IPropertySource.class.equals(key)) {
return new MvcLogoExampleViewPropertySource(this);
}
return super.getAdapter(key);
}
protected IViewer getPaletteViewer() {
return getDomain().getAdapter(AdapterKey.get(IViewer.class,
MvcLogoExampleModule.PALETTE_VIEWER_ROLE));
}
@Override
protected void hookViewers() {
// build viewers composite
MvcLogoExampleViewersComposite viewersComposite = new MvcLogoExampleViewersComposite(
getContentViewer(), getPaletteViewer());
// create scene and populate canvas
getCanvas().setScene(new Scene(viewersComposite.getComposite()));
}
}