/******************************************************************************* * 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 org.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.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.widgets.Composite; 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.processeditor.Activator; import org.simantics.processeditor.ProcessResource; import org.simantics.processeditor.actions.InsertComponentAction; import org.simantics.processeditor.actions.InsertEquipmentAction; import org.simantics.processeditor.actions.InsertNozzleAction; import org.simantics.processeditor.actions.RoutePipeAction; import org.simantics.processeditor.common.ControlPointTools; import org.simantics.processeditor.common.PipingRules; import org.simantics.processeditor.dialogs.FloorConfigureDialog; import org.simantics.processeditor.scenegraph.NonVisibleNode; import org.simantics.processeditor.scenegraph.PipeComponentNode; import org.simantics.processeditor.scenegraph.PipeRunNode; import org.simantics.processeditor.scenegraph.PipelineComponentNode; import org.simantics.processeditor.stubs.PipeControlPoint; import org.simantics.processeditor.stubs.PipeRun; import org.simantics.processeditor.stubs.Plant; import org.simantics.processeditor.stubs.Plant3DResource; import org.simantics.processeditor.tools.PlantEditContribution; import org.simantics.processeditor.tools.PlantVisualizationContribution; 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.ui.ErrorLogger; import org.simantics.utils.ui.jface.MenuTools; import com.jme.math.Vector3f; import com.jme.scene.Geometry; public class ProcessEditor extends ThreeDimensionalEditorBase { private Resource plantResource = null; 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 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 NormalScenegraphQuery extends ScenegraphQuery { public NormalScenegraphQuery(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 NormalScenegraphQuery(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); } @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); } } } 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); } } } @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); } } } } @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; } }