blob: dc5a5f5477c55898821dc50e89d2e5f1c34b22ad [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 - 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.HashMap;
import javax.inject.Inject;
import org.eclipse.apogy.common.ApogyCommonOSGiUtilities;
import org.eclipse.apogy.common.emf.ui.parts.AbstractEObjectSelectionPart;
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.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.JME3Application;
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.ApogySystem;
import org.eclipse.apogy.core.topology.ui.ApogyCoreTopologyUIFacade;
import org.eclipse.apogy.core.topology.ui.ApogyCoreTopologyUIPackage;
import org.eclipse.apogy.core.topology.ui.ApogyCoreTopologyUIRCPConstants;
import org.eclipse.apogy.core.topology.ui.DisplayedTopology;
import org.eclipse.apogy.core.topology.ui.DisplayedTopologyChoice;
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.basic.MPart;
import org.eclipse.e4.ui.workbench.modeling.ISelectionListener;
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.ecore.EObject;
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;
import com.jme3.math.ColorRGBA;
public class ApogySystem3dPart extends AbstractEObjectSelectionPart implements TopologyViewerProvider {
private static final Logger Logger = LoggerFactory.getLogger(ApogySystem3dPart.class);
public static ColorRGBA VIEWER_BACKGROUND_COLOR = new ColorRGBA(145.0f / 255.0f, 233.0f / 255.0f, 1f, 1f);
protected ApogySystem apogySystem;
protected DisplayedTopology displayedTopology = DisplayedTopology.ASSEMBLY;
protected TopologyViewer topologyViewer;
private Adapter topologyTopologyViewerRegistryAdapter = null;
private Adapter editedApogySystemAssemblyRootAdapter = null;
private String currentDirectory = System.getProperty("user.dir");
@Inject
Shell shell;
@Override
public TopologyViewer getTopologyViewer() {
return this.topologyViewer;
}
/*
* 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();
}
}
}
}
@Override
protected void setCompositeContents(EObject eObject) {
if (eObject instanceof ApogySystem) {
setApogySystem((ApogySystem) eObject);
} else if (eObject instanceof DisplayedTopologyChoice) {
DisplayedTopologyChoice displayedTopologyChoice = (DisplayedTopologyChoice) eObject;
setDisplayedTopology(displayedTopologyChoice.getDisplayedTopology());
}
}
@Override
protected HashMap<String, ISelectionListener> getSelectionProvidersIdsToSelectionListeners() {
HashMap<String, ISelectionListener> selectionProvidersIdsToSelectionListeners = new HashMap<String, ISelectionListener>();
// Selection from Apogy System File Editor
selectionProvidersIdsToSelectionListeners.put(ApogyCoreTopologyUIRCPConstants.PART__APOGY_SYSTEM_FILE_EDITOR_ID,
new ISelectionListener() {
@Override
public void selectionChanged(MPart mPart, Object object) {
if (object != null && object instanceof EObject) {
setEObject((EObject) object);
}
}
});
selectionProvidersIdsToSelectionListeners
.put(ApogyCoreTopologyUIRCPConstants.PART_APOGY_SYSTEM_TOPOLOGY_EDITOR_ID, new ISelectionListener() {
@Override
public void selectionChanged(MPart mPart, Object object) {
if (object != null && object instanceof EObject) {
setEObject((EObject) object);
}
}
});
return selectionProvidersIdsToSelectionListeners;
}
@Override
public void userPostConstruct(MPart mPart) {
ApogyCoreTopologyUIFacade.INSTANCE.eAdapters().add(getEditedApogySystemAssemblyRootAdapter());
super.userPostConstruct(mPart);
}
@Override
public void userPreDestroy(MPart mPart) {
ApogyCoreTopologyUIFacade.INSTANCE.eAdapters().remove(getEditedApogySystemAssemblyRootAdapter());
super.userPreDestroy(mPart);
}
protected void setApogySystem(ApogySystem newApogySystem) {
if (this.apogySystem != newApogySystem) {
this.apogySystem = newApogySystem;
updateDisplayedTopology();
}
}
protected void setDisplayedTopology(DisplayedTopology newDisplayedTopology) {
if (this.displayedTopology != newDisplayedTopology) {
this.displayedTopology = newDisplayedTopology;
updateDisplayedTopology();
}
}
private void updateDisplayedTopology() {
Node root = null;
switch (this.displayedTopology.getValue()) {
case DisplayedTopology.SYSTEM_VALUE: {
if (this.apogySystem != null && this.apogySystem.getTopologyRoot() != null) {
root = this.apogySystem.getTopologyRoot().getOriginNode();
}
}
break;
case DisplayedTopology.ASSEMBLY_VALUE: {
root = ApogyCoreTopologyUIFacade.INSTANCE.getEditedApogySystemAssemblyRoot();
}
break;
default:
break;
}
if (root == null) {
root = ApogyCommonTopologyFactory.eINSTANCE.createAggregateContentNode();
}
if (this.topologyViewer != null) {
GraphicsContext graphicsContext = ApogyCommonTopologyUIFacade.INSTANCE.createGraphicsContext(root);
this.topologyViewer.setInput(graphicsContext);
}
}
@Override
protected void createNoContentComposite(Composite parent, int style) {
new NoContentComposite(parent, SWT.None) {
@Override
protected String getMessage() {
return "No Apogy System Topology to display.";
}
};
}
@Override
protected void createContentComposite(Composite parent, int style) {
JME3RenderEngineDelegate jme3RenderEngineDelegate = createJME3RenderEngineDelegate();
this.topologyViewer = new TopologyViewer(parent, jme3RenderEngineDelegate);
jme3RenderEngineDelegate.setMaximumFrameRate(this.topologyViewer.getMaximumFrameRate());
jme3RenderEngineDelegate.setAntiAliasing(this.topologyViewer.isAntiAliasingEnabled());
jme3RenderEngineDelegate.setShowStatisticsEnabled(this.topologyViewer.isShowStatisticsEnabled());
jme3RenderEngineDelegate.setAmbientLightEnabled(true);
// Listens for selection in the 3D viewer.
org.eclipse.apogy.common.topology.ui.viewer.Activator.getTopologyViewerRegistry().eAdapters()
.add(getTopologyTopologyViewerRegistryAdapter());
}
protected JME3RenderEngineDelegate createJME3RenderEngineDelegate() {
JME3RenderEngineDelegate engineDelegate = new JME3RenderEngineDelegate() {
@Override
protected JME3Application createJME3Application(Composite parent) {
JME3Application app = new JME3Application(parent) {
@Override
public void simpleInitApp() {
super.simpleInitApp();
// Sets the far clipping plane to 200m.
getCamera().setFrustumFar(100f);
getViewPort().setBackgroundColor(VIEWER_BACKGROUND_COLOR);
}
@Override
protected void initLighting() {
super.initLighting();
}
};
app.setJMERenderEngineDelegate(this);
return app;
}
};
return engineDelegate;
}
protected Adapter getEditedApogySystemAssemblyRootAdapter() {
if (this.editedApogySystemAssemblyRootAdapter == null) {
this.editedApogySystemAssemblyRootAdapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
if (msg.getNotifier() instanceof ApogyCoreTopologyUIFacade) {
int featureId = msg.getFeatureID(ApogyCoreTopologyUIFacade.class);
if (featureId == ApogyCoreTopologyUIPackage.APOGY_CORE_TOPOLOGY_UI_FACADE__EDITED_APOGY_SYSTEM_ASSEMBLY_ROOT) {
updateDisplayedTopology();
}
}
}
};
}
return this.editedApogySystemAssemblyRootAdapter;
}
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();
ApogySystem3dPart.this.selectionService.setSelection(nodeSelection);
}
break;
default:
break;
}
}
}
};
}
return this.topologyTopologyViewerRegistryAdapter;
}
/*
* 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(ApogySystem3dPart.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 " + e.getMessage();
Logger.error(message, e);
Status status = new Status(IStatus.ERROR, ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(ApogySystem3dPart.this.getClass()), message, e);
return status;
}
}
};
job.schedule();
}
}