/******************************************************************************* * Copyright (c) 2007 VTT Technical Research Centre of Finland 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package fi.vtt.simantics.processeditor.views; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.simantics.db.Graph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.management.ISessionContext; import org.simantics.layer0.utils.EntityFactory; import org.simantics.layer0.utils.IEntity; import org.simantics.layer0.utils.Property; import org.simantics.proconf.g3d.base.JmeRenderingComponent; import org.simantics.proconf.g3d.base.ScenegraphAdapter; import org.simantics.proconf.g3d.base.ScenegraphAdapterImpl; import org.simantics.proconf.g3d.base.SelectionAdapter; import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase; import org.simantics.proconf.g3d.common.StructuredResourceSelection; import org.simantics.proconf.g3d.scenegraph.IGeometryNode; import org.simantics.proconf.g3d.scenegraph.IGraphicsNode; import org.simantics.proconf.g3d.scenegraph.ISelectableNode; import org.simantics.proconf.g3d.scenegraph.ParameterizedModelNode; import org.simantics.proconf.g3d.shapes.FloorShape; import org.simantics.proconf.g3d.stubs.G3DNode; import org.simantics.utils.ErrorLogger; import org.simantics.utils.ui.jface.MenuTools; import com.jme.math.Vector3f; import com.jme.scene.Geometry; import fi.vtt.simantics.processeditor.Activator; import fi.vtt.simantics.processeditor.ProcessResource; import fi.vtt.simantics.processeditor.actions.InsertComponentAction; import fi.vtt.simantics.processeditor.actions.InsertEquipmentAction; import fi.vtt.simantics.processeditor.actions.InsertNozzleAction; import fi.vtt.simantics.processeditor.actions.RoutePipeAction; import fi.vtt.simantics.processeditor.common.ControlPointTools; import fi.vtt.simantics.processeditor.common.PipingRules; import fi.vtt.simantics.processeditor.scenegraph.NonVisibleNode; import fi.vtt.simantics.processeditor.scenegraph.PipelineComponentNode; import fi.vtt.simantics.processeditor.scenegraph.PipeComponentNode; import fi.vtt.simantics.processeditor.scenegraph.PipeRunNode; import fi.vtt.simantics.processeditor.stubs.PipeControlPoint; import fi.vtt.simantics.processeditor.stubs.PipeRun; import fi.vtt.simantics.processeditor.stubs.Plant; import fi.vtt.simantics.processeditor.stubs.Plant3DResource; import fi.vtt.simantics.processeditor.tools.PlantEditContribution; import fi.vtt.simantics.processeditor.tools.PlantVisualizationContribution; public class ProcessEditor extends ThreeDimensionalEditorBase { private Resource plantResource = null; //private List animationControllers = new ArrayList(); private Action configureFloorAction = null; private Geometry floorShape = null; public ProcessEditor(ISessionContext session) { super(session); addEditorContribution(new PlantEditContribution(this)); addEditorContribution(new PlantVisualizationContribution(this)); } public ProcessEditor(ISessionContext session,JmeRenderingComponent component) { super(session,component); addEditorContribution(new PlantEditContribution(this)); addEditorContribution(new PlantVisualizationContribution(this)); } @Override protected ScenegraphAdapter createScenegraphAdapter() { return new ProcessEditorAdapter(session,getRenderingComponent()); } @Override public void createControl(Graph graph,Composite parent) { super.createControl(graph,parent); floorShape = FloorShape.getShape(getRenderingComponent().getDisplaySystem().getRenderer(), 100.f,0.2f); getRenderingComponent().getNoCastRoot().attachChild(floorShape); floorShape.setLocalTranslation(new Vector3f(0.f,-0.01f,0.f)); } @Override protected void makeActions(Graph graph) { super.makeActions(graph); //actions.add(new ShowTrendsAction(this)); configureFloorAction = new Action() { public void run() { FloorConfigureDialog dialog = new FloorConfigureDialog(ProcessEditor.this.parent.getShell()); if (dialog.open() == FloorConfigureDialog.CANCEL) return; if (dialog.isFloorEnabled()) { if (floorShape.getParent() == null) getRenderingComponent().getNoCastRoot().attachChild(floorShape); } else { floorShape.removeFromParent(); } floorShape.setLocalTranslation(new Vector3f(0.f,(float)dialog.getFloorHeight(),0.f)); } }; configureFloorAction.setText("Configure floor"); configureFloorAction.setImageDescriptor(Activator.imageDescriptorFromPlugin("fi.vtt.proconf.ode", "icons/silk/shape_align_bottom.png")); // ContextActionFactory extended[] = ContextActionRegistry.getActions("fi.vtt.proconf.shapeeditor.processeditorview"); // for (ContextActionFactory c : extended) { // actions.add(c.createAction(this)); // } } // protected void stopAnimations() { // animationSystem.stop(); // } protected void fillLocalPullDown() { super.fillLocalPullDown(); MenuTools.getOrCreate(getMenuID(),"Advanced", menuManager).add(configureFloorAction); } protected class ProcessEditorAdapter extends ScenegraphAdapterImpl { public ProcessEditorAdapter(Session session, JmeRenderingComponent component) { super(session, component); } private class NormalScengraphQuery extends ScenegraphQuery { public NormalScengraphQuery(Resource node) { super(node); } @Override public void shapeAdded(Graph graph, IGraphicsNode node) { // FIXME : this won't work like in previous ProConf } } private Map pipeRunQueries = new HashMap(); protected ScenegraphQuery newSubnodeListener(G3DNode node) { if (node.isInstanceOf(ProcessResource.plant3Dresource.PipeRun)) { PipeRunControlPointQuery query = new PipeRunControlPointQuery(node.getResource()); pipeRunQueries.put(node.getResource(), query); node.getGraph().performQuery(query); // return new SubnodeListener(node) { // @Override // public void shapeAdded(IGraphicsNode node) { // if (node instanceof IGeometryNode) { // updateGeometry((IGeometryNode)node); // // } // node.setVisible(true); // } // }; } return new NormalScengraphQuery(node.getResource()); } @Override protected NodePropertyQuery newRootPropertyListener(G3DNode root) { // currently Plant does not have any properties. return null; } private class TransformationQuery extends NodeTransformationQuery { public TransformationQuery(Resource res) { super(res); } @Override public void shapeUpdated(Graph graph, IGraphicsNode shape) { //if (shape instanceof IGeometryNode) { // updateGeometry((IGeometryNode)shape); //} else { shape.updateTransform(graph); //} } } @Override protected NodeTransformationQuery newTransformationListener(G3DNode root) { return new TransformationQuery(root.getResource()); } private class NormalNodePropertyQuery extends org.simantics.proconf.g3d.base.ScenegraphAdapterImpl.NodePropertyQuery { public NormalNodePropertyQuery(Resource resource) { super(resource); } @Override public void shapeUpdated(Graph graph,IGraphicsNode shape) { if (shape instanceof IGeometryNode) { updateGeometry((IGeometryNode)shape); } else { shape.updateTransform(graph); } } } @Override protected NodePropertyQuery newPropertyListener(G3DNode node) { return new NormalNodePropertyQuery(node.getResource()); } @Override protected IGraphicsNode instantiateNode(IGraphicsNode parent, G3DNode node) { Plant3DResource p3r = ProcessResource.plant3Dresource; IGraphicsNode newNode = null; try { if (node.isInstanceOf(p3r.Equipment)) { newNode = new ParameterizedModelNode( ProcessEditor.this, parent, node.getGraph(), node.getResource(), p3r.HasGraphics); } else if (node.isInstanceOf(p3r.PipeRun)) { newNode = new PipeRunNode(parent, node.getGraph(), node.getResource()); } else if (node.isInstanceOf(p3r.Nozzle)) { newNode = new ParameterizedModelNode( ProcessEditor.this, parent, node.getGraph(), node.getResource(), p3r.HasGraphics); // CodedComponent must be handled first since it uses // hard-coded geometries // TODO : is this really necessary, or could we unify // PipeComponentNode, InlineComponentNode,... } else if (node.isInstanceOf(p3r.CodedComponent)) { newNode = new PipeComponentNode(ProcessEditor.this, parent, node.getGraph(), node.getResource()); } else if (node.isInstanceOf(p3r.NonVisibleComponent)) { newNode = new NonVisibleNode(parent, node.getGraph(), node.getResource()); } else if (node.isInstanceOf(p3r.PipelineComponent)) { newNode = new PipelineComponentNode(ProcessEditor.this, parent, node.getGraph(), node.getResource()); } // } else if (node instanceof Shape) // Markers (ar/mobile) // needed this // newNode = new ShapeNode(TestProcessEditor.this,parent,node); if (newNode != null) { if (newNode instanceof ISelectableNode) ((ISelectableNode) newNode).setVisible(true); if (newNode instanceof IGeometryNode) { updateGeometry((IGeometryNode) newNode); } return newNode; } } catch (Exception e) { ErrorLogger.defaultLogError("Cannot handle node " + node.getResource(), e); return null; } ErrorLogger.defaultLogError("Cannot handle node " + node.getResource(), null); return null; } /** * This is used to create elbows and straight pipes to pipeline TODO : * this should be done with rule-engine! * * * @author Marko Luukkainen * */ protected class PipeRunControlPointQuery extends NodeQuery { private List removed = new ArrayList(); private List added = new ArrayList(); public PipeRunControlPointQuery(Resource r) { super(r); if (DEBUG) System.out.println("Created PipeRunControlPointQuery for " + r); } @Override protected Object compute2(Graph graph) { PipeRun run = new PipeRun(graph, nodeResource); Collection cps = run .getRelatedObjects(ProcessResource.plant3Dresource.HasControlPoints); List res = new ArrayList(); for (IEntity t : cps) res.add(t.getResource()); return res; } @Override public boolean updated(Graph graph, Object oldResult, Object newResult) { removed.clear(); added.clear(); List oldCps = (List) oldResult; List newCps = (List) newResult; if (oldCps == null) oldCps = new ArrayList(); for (Resource r : oldCps) { if (!newCps.contains(r)) removed.add(r); } for (Resource r : newCps) { if (!oldCps.contains(r)) added.add(r); } for (Resource r : removed) removeControlPoint(graph, r); for (Resource r : added) { addControlPoint(graph, r); // ControlPointTools.addControlPoint(new // PipeRun(graph,pipeRun), new PipeControlPoint(graph, r)); } return (added.size() > 0 || removed.size() > 0); } @Override public void dispose() { super.dispose(); for (ControlPointPropertyQuery q : controlPointPropertyQueries.values()) q.dispose(); controlPointPropertyQueries.clear(); } private Map controlPointPropertyQueries = new HashMap(); private void addControlPoint(Graph graph, Resource resource) { ControlPointPropertyQuery query = new ControlPointPropertyQuery(resource); graph.performQuery(query); controlPointPropertyQueries.put(resource,query); } private void removeControlPoint(Graph graph, Resource resource) { ControlPointPropertyQuery query = controlPointPropertyQueries.remove(resource); query.dispose(); ControlPointTools.removeControlPoint(new PipeControlPoint( graph, resource)); } } protected class ControlPointPropertyQuery extends NodeQuery { boolean initialized = false; public ControlPointPropertyQuery(Resource r) { super(r); if (DEBUG) System.out.println("Created ControlPointPropertyQuery for " + r); } @Override public List compute2(Graph g) { IEntity t = EntityFactory.create(g,nodeResource); Collection properties = t.getRelatedProperties(ProcessResource.builtins.HasProperty); List propertyValues = new ArrayList(); p(properties,propertyValues); return propertyValues; } private void p(Collection properties, List propertyValues) { for (Property p : properties) { Collection subProperties = p.getRelatedProperties(p.getGraph().getBuiltins().HasProperty); if (subProperties.size() != 0) { p(subProperties,propertyValues); } if (p.hasValue()){ propertyValues.add(p.getValue()); } } } @Override public boolean updated(Graph graph, Object oldResult, Object newResult) { PipingRules.pipeControlPointPositionUpdate(graph, this.nodeResource); if (initialized) { //PipingRules.pipeControlPointPositionUpdate(graph, this.nodeResource); } else { initialized = true; } return true; } } @Override protected void removeNode(Resource parent, Resource r) { super.removeNode(parent, r); PipeRunControlPointQuery q = pipeRunQueries.get(r); if (q != null) q.dispose(); } @Override public void dispose() { super.dispose(); } } @Override protected void pageSelectionChanged(IWorkbenchPart part, ISelection selection) { if (!(selection instanceof StructuredResourceSelection)) { return; } StructuredResourceSelection s = (StructuredResourceSelection) selection; selectionAdapter.setCurrentSelection(s); viewChanged = true; //if (s.getRootSelection() == null) { if (!(part instanceof ProcessEditor)) { //System.out.println("ShapeEditorView.pageSelectionChanged() no root selection"); ((ProcessEditorSelectionAdapter)selectionAdapter).setEditorSelection(true); return; } //if (!s.getRootSelection().getResource().getId().equals(plant.getResource().getId())) { ProcessEditor sender = (ProcessEditor)part; if (!sender.getPlantResource().equals(plantResource)) { // System.out.println("ShapeEditorView.pageSelectionChanged() not right group " // + s.getRootSelection().getResource().getId() + " != " + model.getResource().getId()); selectionAdapter.setCurrentSelection(new StructuredResourceSelection()); ((ProcessEditorSelectionAdapter)selectionAdapter).setEditorSelection(false); return; } selectionAdapter.setEditorSelection(); } @Override protected void reloadFrom(IEntity thing) { if (plantResource != null) { throw new UnsupportedOperationException("Reloading instantiated viewer not supported"); } if (thing.isInstanceOf(ProcessResource.plant3Dresource.Plant)) { plantResource = thing.getResource(); G3DNode plant = new G3DNode(thing); adapter.setRootNode(plant); //adapter.addOutbound(plant); ControlPointTools.reloadCache(thing.getGraph(),plant.getResource()); } else { throw new IllegalArgumentException("Resource is not a plant"); } } public Resource getPlantResource() { return plantResource; } public Plant getPlant(Graph g) { return new Plant(g, plantResource); } @Override protected SelectionAdapter createSelectionAdapter() { return new ProcessEditorSelectionAdapter(adapter); } protected class ProcessEditorSelectionAdapter extends SelectionAdapter { public ProcessEditorSelectionAdapter(ScenegraphAdapter adapter) { super(adapter); // TODO Auto-generated constructor stub } @Override protected StructuredResourceSelection filterSelection(ISelection s) { if (!(s instanceof StructuredResourceSelection)) return new StructuredResourceSelection(); return (StructuredResourceSelection)s; } @Override public void setEditorSelection() { List sel = getSelectedObjects(); for (IGraphicsNode o : adapter.getNodes()) if (o instanceof ISelectableNode) { if (sel.contains(o)) { ((ISelectableNode)o).setSelected(true); } else { ((ISelectableNode)o).setSelected(false); } } List selected = getSelectedResources(); // TODO : don't know why this code is here, but it seems unnecessary // for (Resource r : selected) { // if (!adapter.hasNode(r)) { // // instantiating a new resource : usin this editor's tc // Resource resource = graph.getResource(r.getId()); // adapter.addInbound(resource).setSelected(true); // // } // } } public void setEditorSelection(boolean addShapes) { List sel = getSelectedObjects(); for (IGraphicsNode o : adapter.getNodes()) if (o instanceof ISelectableNode) { if (sel.contains(o)) { ((ISelectableNode)o).setSelected(true); } else { ((ISelectableNode)o).setSelected(false); } } if (addShapes) { // TODO : don't know why this code is here, but it seems unnecessary // List selected = getSelectedResources(); // for (Resource r : selected) { // if (!adapter.hasNode(r)) { // if (r.isInstanceOf(GlobalIdMap.get(PSK3DModelingOntologyMapping.EQUIPMENT))) { // Resource group = GraphicsNodeTools.getModelFromResource(r); // if (group != null && group.getId() == plant.getResource().getId()) { //// instantiating a new resource : usin this editor's tc // Resource resource = graph.getResource(r.getId()); // adapter.addInbound(resource).setSelected(true); // } // // } // } // } } } @Override protected void setEditorHighlightSelection() { List sel = getInteractiveSelectedObjects(); for (IGraphicsNode o : adapter.getNodes()) if (o instanceof ISelectableNode) { if (sel.contains(o)) { ((ISelectableNode)o).setHighlighted(true); } else { ((ISelectableNode)o).setHighlighted(false); } } } } private class FloorConfigureDialog extends Dialog implements KeyListener,SelectionListener { private boolean floorEnabled = true; private double floorHeight = 0.0; private Text floorHeightText = null; private Button floorEnabledButton = null; public FloorConfigureDialog(Shell shell) { super(shell); } @Override protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); Label label = new Label(composite, SWT.WRAP); label.setText("Configure floor"); GridData data = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH); label.setLayoutData(data); label.setFont(parent.getFont()); floorEnabledButton = new Button(composite,SWT.CHECK); floorEnabledButton.setText("Enabled"); label = new Label(composite, SWT.WRAP); label.setText("Height"); label.setLayoutData(data); label.setFont(parent.getFont()); floorHeightText = new Text(composite,SWT.NONE); floorHeightText.addKeyListener(this); floorEnabledButton.addSelectionListener(this); floorEnabledButton.setSelection(floorEnabled); floorHeightText.setText(Double.toString(floorHeight)); return composite; } @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText("Configure floor"); } public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { boolean ok = true; try { floorHeight = Double.parseDouble(floorHeightText.getText()); } catch (NumberFormatException err) { ok = false; } if (ok) { this.getButton(IDialogConstants.OK_ID).setEnabled(true); } else { this.getButton(IDialogConstants.OK_ID).setEnabled(false); } } public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { floorEnabled = floorEnabledButton.getSelection(); } public boolean isFloorEnabled() { return floorEnabled; } public double getFloorHeight() { return floorHeight; } } @Override protected void hookDragAndDrop() { super.hookDragAndDrop(); dropTarget.addDropListener(new InsertEquipmentAction(this)); dropTarget.addDropListener(new InsertNozzleAction(this)); dropTarget.addDropListener(new InsertComponentAction(this)); dropTarget.addDropListener(new RoutePipeAction(this)); } @Override public Object getAdapter(Class adapter) { if (adapter == IContentOutlinePage.class) { if (getPlantResource() == null) return null; final PlantStructureOutlinePage page = new PlantStructureOutlinePage(sessionContext,getPlantResource()); getSelectionAdapter().addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { page.setSelection(event.getSelection()); } }); parent.getDisplay().asyncExec(new Runnable() { @Override public void run() { page.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { selectionAdapter.setSelection(SelectionAdapter.transformSelection(event.getSelection())); } }); } }); return page; } return null; } }