1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling.ui.sg;
14 import java.awt.Color;
15 import java.awt.event.AWTEventListener;
17 import org.eclipse.core.runtime.NullProgressMonitor;
18 import org.simantics.Simantics;
19 import org.simantics.databoard.Bindings;
20 import org.simantics.db.ReadGraph;
21 import org.simantics.db.Resource;
22 import org.simantics.db.common.ResourceArray;
23 import org.simantics.db.common.primitiverequest.PossibleAdapter;
24 import org.simantics.db.exception.DatabaseException;
25 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
26 import org.simantics.db.exception.NoSingleResultException;
27 import org.simantics.db.exception.ServiceException;
28 import org.simantics.db.management.ISessionContext;
29 import org.simantics.db.procedure.Listener;
30 import org.simantics.db.request.Read;
31 import org.simantics.diagram.adapter.DefaultConnectionClassFactory;
32 import org.simantics.diagram.adapter.FlagClassFactory;
33 import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;
34 import org.simantics.diagram.handler.CopyPasteHandler;
35 import org.simantics.diagram.handler.CopyPasteStrategy;
36 import org.simantics.diagram.handler.DefaultCopyPasteStrategy;
37 import org.simantics.diagram.handler.DeleteHandler;
38 import org.simantics.diagram.handler.SimpleElementTransformHandler;
39 import org.simantics.diagram.query.DiagramRequests;
40 import org.simantics.diagram.runtime.RuntimeDiagramManager;
41 import org.simantics.diagram.stubs.DiagramResource;
42 import org.simantics.diagram.synchronization.CopyAdvisor;
43 import org.simantics.diagram.synchronization.SynchronizationHints;
44 import org.simantics.diagram.ui.DiagramModelHints;
45 import org.simantics.g2d.canvas.Hints;
46 import org.simantics.g2d.canvas.ICanvasContext;
47 import org.simantics.g2d.canvas.impl.CanvasContext;
48 import org.simantics.g2d.diagram.DiagramHints;
49 import org.simantics.g2d.diagram.IDiagram;
50 import org.simantics.g2d.diagram.participant.DiagramParticipant;
51 import org.simantics.g2d.diagram.participant.ElementInteractor;
52 import org.simantics.g2d.diagram.participant.ElementPainter;
53 import org.simantics.g2d.diagram.participant.Selection;
54 import org.simantics.g2d.diagram.participant.ZOrderHandler;
55 import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor;
56 import org.simantics.g2d.element.ElementClassProviders;
57 import org.simantics.g2d.element.ElementClasses;
58 import org.simantics.g2d.element.IElementClassProvider;
59 import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;
60 import org.simantics.g2d.multileveldiagram.TransitionFunction;
61 import org.simantics.g2d.multileveldiagram.ZoomTransitionParticipant;
62 import org.simantics.g2d.page.DiagramDesc;
63 import org.simantics.g2d.participant.BackgroundPainter;
64 import org.simantics.g2d.participant.CanvasBoundsParticipant;
65 import org.simantics.g2d.participant.CanvasGrab;
66 import org.simantics.g2d.participant.GridPainter;
67 import org.simantics.g2d.participant.KeyToCommand;
68 import org.simantics.g2d.participant.KeyUtil;
69 import org.simantics.g2d.participant.MouseUtil;
70 import org.simantics.g2d.participant.MultitouchPanZoomRotateInteractor;
71 import org.simantics.g2d.participant.Notifications;
72 import org.simantics.g2d.participant.PageBorderParticipant;
73 import org.simantics.g2d.participant.PanZoomRotateHandler;
74 import org.simantics.g2d.participant.PointerPainter;
75 import org.simantics.g2d.participant.RulerPainter;
76 import org.simantics.g2d.participant.SymbolUtil;
77 import org.simantics.g2d.participant.TimeParticipant;
78 import org.simantics.g2d.participant.TransformUtil;
79 import org.simantics.g2d.participant.ZoomToAreaHandler;
80 import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider;
81 import org.simantics.modeling.ModelingResources;
82 import org.simantics.modeling.mapping.ComponentCopyAdvisor;
83 import org.simantics.modeling.mapping.ElementCopyAdvisor;
84 import org.simantics.modeling.mapping.MappedElementCopyAdvisor;
85 import org.simantics.modeling.mapping.ModelingSynchronizationHints;
86 import org.simantics.modeling.ui.diagramEditor.PopulateElementDropParticipant;
87 import org.simantics.modeling.ui.diagramEditor.PopulateElementMonitorDropParticipant;
88 import org.simantics.modeling.ui.diagramEditor.handlers.HeadlessStructuralBrowsingHandler;
89 import org.simantics.modeling.ui.diagramEditor.handlers.HeadlessStructuralBrowsingHandler.IDiagramUpdateSupport;
90 import org.simantics.scenegraph.g2d.G2DSceneGraph;
91 import org.simantics.scenegraph.g2d.events.adapter.AWTRemoteEventAdapter;
92 import org.simantics.scenegraph.g2d.events.command.CommandKeyBinding;
93 import org.simantics.structural.stubs.StructuralResource2;
94 import org.simantics.structural2.modelingRules.IModelingRules;
95 import org.simantics.utils.datastructures.hints.HintContext;
96 import org.simantics.utils.datastructures.hints.IHintContext;
97 import org.simantics.utils.page.PageDesc;
98 import org.simantics.utils.page.PageOrientation;
99 import org.simantics.utils.threads.AWTThread;
100 import org.simantics.utils.threads.IThreadWorkQueue;
101 import org.simantics.utils.threads.ThreadUtils;
102 import org.simantics.utils.ui.ErrorLogger;
106 * ISceneGraphProvider implementation for diagrams.
107 * TODO: decide if this should be stateless or stateful class
112 public class DiagramSceneGraphProvider implements ICanvasSceneGraphProvider, IDiagramUpdateSupport {
114 protected boolean isSymbol = false;
115 protected Resource resource;
116 protected ResourceArray structuralPath = null;
117 protected IDiagram diagram = null;
118 protected CanvasContext ctx = null;
119 protected boolean ownsContext = false;
120 protected GraphToDiagramSynchronizer synchronizer = null;
121 protected String view = null;
122 protected Boolean navigation = null;
124 protected AWTRemoteEventAdapter listener = null;
126 final IHintContext initialHints = new HintContext();
128 public DiagramSceneGraphProvider(ReadGraph g, final Resource diagramOrComposite) {
129 this.structuralPath = new ResourceArray();
132 StructuralResource2 sr = StructuralResource2.getInstance(g);
133 DiagramResource DIA = DiagramResource.getInstance(g);
135 Resource diagram = diagramOrComposite;
136 if(!g.isInstanceOf(diagram, DIA.Composite)) {
138 ModelingResources mr = ModelingResources.getInstance(g);
139 diagram = g.getPossibleObject(diagramOrComposite, mr.CompositeToDiagram);
140 if(diagram == null) {
141 // looks like we have a component without direct relation to composite.. try to solve this
142 // FIXME: use adapter to get composite directly from resource
143 final Resource type = g.getSingleType(diagramOrComposite, sr.Component);
147 final Resource definedBy = g.getPossibleObject(type, sr.IsDefinedBy);
148 if (definedBy == null)
151 this.structuralPath = new ResourceArray(diagramOrComposite);
152 diagram = g.getPossibleObject(definedBy, mr.CompositeToDiagram);
157 Resource possibleSymbol = g.getPossibleObject(diagram, sr.Defines);
158 if(possibleSymbol != null && g.isInstanceOf(possibleSymbol, DIA.ElementClass)) isSymbol = true;
162 this.resource = diagram;
164 } catch (ManyObjectsForFunctionalRelationException e) {
165 // TODO Auto-generated catch block
167 } catch (ServiceException e) {
168 // TODO Auto-generated catch block
170 } catch (NoSingleResultException e) {
171 // TODO Auto-generated catch block
176 public GraphToDiagramSynchronizer getGraphToDiagramSynchronizer() {
180 protected CopyPasteStrategy getCopyPasteStrategy() {
182 CopyPasteStrategy cpStrategy = Simantics.getSession().syncRequest(new PossibleAdapter<CopyPasteStrategy>(resource, CopyPasteStrategy.class));
183 if(cpStrategy != null) return cpStrategy;
184 } catch (DatabaseException e) {
186 return new DefaultCopyPasteStrategy();
189 protected CopyAdvisor getCopyAdvisor() {
191 CopyAdvisor advisor = Simantics.getSession().syncRequest(new PossibleAdapter<CopyAdvisor>(resource, CopyAdvisor.class));
192 if(advisor != null) return advisor;
193 } catch (DatabaseException e) {
195 return new MappedElementCopyAdvisor(new ElementCopyAdvisor(), new ComponentCopyAdvisor());
198 protected void initContext(CanvasContext ctx) {
199 boolean unlock = false;
200 if (!ctx.isLocked()) {
205 IHintContext h = ctx.getDefaultHintContext();
207 // Support & Util Participants
208 ctx.add( new TransformUtil() );
209 ctx.add( new MouseUtil() );
210 ctx.add( new KeyUtil() );
211 ctx.add( new CanvasGrab() );
212 ctx.add( new SymbolUtil() );
213 ctx.add( new TimeParticipant() );
214 ctx.add( new CanvasBoundsParticipant() );
215 ctx.add( new Notifications() );
217 // SGFocusParticipant does not work for remote viewer at the moment.
218 //ctx.add( new SGFocusParticipant() );
220 h.setHint(PointerPainter.KEY_PAINT_POINTER, true);
222 ctx.add( new PanZoomRotateHandler(navigation == null || navigation == true) );
223 ctx.add( new MultitouchPanZoomRotateInteractor() );
224 ctx.add( new ZoomToAreaHandler() );
225 ctx.add( new SimpleElementTransformHandler() );
228 if(navigation == null || navigation == true)
229 ctx.add( new KeyToCommand( CommandKeyBinding.DEFAULT_BINDINGS ) );
231 // Grid & Ruler & Background
232 h.setHint(RulerPainter.KEY_RULER_ENABLED, false);
233 h.setHint(GridPainter.KEY_GRID_ENABLED, false);
235 ctx.add( new GridPainter() );
236 ctx.add( new RulerPainter() );
237 ctx.add( new BackgroundPainter() );
239 h.setHint(Hints.KEY_DISPLAY_PAGE, Boolean.FALSE);
240 h.setHint(Hints.KEY_DISPLAY_MARGINS, Boolean.TRUE);
241 ctx.add( new PageBorderParticipant() );
243 h.setHint(Hints.KEY_GRID_COLOR, new Color(0.95f, 0.95f, 0.95f));
244 h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.WHITE);
245 h.setHint(RulerPainter.KEY_RULER_BACKGROUND_COLOR, new Color(0.9f, 0.9f, 0.9f, 0.75f));
246 h.setHint(RulerPainter.KEY_RULER_TEXT_COLOR, Color.BLACK);
248 ////// Diagram Participants //////
249 ctx.add( new PointerInteractor(false, false, false, false, true, true, synchronizer.getElementClassProvider(), null) );
250 ctx.add( new ElementInteractor() );
251 ctx.add( new Selection() );
252 ctx.add( new DiagramParticipant() );
253 ctx.add( new ElementPainter(true) );
255 //ctx.add( new ElementHeartbeater() );
256 ctx.add( new ZOrderHandler() );
257 ctx.add( new ZoomTransitionParticipant(TransitionFunction.SIGMOID) );
259 /////// D'n'D ///////
260 ctx.add(new PopulateElementDropParticipant(synchronizer));
261 ctx.add(new PopulateElementMonitorDropParticipant(synchronizer, 0.5, 0.5));
263 h.setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
265 h.setHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT, 1000.0);
266 h.setHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT, 10.0);
268 // Add visual browsing capabilities for structural models
269 ISessionContext sessionContext = Simantics.getSessionContext();
270 ctx.add(new HeadlessStructuralBrowsingHandler(this, sessionContext, structuralPath));
273 PageDesc pageDesc = PageDesc.DEFAULT.withOrientation(PageOrientation.Landscape);
274 // TODO: load page desc from graph
275 h.setHint(Hints.KEY_PAGE_DESC, pageDesc);
277 ctx.add(new CopyPasteHandler(getCopyPasteStrategy()));
278 ctx.add(new DeleteHandler(null));
280 if (resource != null)
281 loadPageSettings(ctx, resource);
283 ctx.assertParticipantDependencies();
285 ctx.setLocked(false);
288 protected void loadPageSettings(ICanvasContext ctx, Resource diagramResource) {
290 DiagramDesc diagramDesc = Simantics.getSession().syncRequest(DiagramRequests.getDiagramDesc(diagramResource));
291 if (diagramDesc != null)
292 setDiagramDesc(ctx, diagramDesc);
294 // Create a listener to react to page setting changes.
295 Simantics.getSession().asyncRequest(DiagramRequests.getDiagramDesc(diagramResource), new Listener<DiagramDesc>() {
298 public void execute(DiagramDesc result) {
299 if (result != null && ctx != null) {
300 ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() {
304 setDiagramDesc(ctx, result);
312 public void exception(Throwable t) {
313 ErrorLogger.defaultLogError(t);
317 public boolean isDisposed() {
322 } catch (DatabaseException e) {
323 ErrorLogger.defaultLogError(e);
327 protected void setDiagramDesc(ICanvasContext ctx, DiagramDesc diagramDesc) {
328 IHintContext hints = ctx.getDefaultHintContext();
329 hints.setHint(Hints.KEY_PAGE_DESC, diagramDesc.getPageDesc());
330 hints.setHint(Hints.KEY_DISPLAY_PAGE, diagramDesc.isPageBordersVisible());
331 hints.setHint(Hints.KEY_DISPLAY_MARGINS, diagramDesc.isMarginsVisible());
335 public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg) {
336 return initializeSceneGraph(sg, null);
340 public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String view) {
341 return initializeSceneGraph(sg, "", "", view);
345 public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String modelURI, String RVI) {
346 return initializeSceneGraph(sg, modelURI, RVI, "");
350 public G2DSceneGraph initializeSceneGraph(ICanvasContext context, String modelURI, String RVI) {
351 G2DSceneGraph sg = context.getSceneGraph();
352 return initializeSceneGraph(context, sg, modelURI, RVI, "");
358 * @param initialHints
361 private G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String modelURI, String RVI, String view) {
362 IThreadWorkQueue thread = AWTThread.getThreadAccess();
363 ctx = new CanvasContext(thread, sg); // By giving the scene graph instance as parameter, we can use external serializer
364 return initializeSceneGraph(ctx, sg, modelURI, RVI, view);
370 * @param initialHints
373 private G2DSceneGraph initializeSceneGraph(ICanvasContext context, G2DSceneGraph sg, String modelURI, String RVI, String view) {
374 this.ctx = (CanvasContext) context;
377 //System.out.println("using layer '"+view+"'");
378 initialHints.setHint(DiagramHints.KEY_FIXED_LAYERS, new String[] { view });
382 fillInitialDiagramHints(initialHints);
384 final RuntimeDiagramManager runtimeDiagramManager = RuntimeDiagramManager.create(Simantics.getSession(), resource, modelURI, RVI);
386 synchronizer = Simantics.getSession().syncRequest(new Read<GraphToDiagramSynchronizer>() {
388 public GraphToDiagramSynchronizer perform(ReadGraph graph) throws DatabaseException {
389 DiagramResource dr = DiagramResource.getInstance(graph);
390 Boolean val = graph.getPossibleRelatedValue(resource, dr.NavigationEnabled, Bindings.BOOLEAN);
391 if(val != null && navigation == null) { // Set only if navigation has not been set manually
394 GraphToDiagramSynchronizer sync = new GraphToDiagramSynchronizer(graph, ctx,createElementClassProvider(graph));
396 sync.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));
397 diagram = sync.loadDiagram(new NullProgressMonitor(), graph, null, resource, runtimeDiagramManager.getRuntimeDiagram(), structuralPath, initialHints); // FIXME
402 } catch (DatabaseException e) {
403 // TODO Auto-generated catch block
406 ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);
410 return ctx.getSceneGraph();
413 protected void fillInitialDiagramHints(IHintContext initialHints) throws DatabaseException {
414 IModelingRules modelingRules = Simantics.getSession().syncRequest(DiagramRequests.getModelingRules(resource, null));
415 if (modelingRules != null) {
416 initialHints.setHint(DiagramModelHints.KEY_MODELING_RULES, modelingRules);
419 initialHints.setHint(SynchronizationHints.COPY_ADVISOR, getCopyAdvisor());
422 protected IElementClassProvider createElementClassProvider(ReadGraph graph) throws DatabaseException {
423 DiagramResource dr = DiagramResource.getInstance(graph);
424 return ElementClassProviders.mappedProvider(
425 ElementClasses.CONNECTION, DefaultConnectionClassFactory.CLASS.newClassWith(new StaticObjectAdapter(dr.Connection)),
426 ElementClasses.FLAG, FlagClassFactory.createFlagClass(dr.Flag, dr.Flag_Terminal)
432 public void dispose() {
438 if (synchronizer != null) {
439 synchronizer.dispose();
442 if (diagram != null) {
449 * Hack for returning canvasContext for some special cases
450 * this method brakes the interface, but this is not intended to be used in production context
454 public ICanvasContext getCanvasContext() {
459 public AWTEventListener getEventListener() {
460 if(ctx == null) return null;
461 if(listener == null) {
462 listener = new AWTRemoteEventAdapter(ctx, ctx.getEventQueue());
468 public void updateDiagram(final ResourceArray structuralPath) {
469 final IHintContext hints = new HintContext();
471 System.out.println("using layer '"+view+"'");
472 hints.setHint(DiagramHints.KEY_FIXED_LAYERS, new String[] { view });
476 // FIXME: I have no idea if this works or not..
477 diagram = Simantics.getSession().syncRequest(new Read<IDiagram>() {
479 public IDiagram perform(ReadGraph graph) throws DatabaseException {
480 IDiagram d = synchronizer.loadDiagram(new NullProgressMonitor(), graph, null, structuralPath.resources[0], null, structuralPath.removeFromBeginning(0), hints);
484 ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);
485 } catch (DatabaseException e) {
486 // TODO Auto-generated catch block
491 public void setExperiment(ReadGraph g, String identifier) {
492 if(diagram == null) return;
493 diagram.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, identifier);
494 initialHints.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, identifier);
497 // DiagramExperiment.setActiveExperiment(g, diagram, (String)identifier);
498 // } catch (DatabaseException e) {
499 // // TODO Auto-generated catch block
500 // e.printStackTrace();
507 * DiagramModelHints.KEY_SESSION_ID
508 * DiagramHints.KEY_NAVIGATION_ENABLED
511 public void setHint(Object key, Object value) {
512 if(DiagramModelHints.KEY_SESSION_ID.equals(key)) {
514 diagram.setHint(DiagramModelHints.KEY_SESSION_ID, value);
515 initialHints.setHint(DiagramModelHints.KEY_SESSION_ID, value);
516 } else if(DiagramModelHints.KEY_ACTIVE_EXPERIMENT.equals(key)) {
518 diagram.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, value);
519 initialHints.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, value);
520 } else if(DiagramHints.KEY_NAVIGATION_ENABLED.equals(key)) {
521 navigation = (Boolean)value;