/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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.modeling.ui.sg; import java.awt.Color; import java.awt.event.AWTEventListener; import org.eclipse.core.runtime.NullProgressMonitor; import org.simantics.Simantics; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.ResourceArray; import org.simantics.db.common.primitiverequest.PossibleAdapter; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ManyObjectsForFunctionalRelationException; import org.simantics.db.exception.NoSingleResultException; import org.simantics.db.exception.ServiceException; import org.simantics.db.management.ISessionContext; import org.simantics.db.request.Read; import org.simantics.diagram.adapter.DefaultConnectionClassFactory; import org.simantics.diagram.adapter.FlagClassFactory; import org.simantics.diagram.adapter.GraphToDiagramSynchronizer; import org.simantics.diagram.handler.CopyPasteHandler; import org.simantics.diagram.handler.CopyPasteStrategy; import org.simantics.diagram.handler.DefaultCopyPasteStrategy; import org.simantics.diagram.handler.DeleteHandler; import org.simantics.diagram.handler.SimpleElementTransformHandler; import org.simantics.diagram.query.DiagramRequests; import org.simantics.diagram.runtime.RuntimeDiagramManager; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.synchronization.CopyAdvisor; import org.simantics.diagram.synchronization.SynchronizationHints; import org.simantics.diagram.ui.DiagramModelHints; import org.simantics.g2d.canvas.Hints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.CanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.participant.DiagramParticipant; import org.simantics.g2d.diagram.participant.ElementInteractor; import org.simantics.g2d.diagram.participant.ElementPainter; import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.diagram.participant.ZOrderHandler; import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor; import org.simantics.g2d.element.ElementClassProviders; import org.simantics.g2d.element.ElementClasses; import org.simantics.g2d.element.IElementClassProvider; import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; import org.simantics.g2d.multileveldiagram.TransitionFunction; import org.simantics.g2d.multileveldiagram.ZoomTransitionParticipant; import org.simantics.g2d.page.DiagramDesc; import org.simantics.g2d.participant.BackgroundPainter; import org.simantics.g2d.participant.CanvasBoundsParticipant; import org.simantics.g2d.participant.CanvasGrab; import org.simantics.g2d.participant.GridPainter; import org.simantics.g2d.participant.KeyToCommand; import org.simantics.g2d.participant.KeyUtil; import org.simantics.g2d.participant.MouseUtil; import org.simantics.g2d.participant.MultitouchPanZoomRotateInteractor; import org.simantics.g2d.participant.Notifications; import org.simantics.g2d.participant.PageBorderParticipant; import org.simantics.g2d.participant.PanZoomRotateHandler; import org.simantics.g2d.participant.PointerPainter; import org.simantics.g2d.participant.RulerPainter; import org.simantics.g2d.participant.SymbolUtil; import org.simantics.g2d.participant.TimeParticipant; import org.simantics.g2d.participant.TransformUtil; import org.simantics.g2d.participant.ZoomToAreaHandler; import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider; import org.simantics.modeling.ModelingResources; import org.simantics.modeling.mapping.ComponentCopyAdvisor; import org.simantics.modeling.mapping.ElementCopyAdvisor; import org.simantics.modeling.mapping.MappedElementCopyAdvisor; import org.simantics.modeling.mapping.ModelingSynchronizationHints; import org.simantics.modeling.ui.diagramEditor.PopulateElementDropParticipant; import org.simantics.modeling.ui.diagramEditor.PopulateElementMonitorDropParticipant; import org.simantics.modeling.ui.diagramEditor.handlers.HeadlessStructuralBrowsingHandler; import org.simantics.modeling.ui.diagramEditor.handlers.HeadlessStructuralBrowsingHandler.IDiagramUpdateSupport; import org.simantics.scenegraph.g2d.G2DSceneGraph; import org.simantics.scenegraph.g2d.events.adapter.AWTRemoteEventAdapter; import org.simantics.scenegraph.g2d.events.command.CommandKeyBinding; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.modelingRules.IModelingRules; import org.simantics.utils.datastructures.hints.HintContext; import org.simantics.utils.datastructures.hints.IHintContext; import org.simantics.utils.page.PageDesc; import org.simantics.utils.page.PageOrientation; import org.simantics.utils.threads.AWTThread; import org.simantics.utils.threads.IThreadWorkQueue; import org.simantics.utils.ui.ErrorLogger; /** * ISceneGraphProvider implementation for diagrams. * TODO: decide if this should be stateless or stateful class * * @author J-P Laine * */ public class DiagramSceneGraphProvider implements ICanvasSceneGraphProvider, IDiagramUpdateSupport { protected boolean isSymbol = false; protected Resource resource; protected ResourceArray structuralPath = null; protected IDiagram diagram = null; protected CanvasContext ctx = null; protected boolean ownsContext = false; protected GraphToDiagramSynchronizer synchronizer = null; protected String view = null; protected Boolean navigation = null; protected AWTRemoteEventAdapter listener = null; final IHintContext initialHints = new HintContext(); public DiagramSceneGraphProvider(ReadGraph g, final Resource diagramOrComposite) { this.structuralPath = new ResourceArray(); try { StructuralResource2 sr = StructuralResource2.getInstance(g); DiagramResource DIA = DiagramResource.getInstance(g); Resource diagram = diagramOrComposite; if(!g.isInstanceOf(diagram, DIA.Composite)) { ModelingResources mr = ModelingResources.getInstance(g); diagram = g.getPossibleObject(diagramOrComposite, mr.CompositeToDiagram); if(diagram == null) { // looks like we have a component without direct relation to composite.. try to solve this // FIXME: use adapter to get composite directly from resource final Resource type = g.getSingleType(diagramOrComposite, sr.Component); if (type == null) return; final Resource definedBy = g.getPossibleObject(type, sr.IsDefinedBy); if (definedBy == null) return; this.structuralPath = new ResourceArray(diagramOrComposite); diagram = g.getPossibleObject(definedBy, mr.CompositeToDiagram); } } else { Resource possibleSymbol = g.getPossibleObject(diagram, sr.Defines); if(possibleSymbol != null && g.isInstanceOf(possibleSymbol, DIA.ElementClass)) isSymbol = true; } this.resource = diagram; } catch (ManyObjectsForFunctionalRelationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ServiceException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSingleResultException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public GraphToDiagramSynchronizer getGraphToDiagramSynchronizer() { return synchronizer; } protected CopyPasteStrategy getCopyPasteStrategy() { try { CopyPasteStrategy cpStrategy = Simantics.getSession().syncRequest(new PossibleAdapter(resource, CopyPasteStrategy.class)); if(cpStrategy != null) return cpStrategy; } catch (DatabaseException e) { } return new DefaultCopyPasteStrategy(); } protected CopyAdvisor getCopyAdvisor() { try { CopyAdvisor advisor = Simantics.getSession().syncRequest(new PossibleAdapter(resource, CopyAdvisor.class)); if(advisor != null) return advisor; } catch (DatabaseException e) { } return new MappedElementCopyAdvisor(new ElementCopyAdvisor(), new ComponentCopyAdvisor()); } protected void initContext(CanvasContext ctx) { boolean unlock = false; if (!ctx.isLocked()) { ctx.setLocked(true); unlock = true; } IHintContext h = ctx.getDefaultHintContext(); // Support & Util Participants ctx.add( new TransformUtil() ); ctx.add( new MouseUtil() ); ctx.add( new KeyUtil() ); ctx.add( new CanvasGrab() ); ctx.add( new SymbolUtil() ); ctx.add( new TimeParticipant() ); ctx.add( new CanvasBoundsParticipant() ); ctx.add( new Notifications() ); // SGFocusParticipant does not work for remote viewer at the moment. //ctx.add( new SGFocusParticipant() ); h.setHint(PointerPainter.KEY_PAINT_POINTER, true); ctx.add( new PanZoomRotateHandler(navigation == null || navigation == true) ); ctx.add( new MultitouchPanZoomRotateInteractor() ); ctx.add( new ZoomToAreaHandler() ); ctx.add( new SimpleElementTransformHandler() ); // Key bindings if(navigation == null || navigation == true) ctx.add( new KeyToCommand( CommandKeyBinding.DEFAULT_BINDINGS ) ); // Grid & Ruler & Background h.setHint(RulerPainter.KEY_RULER_ENABLED, false); h.setHint(GridPainter.KEY_GRID_ENABLED, false); ctx.add( new GridPainter() ); ctx.add( new RulerPainter() ); ctx.add( new BackgroundPainter() ); h.setHint(Hints.KEY_DISPLAY_PAGE, Boolean.FALSE); h.setHint(Hints.KEY_DISPLAY_MARGINS, Boolean.TRUE); ctx.add( new PageBorderParticipant() ); h.setHint(Hints.KEY_GRID_COLOR, new Color(0.95f, 0.95f, 0.95f)); h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.WHITE); h.setHint(RulerPainter.KEY_RULER_BACKGROUND_COLOR, new Color(0.9f, 0.9f, 0.9f, 0.75f)); h.setHint(RulerPainter.KEY_RULER_TEXT_COLOR, Color.BLACK); ////// Diagram Participants ////// ctx.add( new PointerInteractor(false, false, false, false, true, true, synchronizer.getElementClassProvider(), null) ); ctx.add( new ElementInteractor() ); ctx.add( new Selection() ); ctx.add( new DiagramParticipant() ); ctx.add( new ElementPainter(true) ); //ctx.add( new ElementHeartbeater() ); ctx.add( new ZOrderHandler() ); ctx.add( new ZoomTransitionParticipant(TransitionFunction.SIGMOID) ); /////// D'n'D /////// ctx.add(new PopulateElementDropParticipant(synchronizer)); ctx.add(new PopulateElementMonitorDropParticipant(synchronizer, 0.5, 0.5)); h.setHint(Hints.KEY_TOOL, Hints.POINTERTOOL); h.setHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT, 1000.0); h.setHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT, 10.0); // Add visual browsing capabilities for structural models ISessionContext sessionContext = Simantics.getSessionContext(); ctx.add(new HeadlessStructuralBrowsingHandler(this, sessionContext, structuralPath)); // Page settings PageDesc pageDesc = PageDesc.DEFAULT.withOrientation(PageOrientation.Landscape); // TODO: load page desc from graph h.setHint(Hints.KEY_PAGE_DESC, pageDesc); ctx.add(new CopyPasteHandler(getCopyPasteStrategy())); ctx.add(new DeleteHandler(null)); if (resource != null) loadPageSettings(ctx, resource); ctx.assertParticipantDependencies(); if (unlock) ctx.setLocked(false); } protected void loadPageSettings(ICanvasContext ctx, Resource diagramResource) { try { DiagramDesc diagramDesc = Simantics.getSession().syncRequest(DiagramRequests.getDiagramDesc(diagramResource)); if (diagramDesc != null) setDiagramDesc(ctx, diagramDesc); } catch (DatabaseException e) { ErrorLogger.defaultLogError(e); } } protected void setDiagramDesc(ICanvasContext ctx, DiagramDesc diagramDesc) { IHintContext hints = ctx.getDefaultHintContext(); hints.setHint(Hints.KEY_PAGE_DESC, diagramDesc.getPageDesc()); //hints.setHint(Hints.KEY_DISPLAY_PAGE, diagramDesc.isPageBordersVisible()); hints.setHint(Hints.KEY_DISPLAY_MARGINS, diagramDesc.isMarginsVisible()); } @Override public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg) { return initializeSceneGraph(sg, null); } @Override public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String view) { return initializeSceneGraph(sg, "", "", view); } @Override public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String modelURI, String RVI) { return initializeSceneGraph(sg, modelURI, RVI, ""); } @Override public G2DSceneGraph initializeSceneGraph(ICanvasContext context, String modelURI, String RVI) { G2DSceneGraph sg = context.getSceneGraph(); return initializeSceneGraph(context, sg, modelURI, RVI, ""); } /** * @param sg * @param view * @param initialHints * @return */ private G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String modelURI, String RVI, String view) { IThreadWorkQueue thread = AWTThread.getThreadAccess(); ctx = new CanvasContext(thread, sg); // By giving the scene graph instance as parameter, we can use external serializer return initializeSceneGraph(ctx, sg, modelURI, RVI, view); } /** * @param sg * @param view * @param initialHints * @return */ private G2DSceneGraph initializeSceneGraph(ICanvasContext context, G2DSceneGraph sg, String modelURI, String RVI, String view) { this.ctx = (CanvasContext) context; this.view = view; if(view != null) { //System.out.println("using layer '"+view+"'"); initialHints.setHint(DiagramHints.KEY_FIXED_LAYERS, new String[] { view }); } try { IModelingRules modelingRules = Simantics.getSession().syncRequest(DiagramRequests.getModelingRules(resource, null)); if (modelingRules != null) { initialHints.setHint(DiagramModelHints.KEY_MODELING_RULES, modelingRules); } initialHints.setHint(SynchronizationHints.COPY_ADVISOR, getCopyAdvisor()); final RuntimeDiagramManager runtimeDiagramManager = RuntimeDiagramManager.create(Simantics.getSession(), resource, modelURI, RVI); synchronizer = Simantics.getSession().syncRequest(new Read() { @Override public GraphToDiagramSynchronizer perform(ReadGraph graph) throws DatabaseException { DiagramResource dr = DiagramResource.getInstance(graph); Boolean val = graph.getPossibleRelatedValue(resource, dr.NavigationEnabled, Bindings.BOOLEAN); if(val != null && navigation == null) { // Set only if navigation has not been set manually navigation = val; } GraphToDiagramSynchronizer sync = new GraphToDiagramSynchronizer(graph, ctx,createElementClassProvider(graph)); sync.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph)); diagram = sync.loadDiagram(new NullProgressMonitor(), graph, null, resource, runtimeDiagramManager.getRuntimeDiagram(), structuralPath, initialHints); // FIXME return sync; } }); } catch (DatabaseException e) { // TODO Auto-generated catch block e.printStackTrace(); } ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram); initContext(ctx); return ctx.getSceneGraph(); } protected IElementClassProvider createElementClassProvider(ReadGraph graph) throws DatabaseException { DiagramResource dr = DiagramResource.getInstance(graph); return ElementClassProviders.mappedProvider( ElementClasses.CONNECTION, DefaultConnectionClassFactory.CLASS.newClassWith(new StaticObjectAdapter(dr.Connection)), ElementClasses.FLAG, FlagClassFactory.createFlagClass(dr.Flag, dr.Flag_Terminal) ); } @Override public void dispose() { if(ctx != null) { if (ownsContext) ctx.dispose(); ctx = null; } if (synchronizer != null) { synchronizer.dispose(); synchronizer = null; } if (diagram != null) { diagram.dispose(); diagram = null; } } /** * Hack for returning canvasContext for some special cases * this method brakes the interface, but this is not intended to be used in production context * @return */ @Override public ICanvasContext getCanvasContext() { return ctx; } @Override public AWTEventListener getEventListener() { if(ctx == null) return null; if(listener == null) { listener = new AWTRemoteEventAdapter(ctx, ctx.getEventQueue()); } return listener; } @Override public void updateDiagram(final ResourceArray structuralPath) { final IHintContext hints = new HintContext(); if(view != null) { System.out.println("using layer '"+view+"'"); hints.setHint(DiagramHints.KEY_FIXED_LAYERS, new String[] { view }); } try { // FIXME: I have no idea if this works or not.. diagram = Simantics.getSession().syncRequest(new Read() { @Override public IDiagram perform(ReadGraph graph) throws DatabaseException { IDiagram d = synchronizer.loadDiagram(new NullProgressMonitor(), graph, null, structuralPath.resources[0], null, structuralPath.removeFromBeginning(0), hints); return d; } }); ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram); } catch (DatabaseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void setExperiment(ReadGraph g, String identifier) { if(diagram == null) return; diagram.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, identifier); initialHints.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, identifier); // try { // DiagramExperiment.setActiveExperiment(g, diagram, (String)identifier); // } catch (DatabaseException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } /** * Supported keys: * DiagramModelHints.KEY_SESSION_ID * DiagramHints.KEY_NAVIGATION_ENABLED */ @Override public void setHint(Object key, Object value) { if(DiagramModelHints.KEY_SESSION_ID.equals(key)) { if(diagram != null) diagram.setHint(DiagramModelHints.KEY_SESSION_ID, value); initialHints.setHint(DiagramModelHints.KEY_SESSION_ID, value); } else if(DiagramModelHints.KEY_ACTIVE_EXPERIMENT.equals(key)) { if(diagram != null) diagram.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, value); initialHints.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, value); } else if(DiagramHints.KEY_NAVIGATION_ENABLED.equals(key)) { navigation = (Boolean)value; } } }