blob: bd149bc2d3002e87c63bd3860c0a6cabc332036d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency.
* 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:
* Pierre Allard,
* Regent L'Archeveque,
* Olivier L. Larouche - initial API and implementation
*
* SPDX-License-Identifier: EPL-1.0
*******************************************************************************/
package org.eclipse.apogy.core.topology.ui.parts;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.eclipse.apogy.common.ApogyCommonOSGiUtilities;
import org.eclipse.apogy.common.emf.FeaturePathAdapter;
import org.eclipse.apogy.common.emf.impl.FeaturePathAdapterCustomImpl;
import org.eclipse.apogy.common.images.AbstractEImage;
import org.eclipse.apogy.common.images.ApogyCommonImagesFactory;
import org.eclipse.apogy.common.images.EImage;
import org.eclipse.apogy.common.images.EImagesUtilities;
import org.eclipse.apogy.common.topology.AbstractViewPoint;
import org.eclipse.apogy.common.topology.AbstractViewPointReference;
import org.eclipse.apogy.common.topology.ApogyCommonTopologyFactory;
import org.eclipse.apogy.common.topology.Node;
import org.eclipse.apogy.common.topology.ui.ApogyCommonTopologyUIFacade;
import org.eclipse.apogy.common.topology.ui.GraphicsContext;
import org.eclipse.apogy.common.topology.ui.NodeSelection;
import org.eclipse.apogy.common.topology.ui.jme3.JME3RenderEngineDelegate;
import org.eclipse.apogy.common.topology.ui.viewer.ApogyCommonTopologyUIViewerPackage;
import org.eclipse.apogy.common.topology.ui.viewer.TopologyViewer;
import org.eclipse.apogy.common.topology.ui.viewer.TopologyViewerProvider;
import org.eclipse.apogy.common.topology.ui.viewer.TopologyViewerRegistry;
import org.eclipse.apogy.common.ui.composites.NoContentComposite;
import org.eclipse.apogy.core.ApogyTopology;
import org.eclipse.apogy.core.environment.AbstractApogyEnvironmentItem;
import org.eclipse.apogy.core.environment.ApogyEnvironment;
import org.eclipse.apogy.core.environment.ViewPointList;
import org.eclipse.apogy.core.invocator.ApogyCoreInvocatorFacade;
import org.eclipse.apogy.core.invocator.ApogyCoreInvocatorPackage;
import org.eclipse.apogy.core.invocator.ui.ApogyCoreInvocatorUIFacade;
import org.eclipse.apogy.core.topology.ApogyCoreTopologyFacade;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.progress.UIJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractApogy3dPart extends AbstractApogyTopologyBasedPart implements TopologyViewerProvider {
private static final Logger Logger = LoggerFactory.getLogger(AbstractApogy3dPart.class);
protected static MPart activeAbstractApogy3dPartMPart = null;
public static final String VIEW_POINT_ID = "VIEW_POINT_ID";
private AbstractViewPoint abstractViewPoint;
private Node topologyRoot;
private String currentDirectory = System.getProperty("user.dir");
protected TopologyViewer topologyViewer;
private Adapter topologyTopologyViewerRegistryAdapter = null;
private FeaturePathAdapter variableInstanciationAdapter = null;
@Inject
Shell shell;
@Inject
EModelService modelService;
@Override
public void userPostConstruct(MPart mPart) {
// Reset the ViewPoint.
AbstractViewPointReference ref = (AbstractViewPointReference) ApogyCoreInvocatorUIFacade.INSTANCE
.readFromPersistedState(mPart, VIEW_POINT_ID);
if (ref != null) {
this.abstractViewPoint = ref.getAbstractViewPoint();
} else {
mPart.setLabel(getPartDisplayedName());
}
// Calls variablesInstanciated is variable are already instantiated.
if (ApogyCoreInvocatorFacade.INSTANCE.getInitVariableInstancesDate() != null) {
variablesInstanciated();
}
// Attaches listener to variable instantiation.
getVariableInstanciationAdapter().init(ApogyCoreInvocatorFacade.INSTANCE);
}
@Override
public void userPreDestroy(MPart mPart) {
getVariableInstanciationAdapter().dispose();
super.userPreDestroy(mPart);
}
@Override
protected void createContentComposite(Composite parent, int style) {
if (activeAbstractApogy3dPartMPart == null) {
JME3RenderEngineDelegate jme3RenderEngineDelegate = createJME3RenderEngineDelegate();
this.topologyViewer = new TopologyViewer(parent, jme3RenderEngineDelegate);
jme3RenderEngineDelegate.setMaximumFrameRate(this.topologyViewer.getMaximumFrameRate());
jme3RenderEngineDelegate.setAntiAliasing(this.topologyViewer.isAntiAliasingEnabled());
jme3RenderEngineDelegate.setShowStatisticsEnabled(this.topologyViewer.isShowStatisticsEnabled());
// Listens for selection in the 3D viewer.
org.eclipse.apogy.common.topology.ui.viewer.Activator.getTopologyViewerRegistry().eAdapters()
.add(getTopologyTopologyViewerRegistryAdapter());
setActiveViewPoint(this.abstractViewPoint);
activeAbstractApogy3dPartMPart = this.mPart;
} else {
new NoContentComposite(parent, SWT.None) {
@Override
protected String getMessage() {
MPerspective mPerspective = AbstractApogy3dPart.this.modelService
.getPerspectiveFor(activeAbstractApogy3dPartMPart);
String partLabel = activeAbstractApogy3dPartMPart.getLabel();
String elementId = mPerspective.getLabel();
return "3D Viewer is already instanciated in part <" + partLabel + "> in the perspective named <"
+ elementId + "> !";
}
};
}
}
@Override
public void userPersistState(MPart mPart) {
try {
if (this.abstractViewPoint != null) {
AbstractViewPointReference ref = ApogyCommonTopologyFactory.eINSTANCE
.createAbstractViewPointReference();
ref.setAbstractViewPoint(this.abstractViewPoint);
ApogyCoreInvocatorUIFacade.INSTANCE.saveToPersistedState(mPart, VIEW_POINT_ID, ref);
}
} catch (Throwable t) {
Logger.error(t.getMessage(), t);
}
}
@Override
public TopologyViewer getTopologyViewer() {
return this.topologyViewer;
}
/**
* Return the lable prefic of the part.
*
* @return
*/
public String getPartDisplayedName() {
return "3D Viewer";
}
/**
* Method called when the variable are instanciated.
*/
protected void variablesInstanciated() {
// Initialize the viewPoints.
initialiseViewPoints(this.topologyRoot);
}
@Override
protected void newTopology(ApogyTopology apogyTopology) {
if (activeAbstractApogy3dPartMPart == this.mPart) {
GraphicsContext graphicsContext = ApogyCommonTopologyUIFacade.INSTANCE
.createGraphicsContext(apogyTopology.getRootNode());
this.topologyViewer.setInput(graphicsContext);
if (apogyTopology != null) {
this.topologyRoot = apogyTopology.getRootNode();
}
// Initialize the view points.
initialiseViewPoints(this.topologyRoot);
// Reset the current ViewPoint.
AbstractViewPointReference ref = (AbstractViewPointReference) ApogyCoreInvocatorUIFacade.INSTANCE
.readFromPersistedState(this.mPart, VIEW_POINT_ID);
if (ref != null) {
this.abstractViewPoint = ref.getAbstractViewPoint();
setActiveViewPoint(this.abstractViewPoint);
}
}
}
@Override
public void dispose() {
// Unregister listener to 3D selection.
org.eclipse.apogy.common.topology.ui.viewer.Activator.getTopologyViewerRegistry().eAdapters()
.remove(getTopologyTopologyViewerRegistryAdapter());
// Calls super
super.dispose();
if (activeAbstractApogy3dPartMPart == this.mPart) {
activeAbstractApogy3dPartMPart = null;
}
}
/*
* Methods that create the JME3RenderEngineDelegate to be used.
*
* @return The JME3RenderEngineDelegate to be used.
*/
protected abstract JME3RenderEngineDelegate createJME3RenderEngineDelegate();
/*
* Sets the actual zoom to display the entire loaded scene.
*/
public void zoomToFit() {
getTopologyViewer().zoomToFit();
}
/*
* Enables or disables anti-aliasing rendering.
*
* @param enable true enables the anti-aliasing.
*/
public void setAntiAliasing(boolean enable) {
getTopologyViewer().setAntiAliasing(enable);
}
/*
* Enables or disables high speed motion while using the mouse.
*
* @param enable true enables high speed.
*/
public void setHighSpeedMode(boolean enable) {
getTopologyViewer().setHighSpeedMotionEnabled(enable);
}
/*
* Enables or disables the picking mode.
*
* @param enable true enables the picking mode.
*/
public void setPickingMode(boolean enable) {
getTopologyViewer().setPickingModeEnabled(enable);
}
/*
* Shows of hides the renderer statistics.
*
* @param enable true enables the statistics display.
*/
public void showStatisticsDisplay(boolean enable) {
getTopologyViewer().setShowStatisticsEnabled(enable);
}
/*
* Resets the horizontal view point.
*/
public void resetLevelViewpoint() {
getTopologyViewer().levelViewPoint();
}
/*
* Takes a screenshot and prompts the user to identify the file type and the
* filename.
*/
public void takeScreenshot() {
getTopologyViewer().takeScreenshot();
BufferedImage image = getTopologyViewer().takeScreenshot();
if (image != null) {
EImage eImage = ApogyCommonImagesFactory.eINSTANCE.createEImage();
eImage.setImageContent(image);
if (eImage != null) {
// Open pop-up and save.
FileDialog fileChooser = new FileDialog(this.shell, SWT.SAVE);
fileChooser.setText("Saves image to file");
fileChooser.setFilterPath(this.currentDirectory);
fileChooser.setFilterExtensions(new String[] { "*.png;" });
String filename = fileChooser.open();
if (filename != null) {
try {
if (filename.endsWith(".png")) {
saveImageInJob(eImage, filename, SWT.IMAGE_PNG);
} else {
String fileExtension = "";
if (filename.lastIndexOf(".") > 0) {
fileExtension = filename.substring(filename.lastIndexOf("."));
}
String message = "Failed to save the image. The specified file extension <" + fileExtension
+ "> is not supported.";
Logger.error(message);
MessageBox messageBox = new MessageBox(this.shell, SWT.ICON_ERROR | SWT.OK);
messageBox.setMessage(message);
messageBox.open();
}
} catch (Exception e) {
Logger.error("Unable to save image to file <" + filename + ">.");
}
this.currentDirectory = fileChooser.getFilterPath();
}
}
}
}
/**
* Sets the currently active View Point
*
* @param abstractViewPoint The view point to be displayed.
*/
public void setActiveViewPoint(AbstractViewPoint abstractViewPoint) {
this.abstractViewPoint = abstractViewPoint;
if (abstractViewPoint != null) {
this.mPart.setLabel(getPartDisplayedName() + " - " + abstractViewPoint.getName());
if (ApogyCoreTopologyFacade.INSTANCE.getApogyTopology() != null) {
Node root = ApogyCoreTopologyFacade.INSTANCE.getApogyTopology().getRootNode();
abstractViewPoint.initialize(root);
}
} else {
this.mPart.setLabel(getPartDisplayedName());
}
if (getTopologyViewer() != null && abstractViewPoint != null) {
getTopologyViewer().setActiveViewPoint(abstractViewPoint);
}
}
/**
* Returns the currently active View Point
*
* @return The view point displayed, can be null.
*/
public AbstractViewPoint getActiveViewPoint() {
return getTopologyViewer().getActiveViewPoint();
}
/**
* Method that ensures the viewpoint in the list are initialized and the
* selected one properly displayed.
*
* @param root The root of the topology being displayed.
*/
protected void initialiseViewPoints(Node root) {
if (ApogyCoreInvocatorFacade.INSTANCE.getActiveInvocatorSession() != null) {
if (ApogyCoreInvocatorFacade.INSTANCE.getActiveInvocatorSession()
.getEnvironment() instanceof ApogyEnvironment) {
ApogyEnvironment apogyEnvironment = (ApogyEnvironment) ApogyCoreInvocatorFacade.INSTANCE
.getActiveInvocatorSession().getEnvironment();
EList<AbstractApogyEnvironmentItem> items = apogyEnvironment.getEnvironmentItems();
ViewPointList viewPointList = null;
for (AbstractApogyEnvironmentItem item : items) {
if (item instanceof ViewPointList) {
viewPointList = (ViewPointList) item;
}
}
// If ViewPointList has been found.
if (viewPointList != null) {
for (AbstractViewPoint vp : viewPointList.getViewPoints()) {
vp.initialize(root);
}
}
}
// Forces update of the current viewpoint.
setActiveViewPoint(this.abstractViewPoint);
}
}
/*
* Saves the current image to a file. This is done in a UIJob to keep the
* desktop responsive in case of a long save process.
*
* @param fileName The destination file where to save the image.
*
* @param fileType The file type : SWT.IMAGE_JPEG or SWT.IMAGE_BMP or ...
*/
private void saveImageInJob(final AbstractEImage image, final String fileName, final int fileType) {
UIJob job = new UIJob("Save Image to file <" + fileName + ">") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
try {
// Save image.
if (fileType == SWT.IMAGE_JPEG) {
EImagesUtilities.INSTANCE.saveImageAsJPEG(fileName, image);
} else if (fileType == SWT.IMAGE_PNG) {
EImagesUtilities.INSTANCE.saveImageAsPNG(fileName, image);
}
Logger.info("Sucessfully saved image to file <" + fileName + ">.");
Status status = new Status(IStatus.OK, ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(this.getClass()),
"Sucessfully saved image to file <" + fileName + ">.");
return status;
} catch (Exception e) {
String message = "Failed to save the image to file <" + fileName
+ ">. The following exception was thrown : \n\n ";
Logger.error(message, e);
Status status = new Status(IStatus.ERROR, ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(this.getClass()), message, e);
return status;
}
}
};
job.schedule();
}
private Adapter getTopologyTopologyViewerRegistryAdapter() {
if (this.topologyTopologyViewerRegistryAdapter == null) {
this.topologyTopologyViewerRegistryAdapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
if (msg.getNotifier() instanceof TopologyViewerRegistry) {
int featureId = msg.getFeatureID(TopologyViewerRegistry.class);
switch (featureId) {
case ApogyCommonTopologyUIViewerPackage.TOPOLOGY_VIEWER_REGISTRY__LATEST_NODE_SELECTION:
if (msg.getNewValue() instanceof NodeSelection) {
NodeSelection nodeSelection = (NodeSelection) msg.getNewValue();
AbstractApogy3dPart.this.selectionService.setSelection(nodeSelection);
}
break;
default:
break;
}
}
}
};
}
return this.topologyTopologyViewerRegistryAdapter;
}
private FeaturePathAdapter getVariableInstanciationAdapter() {
if (this.variableInstanciationAdapter == null) {
this.variableInstanciationAdapter = new FeaturePathAdapterCustomImpl() {
@Override
public List<? extends EStructuralFeature> getFeatureList() {
List<EStructuralFeature> features = new ArrayList<>();
features.add(
ApogyCoreInvocatorPackage.Literals.APOGY_CORE_INVOCATOR_FACADE__INIT_VARIABLE_INSTANCES_DATE);
return features;
}
@Override
public void notifyChanged(Notification msg) {
variablesInstanciated();
}
};
}
return this.variableInstanciationAdapter;
}
}