1 /*******************************************************************************
2 * Copyright (c) 2007, 2019 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 * Semantum Oy - issue #4384, gitlab #399
12 *******************************************************************************/
13 package org.simantics.modeling.ui.diagramEditor;
15 import java.awt.Color;
16 import java.awt.dnd.DnDConstants;
17 import java.util.Collections;
19 import java.util.concurrent.TimeUnit;
20 import java.util.function.Supplier;
22 import org.eclipse.core.runtime.Assert;
23 import org.eclipse.core.runtime.IAdaptable;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.SubMonitor;
26 import org.eclipse.core.runtime.jobs.Job;
27 import org.eclipse.jface.action.IStatusLineManager;
28 import org.eclipse.jface.resource.JFaceResources;
29 import org.eclipse.jface.resource.LocalResourceManager;
30 import org.eclipse.swt.widgets.Composite;
31 import org.eclipse.swt.widgets.Display;
32 import org.eclipse.ui.IEditorInput;
33 import org.eclipse.ui.IEditorSite;
34 import org.eclipse.ui.IWorkbenchPartSite;
35 import org.eclipse.ui.contexts.IContextService;
36 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
37 import org.simantics.Simantics;
38 import org.simantics.browsing.ui.model.browsecontexts.BrowseContext;
39 import org.simantics.db.ReadGraph;
40 import org.simantics.db.Resource;
41 import org.simantics.db.Session;
42 import org.simantics.db.WriteGraph;
43 import org.simantics.db.common.primitiverequest.PossibleAdapter;
44 import org.simantics.db.common.primitiverequest.PossibleObject;
45 import org.simantics.db.common.procedure.adapter.ListenerDelegate;
46 import org.simantics.db.common.procedure.adapter.ListenerSupport;
47 import org.simantics.db.common.request.ParametrizedRead;
48 import org.simantics.db.common.request.TypeURIs;
49 import org.simantics.db.common.request.WriteRequest;
50 import org.simantics.db.common.utils.CommonDBUtils;
51 import org.simantics.db.common.utils.TagUtil;
52 import org.simantics.db.exception.DatabaseException;
53 import org.simantics.db.layer0.request.combinations.Combinators;
54 import org.simantics.db.management.ISessionContext;
55 import org.simantics.db.management.ISessionContextProvider;
56 import org.simantics.db.request.Read;
57 import org.simantics.diagram.DiagramTypeUtils;
58 import org.simantics.diagram.adapter.FlagClassFactory;
59 import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;
60 import org.simantics.diagram.adapter.IDiagramLoader;
61 import org.simantics.diagram.connection.ModelledConnectionAdvisor;
62 import org.simantics.diagram.handler.ConnectionCommandHandler;
63 import org.simantics.diagram.handler.CopyPasteHandler;
64 import org.simantics.diagram.handler.CopyPasteStrategy;
65 import org.simantics.diagram.handler.DefaultCopyPasteStrategy;
66 import org.simantics.diagram.handler.DeleteHandler;
67 import org.simantics.diagram.handler.ExpandSelectionHandler;
68 import org.simantics.diagram.handler.SimpleElementTransformHandler;
69 import org.simantics.diagram.layer.ILayersViewPage;
70 import org.simantics.diagram.participant.ConnectionCrossingsParticipant;
71 import org.simantics.diagram.participant.ContextUtil;
72 import org.simantics.diagram.participant.CopyAsSVGParticipant;
73 import org.simantics.diagram.participant.PointerInteractor2;
74 import org.simantics.diagram.participant.SGFocusParticipant;
75 import org.simantics.diagram.query.DiagramRequests;
76 import org.simantics.diagram.runtime.RuntimeDiagramManager;
77 import org.simantics.diagram.stubs.DiagramResource;
78 import org.simantics.diagram.symbolcontribution.SymbolProviderFactory;
79 import org.simantics.diagram.synchronization.CopyAdvisor;
80 import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;
81 import org.simantics.diagram.synchronization.SynchronizationHints;
82 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
83 import org.simantics.diagram.ui.DiagramModelHints;
84 import org.simantics.diagram.ui.SWTPopupMenuParticipant;
85 import org.simantics.diagram.ui.SWTPopupMenuParticipantAwt;
86 import org.simantics.diagram.ui.WorkbenchSelectionProvider;
87 import org.simantics.g2d.canvas.Hints;
88 import org.simantics.g2d.canvas.ICanvasContext;
89 import org.simantics.g2d.canvas.ICanvasParticipant;
90 import org.simantics.g2d.canvas.impl.CanvasContext;
91 import org.simantics.g2d.chassis.AWTChassis;
92 import org.simantics.g2d.chassis.ICanvasChassis;
93 import org.simantics.g2d.chassis.IChassisListener;
94 import org.simantics.g2d.chassis.SWTChassis;
95 import org.simantics.g2d.connection.IConnectionAdvisor;
96 import org.simantics.g2d.diagram.DiagramClass;
97 import org.simantics.g2d.diagram.DiagramHints;
98 import org.simantics.g2d.diagram.IDiagram;
99 import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;
100 import org.simantics.g2d.diagram.impl.Diagram;
101 import org.simantics.g2d.diagram.participant.DelayedBatchElementPainter;
102 import org.simantics.g2d.diagram.participant.DiagramParticipant;
103 import org.simantics.g2d.diagram.participant.ElementInteractor;
104 import org.simantics.g2d.diagram.participant.ElementPainter;
105 import org.simantics.g2d.diagram.participant.Selection;
106 import org.simantics.g2d.diagram.participant.TerminalPainter;
107 import org.simantics.g2d.diagram.participant.ZOrderHandler;
108 import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor;
109 import org.simantics.g2d.element.ElementClassProviders;
110 import org.simantics.g2d.element.ElementClasses;
111 import org.simantics.g2d.element.ElementUtils;
112 import org.simantics.g2d.element.IElement;
113 import org.simantics.g2d.element.IElementClassProvider;
114 import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;
115 import org.simantics.g2d.elementclass.connection.ConnectionClass;
116 import org.simantics.g2d.page.DiagramDesc;
117 import org.simantics.g2d.participant.BackgroundPainter;
118 import org.simantics.g2d.participant.CanvasBoundsParticipant;
119 import org.simantics.g2d.participant.CanvasGrab;
120 import org.simantics.g2d.participant.GridPainter;
121 import org.simantics.g2d.participant.KeyUtil;
122 import org.simantics.g2d.participant.MouseUtil;
123 import org.simantics.g2d.participant.Notifications;
124 import org.simantics.g2d.participant.PageBorderParticipant;
125 import org.simantics.g2d.participant.PanZoomRotateHandler;
126 import org.simantics.g2d.participant.PointerPainter;
127 import org.simantics.g2d.participant.RenderingQualityInteractor;
128 import org.simantics.g2d.participant.RulerPainter;
129 import org.simantics.g2d.participant.SymbolUtil;
130 import org.simantics.g2d.participant.TimeParticipant;
131 import org.simantics.g2d.participant.TransformUtil;
132 import org.simantics.g2d.participant.WorkbenchStatusLine;
133 import org.simantics.g2d.participant.ZoomToAreaHandler;
134 import org.simantics.g2d.tooltip.TerminalTooltipParticipant;
135 import org.simantics.g2d.utils.CanvasUtils;
136 import org.simantics.layer0.utils.triggers.IActivation;
137 import org.simantics.layer0.utils.triggers.IActivationManager;
138 import org.simantics.modeling.ModelingResources;
139 import org.simantics.modeling.mapping.ComponentCopyAdvisor;
140 import org.simantics.modeling.mapping.ElementCopyAdvisor;
141 import org.simantics.modeling.mapping.MappedElementCopyAdvisor;
142 import org.simantics.modeling.mapping.ModelingSynchronizationHints;
143 import org.simantics.modeling.ui.diagramEditor.handlers.LinkBrowsingHandler;
144 import org.simantics.modeling.ui.diagramEditor.handlers.StructuralBrowsingHandler;
145 import org.simantics.modeling.ui.preferences.DiagramPreferenceUtil;
146 import org.simantics.modeling.ui.preferences.DiagramPreferences;
147 import org.simantics.project.ontology.ProjectResource;
148 import org.simantics.scenegraph.INode;
149 import org.simantics.scenegraph.g2d.snap.GridSnapAdvisor;
150 import org.simantics.scenegraph.utils.NodeUtil;
151 import org.simantics.selectionview.StandardPropertyPage;
152 import org.simantics.structural.stubs.StructuralResource2;
153 import org.simantics.structural2.modelingRules.IModelingRules;
154 import org.simantics.ui.SimanticsUI;
155 import org.simantics.ui.jobs.SessionGarbageCollectorJob;
156 import org.simantics.ui.workbench.IPropertyPage;
157 import org.simantics.ui.workbench.IResourceEditorInput;
158 import org.simantics.ui.workbench.IResourceEditorInput2;
159 import org.simantics.ui.workbench.TitleRequest;
160 import org.simantics.ui.workbench.TitleUpdater;
161 import org.simantics.ui.workbench.ToolTipRequest;
162 import org.simantics.ui.workbench.editor.input.InputValidationCombinators;
163 import org.simantics.utils.DataContainer;
164 import org.simantics.utils.datastructures.hints.HintContext;
165 import org.simantics.utils.datastructures.hints.HintListenerAdapter;
166 import org.simantics.utils.datastructures.hints.IHintContext;
167 import org.simantics.utils.datastructures.hints.IHintContext.Key;
168 import org.simantics.utils.datastructures.hints.IHintListener;
169 import org.simantics.utils.datastructures.hints.IHintObservable;
170 import org.simantics.utils.threads.AWTThread;
171 import org.simantics.utils.threads.IThreadWorkQueue;
172 import org.simantics.utils.threads.SWTThread;
173 import org.simantics.utils.threads.ThreadUtils;
174 import org.simantics.utils.ui.ErrorLogger;
175 import org.simantics.utils.ui.ExceptionUtils;
177 import com.kitfox.svg.SVGCache;
180 * @author Toni Kalajainen
181 * @author Tuukka Lehtonen
183 public class DiagramViewer
184 //extends EditorPart implements IResourceEditorPart2,
185 implements ListenerSupport, IAdaptable {
187 public interface DiagramViewerHost {
188 void doSetPartName(String name);
189 void doSetTitleToolTip(String name);
192 public static final String DIAGRAMMING_CONTEXT = "org.simantics.modeling.ui.diagramming"; //$NON-NLS-1$
193 private static final String PREFERENCE_VIRTUAL_GRAPH = "preferences"; //$NON-NLS-1$
195 private static final boolean PROFILE = false;
197 ParametrizedRead<IResourceEditorInput, Boolean> INPUT_VALIDATOR =
199 InputValidationCombinators.or(
200 // Normal configuration diagrams of a model
202 InputValidationCombinators.hasURI(),
203 InputValidationCombinators.partialFunction(ModelingResources.URIs.DiagramToComposite)
205 // Configuration diagrams of a component type
207 InputValidationCombinators.hasURI(),
209 InputValidationCombinators.partialFunction(StructuralResource2.URIs.Defines),
210 InputValidationCombinators.partialFunction(ModelingResources.URIs.DiagramToComposite)
214 InputValidationCombinators.extractInputResource()
217 protected EditorState editorState;
219 protected boolean disposed = false;
220 protected IThreadWorkQueue swt;
221 protected IStatusLineManager statusLineManager;
222 protected Display display;
223 protected LocalResourceManager resourceManager;
224 protected SWTChassis c;
225 protected IDiagram sourceDiagram;
226 protected DataContainer<IDiagram> sourceDiagramContainer;
227 protected CanvasContext canvasContext;
228 protected ISessionContextProvider sessionContextProvider;
229 protected ISessionContext sessionContext;
230 protected Resource diagramResource;
231 protected GraphToDiagramSynchronizer synchronizer;
232 protected IActivation activation;
233 protected ContextUtil contextUtil;
234 protected SWTPopupMenuParticipant popupMenuParticipant;
236 protected DiagramPreferences diagramPreferences;
237 protected DiagramDesc diagramDesc;
238 protected GridSnapAdvisor snapAdvisor;
240 private RuntimeDiagramManager runtimeDiagramManager;
241 private Resource runtimeDiagramResourceCache;
242 private HasDiagramSourceListener hasDiagramSourceListener;
246 * {@link #init(DiagramViewerHost, IEditorSite, IEditorInput, DataContainer, WorkbenchSelectionProvider)}
249 protected WorkbenchSelectionProvider selectionProvider;
251 protected Resource getRuntimeFromManager() {
252 RuntimeDiagramManager rtdm = runtimeDiagramManager;
253 return (rtdm == null) ? null : rtdm.getRuntimeDiagram();
256 public Resource getRuntime() {
257 if (runtimeDiagramResourceCache != null)
258 return runtimeDiagramResourceCache;
259 runtimeDiagramResourceCache = getRuntimeFromManager();
260 return runtimeDiagramResourceCache;
263 public ParametrizedRead<IResourceEditorInput, Boolean> getInputValidator() {
264 return INPUT_VALIDATOR;
267 protected void addDropParticipants(ICanvasContext ctx) {
268 ctx.getDefaultHintContext().setHint(Hints.KEY_ALLOWED_DRAG_ACTIONS, DnDConstants.ACTION_COPY);
270 ctx.add(new PopulateElementDropParticipant(synchronizer, getSite()));
271 ctx.add(new PopulateElementMonitorDropParticipant(synchronizer, 0.5, 0.5));
274 protected CopyPasteStrategy getCopyPasteStrategy() {
276 CopyPasteStrategy cpStrategy = Simantics.getSession().syncRequest(new PossibleAdapter<CopyPasteStrategy>(getInputResource(), CopyPasteStrategy.class));
277 if(cpStrategy != null) return cpStrategy;
278 } catch (DatabaseException e) {
280 return getDefaultCopyPasteStrategy();
283 protected CopyPasteStrategy getDefaultCopyPasteStrategy() {
284 return new DefaultCopyPasteStrategy();
287 protected CopyAdvisor getCopyAdvisor() {
289 CopyAdvisor advisor = Simantics.getSession().syncRequest(new PossibleAdapter<CopyAdvisor>(getInputResource(), CopyAdvisor.class));
290 if(advisor != null) return advisor;
291 } catch (DatabaseException e) {
293 return new MappedElementCopyAdvisor(new ElementCopyAdvisor(), new ComponentCopyAdvisor());
298 * TODO: change argument from CanvasContext to ICanvasContext
300 protected void addKeyBindingParticipants(CanvasContext ctx) {
301 //ctx.add( new KeyToCommand( CommandKeyBinding.DEFAULT_BINDINGS ) );
302 ctx.add(new DeleteHandler(statusLineManager));
303 ctx.add(new CopyPasteHandler(getCopyPasteStrategy(), statusLineManager).setWorkbenchSite(getEditorSite()));
304 ctx.add(new ConnectionCommandHandler());
307 protected void addPopupmenu(ICanvasContext ctx) {
308 ctx.add(popupMenuParticipant);
311 protected void addWorkbenchSelectionProvider(ICanvasContext ctx) {
312 if (selectionProvider != null)
313 ctx.add(selectionProvider);
316 protected void addViewManipulationParticipants(CanvasContext ctx) {
317 ctx.add(new PanZoomRotateHandler());
318 //ctx.add(new MousePanZoomInteractor());
319 //ctx.add(new MultitouchPanZoomRotateInteractor());
320 // ctx.add( new OrientationRestorer() );
321 ctx.add(new ZoomToAreaHandler());
324 protected void addDiagramParticipants(ICanvasContext ctx) {
325 ctx.add(new ZOrderHandler());
326 ctx.add(getPointerInteractor());
327 ctx.add(new ElementInteractor());
328 ctx.add(new Selection());
329 ctx.add(new DiagramParticipant());
330 ctx.add(new ElementPainter());
331 //ctx.add(new ElementHeartbeater());
332 //ctx.add(new ZoomTransitionParticipant(TransitionFunction.SIGMOID));
333 //ctx.add(new TooltipParticipant());
334 ctx.add(new TerminalTooltipParticipant());
335 ctx.add(new ConnectionCrossingsParticipant(getInputResource()));
336 ctx.add(new CopyAsSVGParticipant());
339 protected void addPainterParticipants(ICanvasContext ctx) {
340 ctx.add(new RenderingQualityInteractor());
341 ctx.add(new TerminalPainter(true, true, false, true));
342 ctx.add(new DelayedBatchElementPainter(PickFilter.FILTER_MONITORS, 500, TimeUnit.MILLISECONDS));
345 protected void addStructureParticipants(ICanvasContext ctx) {
346 addWorkbenchSelectionProvider(ctx);
347 // Add visual browsing capabilities for structural models
348 ctx.add(new StructuralBrowsingHandler(getSite(), sessionContext, getResourceInput2()));
349 ctx.add(new LinkBrowsingHandler(getSite(), this, sessionContext));
353 * Override to add any diagram/canvas participants to the canvas context
354 * initialized for the editor.
358 protected void addOtherParticipants(CanvasContext ctx) {
361 public static Set<String> defaultPropertyBrowseContexts = Collections.singleton(ProjectResource.URIs.ProjectBrowseContext);
363 protected Set<String> getPropertyPageContexts() {
365 return BrowseContext.getBrowseContextClosure(Simantics.getSession(), defaultPropertyBrowseContexts);
366 } catch (DatabaseException e) {
367 ExceptionUtils.logAndShowError(Messages.DiagramViewer_FailedtoLoadModeled, e);
368 return defaultPropertyBrowseContexts;
372 protected IPropertyPage createPropertyPage(IWorkbenchPartSite site, Set<String> contexts) {
373 return new StandardPropertyPage(site, contexts);
376 protected String getPopupId() {
377 return "#ModelingDiagramPopup"; //$NON-NLS-1$
380 protected void getPreferences() {
381 this.diagramPreferences = DiagramPreferenceUtil.getPreferences();
384 protected void initSession() {
385 sessionContextProvider = Simantics.getSessionContextProvider();
386 sessionContext = sessionContextProvider.getSessionContext();
389 protected void readNames() {
390 String name = getEditorInput().getName();
391 host.doSetPartName(name);
392 host.doSetTitleToolTip(name);
395 class ChassisListener implements IChassisListener {
397 public void chassisClosed(ICanvasChassis sender) {
398 // Prevent deadlock while disposing which using syncExec would result in.
399 final ICanvasContext ctx = canvasContext;
400 ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() {
404 saveEditorState(ctx);
405 ctx.getHintStack().removeKeyHintListener(GridPainter.KEY_GRID_ENABLED, canvasHintListener);
406 ctx.getHintStack().removeKeyHintListener(RulerPainter.KEY_RULER_ENABLED, canvasHintListener);
407 final AWTChassis awtChassis = c.getAWTComponent();
408 if (awtChassis != null)
409 awtChassis.setCanvasContext(null);
413 if (sourceDiagramContainer != null) {
414 sourceDiagramContainer.set(null);
415 sourceDiagramContainer = null;
418 if (sourceDiagram != null)
419 sourceDiagram.dispose();
421 if(synchronizer != null) {
422 synchronizer.dispose();
427 if (runtimeDiagramManager != null) {
428 runtimeDiagramManager.dispose();
429 runtimeDiagramManager = null;
433 c.removeChassisListener(ChassisListener.this);
437 protected void createChassis(Composite parent) {
438 resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
439 c = new SWTChassis(parent, 0);
441 Object task = BEGIN("DV.precreateParticipants"); //$NON-NLS-1$
442 createCustomParticipants();
445 c.populate(component -> {
447 c.addChassisListener(new ChassisListener());
453 protected void beforeSetCanvasContext(ICanvasContext canvasContext2) {
457 * Invoke this only from the AWT thread.
460 protected void setCanvasContext(ICanvasContext context) {
461 // Cannot directly invoke SWTChassis.setCanvasContext only because it
462 // needs to be invoked in the SWT thread and AWTChassis.setCanvasContext in the
463 // AWT thread, but directly invoking SWTChassis.setCanvasContext would call both
464 // in the SWT thread which would cause synchronous scheduling of AWT
465 // runnables which is always a potential source of deadlocks.
466 c.getAWTComponent().setCanvasContext(canvasContext);
467 swt.asyncExec(new Runnable() {
471 // For AWT, this is a no-operation.
472 c.setCanvasContext(canvasContext);
477 public void createPartControl(Composite parent) {
478 //ProfileObserver.begin = System.nanoTime();
479 display = parent.getDisplay();
480 swt = SWTThread.getThreadAccess(display);
481 statusLineManager = getEditorSite().getActionBars().getStatusLineManager();
483 Object task = BEGIN("DV.initSession"); //$NON-NLS-1$
487 diagramResource = getInputResource();
492 this.runtimeDiagramManager = RuntimeDiagramManager.track(sessionContext.getSession(), diagramResource, getEditorInput(), this);
494 // Create the canvas context here before finishing createPartControl
495 // to give DiagramViewerActionContributor a chance to work.
496 // The context can be created in SWT thread without scheduling
497 // to the context thread and having potential deadlocks.
498 IThreadWorkQueue thread = AWTThread.getThreadAccess();
499 this.canvasContext = new CanvasContext(thread);
500 this.canvasContext.setLocked(true);
502 task = BEGIN("DV.createChassis"); //$NON-NLS-1$
503 createChassis(parent);
505 } catch (DatabaseException e) {
506 ErrorLogger.defaultLogError(e);
512 * A method invoked before chassis construction for creating such
513 * {@link ICanvasParticipant}s that need to be constructed in the SWT
516 * Use it for creating any such canvas participants during the viewer
517 * construction and add them to the {@link ICanvasContext} later on from
520 protected void createCustomParticipants() {
521 if (SimanticsUI.isLinuxGTK()) {
522 popupMenuParticipant = new SWTPopupMenuParticipantAwt(getSite(), c, display, getPopupId());
524 popupMenuParticipant = new SWTPopupMenuParticipant(getSite(), c, display, getPopupId());
529 * Invoke this only from the AWT thread.
531 protected void initializeCanvas() {
532 Object canvasInit = BEGIN("DV.canvasInitialization"); //$NON-NLS-1$
534 Object task = BEGIN("DV.createViewerCanvas"); //$NON-NLS-1$
535 initializeCanvasContext(canvasContext);
538 beforeSetCanvasContext(canvasContext);
539 //FullscreenUtils.addFullScreenHandler(canvasContext, s, canvasProvider);
541 // Without this all diagram participants will crash like there's no tomorrow.
542 // Just trying to keep the behavior a bit more sane in case of
543 // errors instead of flooding the console with exceptions.
544 canvasContext.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, Diagram.spawnNew(DiagramClass.DEFAULT));
546 // Changes in ruler/grid activity shall be written as
547 // workspace-persistent diagram-specific preferences.
548 canvasContext.getHintStack().addKeyHintListener(GridPainter.KEY_GRID_ENABLED, canvasHintListener);
549 canvasContext.getHintStack().addKeyHintListener(RulerPainter.KEY_RULER_ENABLED, canvasHintListener);
551 task = BEGIN("DV.setCanvasContext"); //$NON-NLS-1$
552 setCanvasContext(canvasContext);
555 // Finish loading in a worker thread because it may be a time consuming
556 // process to load a large diagram and we don't want unnecessary AWT
557 // thread contention.
558 Job loadJob = new DiagramViewerLoadJob(this);
564 protected void activateUiContexts(ContextUtil util) {
565 util.activate(DIAGRAMMING_CONTEXT);
569 * @param monitor the progress monitor to use for reporting progress to the
570 * user. It is the caller's responsibility to call done() on the
571 * given monitor. Accepts <code>null</code>, indicating that no
572 * progress should be reported and that the operation cannot be
575 protected void performActivation(IProgressMonitor monitor) {
576 SubMonitor progress = SubMonitor.convert(monitor, Messages.DiagramViewer_MonitorActivateMapping, 100);
577 IActivationManager activationManager = sessionContext.getSession().peekService(IActivationManager.class);
578 if (activationManager != null) {
579 activation = activationManager.activate(diagramResource);
581 progress.worked(100);
584 protected void onCreated() {
590 protected void scheduleZoomToFit(IDiagram diagram) {
592 throw new IllegalStateException("diagram is null"); //$NON-NLS-1$
594 CanvasUtils.scheduleZoomToFit(swt, () -> disposed, canvasContext, diagram);
598 * Subclasses may override but should always invoke super.
601 * @param initialHints
602 * @throws DatabaseException
604 protected void fillInitialDiagramHints(Resource diagram, IHintContext initialHints) throws DatabaseException {
605 IModelingRules modelingRules = sessionContext.getSession().syncRequest(
606 DiagramRequests.getModelingRules(diagram, null));
607 if (modelingRules != null) {
608 initialHints.setHint(DiagramModelHints.KEY_MODELING_RULES, modelingRules);
609 initialHints.setHint(DiagramHints.CONNECTION_ADVISOR,
610 getConnectionAdvisor(modelingRules, sessionContext.getSession()));
613 initialHints.setHint(SynchronizationHints.COPY_ADVISOR, getCopyAdvisor());
614 initialHints.setHint(DiagramHints.KEY_USE_CONNECTION_FLAGS, Boolean.TRUE);
615 initialHints.setHint(DiagramHints.KEY_ALLOW_CONNECTION_BRANCHING, Boolean.TRUE);
616 initialHints.setHint(DiagramHints.KEY_ALLOW_ROUTE_POINTS, Boolean.TRUE);
620 * @param monitor the progress monitor to use for reporting progress to the
621 * user. It is the caller's responsibility to call done() on the
622 * given monitor. Accepts <code>null</code>, indicating that no
623 * progress should be reported and that the operation cannot be
627 * @throws DatabaseException
629 protected IDiagram loadDiagram(IProgressMonitor monitor, final Resource r) throws DatabaseException {
630 // Pre-load modeling rules and possibly other hints too since they are
631 // needed already while loading the diagram contents.
632 IHintContext initialHints = new HintContext();
633 fillInitialDiagramHints(r, initialHints);
634 IDiagram d = loadDiagram(monitor, r, initialHints);
639 * @param monitor the progress monitor to use for reporting progress to the
640 * user. It is the caller's responsibility to call done() on the
641 * given monitor. Accepts <code>null</code>, indicating that no
642 * progress should be reported and that the operation cannot be
645 * @param initialHints
647 * @throws DatabaseException
649 protected IDiagram loadDiagram(IProgressMonitor monitor, Resource diagram, IHintContext initialHints) throws DatabaseException {
650 RuntimeDiagramManager rtdm = runtimeDiagramManager;
651 Resource runtimeDiagram = rtdm != null ? rtdm.getRuntimeDiagram() : null;
652 IDiagramLoader loader = synchronizer;
653 if (rtdm == null || runtimeDiagram == null || loader == null)
655 IDiagram d = sessionContext.getSession().syncRequest((Read<IDiagram>) graph -> {
656 IDiagram result = DiagramRequests.loadDiagram(monitor, getResourceInput2().getModel(null), diagram,
657 runtimeDiagram, null, loader, initialHints).perform(graph);
659 // #399: Enable certain PropertyTester implementation without database transactions
660 ModelingResources MOD = ModelingResources.getInstance(graph);
661 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
663 DiagramModelHints.KEY_DIAGRAM_RESOURCE_TYPE_URIS,
664 graph.syncRequest(new TypeURIs(diagram)));
666 DiagramModelHints.KEY_MAPPED_COMPOSITE_RESOURCE_TYPE_URIS,
667 composite != null ? graph.syncRequest(new TypeURIs(composite)) : Collections.emptySet());
669 Resource diagramSource = graph.syncRequest(
670 new PossibleObject(diagram, MOD.HasDiagramSource),
671 hasDiagramSourceListener = new HasDiagramSourceListener(sourceDiagramContainer));
672 ElementUtils.setOrRemoveHint(
674 DiagramModelHints.KEY_HAS_DIAGRAM_SOURCE,
682 protected void beforeSetDiagram(IDiagram diagram) {
685 protected PointerInteractor getPointerInteractor() {
686 return new PointerInteractor2(true, true, true, false, true, false, synchronizer.getElementClassProvider());
689 protected IConnectionAdvisor getConnectionAdvisor(IModelingRules modelingRules, Session session) {
690 return new ModelledConnectionAdvisor(modelingRules, sessionContext.getSession());
693 protected GraphToDiagramSynchronizer createSynchronizer(final ICanvasContext ctx, final ISessionContext sessionContext) {
695 return sessionContext.getSession().syncRequest(new Read<GraphToDiagramSynchronizer>() {
697 public GraphToDiagramSynchronizer perform(ReadGraph graph) throws DatabaseException {
698 GraphToDiagramSynchronizer sync = new GraphToDiagramSynchronizer(graph, ctx, createElementClassProvider(graph));
699 initializeSynchronizationContext(graph, sync);
703 } catch (DatabaseException e) {
704 throw new UnsupportedOperationException("Failed to initialize data model synchronizer", e); //$NON-NLS-1$
708 protected void initializeSynchronizationContext(ReadGraph graph, IModifiableSynchronizationContext context) {
709 context.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));
712 protected IElementClassProvider createElementClassProvider(ReadGraph graph) {
713 DiagramResource dr = DiagramResource.getInstance(graph);
714 return ElementClassProviders.mappedProvider(
715 ElementClasses.CONNECTION, ConnectionClass.CLASS.newClassWith(new StaticObjectAdapter(dr.RouteGraphConnection)),
716 ElementClasses.FLAG, FlagClassFactory.createFlagClass(dr.Flag, dr.Flag_Terminal)
720 protected SimpleElementTransformHandler getTransformHandler() {
721 return new SimpleElementTransformHandler(true, true, true);
724 public void initializeCanvasContext(CanvasContext ctx) {
725 IHintContext h = ctx.getDefaultHintContext();
727 // The canvas context should not be painted until it is ready to avoid
728 // unnecessary visual glitches.
729 h.setHint(Hints.KEY_DISABLE_PAINTING, Boolean.TRUE);
731 Object task = BEGIN("createSynchronizer"); //$NON-NLS-1$
732 this.synchronizer = createSynchronizer(ctx, sessionContext);
735 IContextService contextService = (IContextService) getSite().getService(IContextService.class);
736 contextUtil = new ContextUtil(contextService, swt);
738 // Support & Util Participants
739 ctx.add(new TransformUtil());
740 ctx.add(new MouseUtil());
741 ctx.add(new KeyUtil());
742 ctx.add(contextUtil);
743 ctx.add(new WorkbenchStatusLine(statusLineManager));
744 ctx.add(new CanvasGrab());
745 ctx.add(new SymbolUtil());
746 ctx.add(new TimeParticipant());
747 ctx.add(new CanvasBoundsParticipant());
748 ctx.add(new Notifications());
750 ctx.add(new SGFocusParticipant(c, DIAGRAMMING_CONTEXT));
752 // Debug participant(s)
753 // ctx.add( new PointerPainter() );
754 // ctx.add( new HandPainter() );
755 h.setHint(PointerPainter.KEY_PAINT_POINTER, true);
757 // Pan & Zoom & Rotate
758 addViewManipulationParticipants(ctx);
760 ctx.add(getTransformHandler());
761 ctx.add(new ExpandSelectionHandler(getEditorSite().getActionBars().getStatusLineManager()));
764 addKeyBindingParticipants(ctx);
766 // Grid & Ruler & Background
767 addGridRulerBackgroundParticipants(ctx);
769 h.setHint(Hints.KEY_DISPLAY_PAGE, diagramPreferences.get(DiagramPreferences.P_DISPLAY_PAGE_SIZE));
770 h.setHint(Hints.KEY_DISPLAY_MARGINS, diagramPreferences.get(DiagramPreferences.P_DISPLAY_MARGINS));
771 ctx.add(new PageBorderParticipant());
773 // h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f));
774 // h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.LIGHT_GRAY);
775 h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f));
776 h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.WHITE);
777 h.setHint(RulerPainter.KEY_RULER_BACKGROUND_COLOR, new Color(0.9f, 0.9f, 0.9f, 0.75f));
778 h.setHint(RulerPainter.KEY_RULER_TEXT_COLOR, Color.BLACK);
780 ////// Diagram Participants //////
781 addDiagramParticipants(ctx);
782 addPainterParticipants(ctx);
784 /////// D'n'D ///////
785 addDropParticipants(ctx);
787 h.setHint(ElementPainter.KEY_SELECTION_FRAME_COLOR, Color.MAGENTA);
788 h.setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
790 h.setHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT, 100000.0);
791 h.setHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT, 10.0);
793 Double snapResolution = diagramPreferences.get(DiagramPreferences.P_SNAP_GRID_SIZE);
794 this.snapAdvisor = new GridSnapAdvisor(snapResolution);
795 h.setHint(DiagramHints.SNAP_ADVISOR, this.snapAdvisor);
796 h.setHint(GridPainter.KEY_GRID_SIZE, snapResolution);
797 h.setHint(GridPainter.KEY_GRID_ENABLED, Boolean.FALSE);
799 // Workbench selection provider
800 addStructureParticipants(ctx);
805 // Load page frame description settings
806 loadPageSettings(ctx);
808 addOtherParticipants(ctx);
810 ctx.assertParticipantDependencies();
811 ctx.setLocked(false);
814 protected void addGridRulerBackgroundParticipants(CanvasContext ctx) {
815 ctx.add(new GridPainter());
816 ctx.add(new RulerPainter());
817 ctx.add(new BackgroundPainter());
820 protected void loadPageSettings(ICanvasContext ctx) {
821 DiagramDesc diagramDesc = null;
823 // load diagram page configuration
824 if (diagramResource != null) {
826 diagramDesc = sessionContext.getSession().syncRequest(DiagramRequests.getDiagramDesc(diagramResource));
827 } catch (DatabaseException e) {
828 ErrorLogger.defaultLogError(e);
832 if (diagramDesc == null) {
833 // Take page description from the preferences if nothing else is available.
834 final DiagramDesc desc = diagramDesc = diagramPreferences.getDiagramDesc();
836 // Write page configuration to graph
837 sessionContext.getSession().asyncRequest(new WriteRequest() {
839 public void perform(WriteGraph graph) throws DatabaseException {
840 if (graph.isImmutable(diagramResource))
842 CommonDBUtils.selectClusterSet(graph, diagramResource);
843 DiagramGraphUtil.setDiagramDesc(graph, diagramResource, desc);
846 if (parameter != null)
847 ErrorLogger.defaultLogError("Failed to write default diagram page description to database, see exception for details", parameter); //$NON-NLS-1$
851 setDiagramDesc(ctx, diagramDesc);
853 // Create a listener to react to page setting changes.
854 sessionContext.getSession().asyncRequest(DiagramRequests.getDiagramDesc(diagramResource),
855 new ListenerDelegate<DiagramDesc>(this) {
857 public void execute(final DiagramDesc result) {
858 if (result != null && canvasContext != null) {
859 ThreadUtils.asyncExec(canvasContext.getThreadAccess(), new Runnable() {
863 setDiagramDesc(canvasContext, result);
871 protected void setDiagramDesc(ICanvasContext ctx, DiagramDesc diagramDesc) {
872 if (diagramDesc == null)
873 throw new NullPointerException("null diagram desc"); //$NON-NLS-1$
875 if (diagramDesc.equals(this.diagramDesc))
878 this.diagramDesc = diagramDesc;
879 IHintContext hints = ctx.getDefaultHintContext();
880 hints.setHint(Hints.KEY_PAGE_DESC, diagramDesc.getPageDesc());
881 hints.setHint(Hints.KEY_DISPLAY_PAGE, diagramDesc.isPageBordersVisible());
882 hints.setHint(Hints.KEY_DISPLAY_MARGINS, diagramDesc.isMarginsVisible());
883 hints.setHint(GridPainter.KEY_GRID_ENABLED, diagramDesc.isGridVisible());
884 hints.setHint(RulerPainter.KEY_RULER_ENABLED, diagramDesc.isRulerVisible());
885 snapAdvisor.setResolution(diagramDesc.getGridSize());
886 hints.setHint(GridPainter.KEY_GRID_SIZE, diagramDesc.getGridSize());
890 * Must be invoked from the AWT thread only.
895 protected void applyEditorState(final EditorState state, final ICanvasContext ctx) {
896 final IDiagram diagram = ctx.getHintStack().getHint(DiagramHints.KEY_DIAGRAM);
898 if (state.viewTransform != null && state.viewTransform.getDeterminant() != 0) {
899 for (PanZoomRotateHandler h : ctx.getItemsByClass(PanZoomRotateHandler.class)) {
900 h.setTransform(state.viewTransform);
904 if (diagram != null) {
905 if (state.viewTransform != null)
906 diagram.removeHint(DiagramHints.KEY_INITIAL_ZOOM_TO_FIT);
908 if (state.toolMode != null)
909 ctx.getDefaultHintContext().setHint(Hints.KEY_TOOL, state.toToolMode());
911 ctx.getDefaultHintContext().setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
913 final Set<IElement> selected = DiagramEditorStates.toElements(state.selection, diagram);
914 if (!selected.isEmpty()) {
915 for (Selection s : ctx.getItemsByClass(Selection.class)) {
916 s.setSelection(0, selected);
922 protected EditorState getSavedEditorState(ICanvasContext ctx) {
923 return DiagramEditorStates.toEditorState(ctx, true, true, true);
926 protected void saveEditorState(ICanvasContext ctx) {
927 DiagramEditorStates.saveEditorState(PREFERENCE_VIRTUAL_GRAPH, diagramResource, getSavedEditorState(ctx) , DiagramViewer.this);
930 private boolean firstFocus = true;
932 public void setFocus() {
938 // This is a workaround for using the symbol viewer in multi-page
939 // editors which causes the first zoom-to-fit scheduling to happen
940 // already before the controls have been laid out properly.
949 protected void firstTimeSetFocus() {
950 // [Tuukka@2010-02-11]
951 // Removed since this is actually a workaround for multi-page editors.
952 //scheduleZoomToFit();
955 public void dispose() {
956 // Deactivate all contexts here because after this the context service
957 // in question will not be available.
958 if (contextUtil != null) {
959 contextUtil.deactivateAll();
964 if (hasDiagramSourceListener != null) {
965 hasDiagramSourceListener.dispose();
966 hasDiagramSourceListener = null;
969 if (activation != null) {
970 activation.deactivate();
974 if (resourceManager != null) {
975 resourceManager.dispose();
976 resourceManager = null;
981 protected Resource getInputResource() {
982 return getResourceInput().getResource();
985 public IResourceEditorInput getResourceInput() {
986 return (IResourceEditorInput) getEditorInput();
989 public IResourceEditorInput2 getResourceInput2() {
990 return (IResourceEditorInput2) getEditorInput();
993 public void init(DiagramViewerHost _host, IEditorSite site, IEditorInput input, DataContainer<IDiagram> diagramContainer, WorkbenchSelectionProvider selectionProvider) {
994 if (!(input instanceof IResourceEditorInput))
995 throw new RuntimeException("Invalid input: must be IResourceEditorInput"); //$NON-NLS-1$
1000 this.sourceDiagramContainer = diagramContainer;
1001 this.selectionProvider = selectionProvider;
1003 // Set initial part name according to the name given by IEditorInput
1004 host.doSetPartName(getEditorInput().getName());
1006 Session session = Simantics.peekSession();
1007 if (session != null) {
1008 Supplier<Boolean> disposedCallback = () -> disposed;
1009 session.asyncRequest(
1010 new TitleRequest(site.getId(), getResourceInput()),
1011 new TitleUpdater(site.getShell().getDisplay(), host::doSetPartName, disposedCallback));
1012 session.asyncRequest(
1013 new ToolTipRequest(site.getId(), getResourceInput()),
1014 new TitleUpdater(site.getShell().getDisplay(), host::doSetTitleToolTip, disposedCallback));
1018 // Read previous editor state from the database
1019 editorState = DiagramEditorStates.readEditorState(getInputResource());
1020 } catch (DatabaseException e) {
1025 @SuppressWarnings("unchecked")
1027 public <T> T getAdapter(Class<T> adapter) {
1028 // System.out.println("diagramViewer getAdapter " + adapter);
1029 // Property view support
1030 if (adapter == IPropertyPage.class)
1031 return (T) createPropertyPage(getSite(), getPropertyPageContexts());
1032 // Provide symbols for the editor
1033 if (adapter == SymbolProviderFactory.class) {
1035 return (T) DiagramTypeUtils.readSymbolProviderFactory(sessionContext.getSession(), diagramResource);
1036 } catch (DatabaseException e) {
1037 ErrorLogger.defaultLogError(getClass() + " failed to adapt to SymbolProviderFactory, see exception for details.", e); //$NON-NLS-1$
1041 // Outline view support
1042 if (adapter == IContentOutlinePage.class)
1043 return (T) new DiagramOutlinePage(sessionContextProvider, getResourceInput2());
1044 // Role view support
1045 if (adapter == ILayersViewPage.class)
1046 return (T) new DiagramLayersPage(sourceDiagram, canvasContext);
1047 // Support external steering of the diagram canvas, zooming etc.
1048 if (adapter == ICanvasContext.class)
1049 return (T) canvasContext;
1050 // Support scene graph debugger view
1051 if (adapter == INode.class) {
1052 if (canvasContext != null) {
1053 INode node = canvasContext.getCanvasNode();
1055 return (T) NodeUtil.getRootNode(node);
1059 // Support retrieval of the current diagram.
1060 if (adapter == IDiagram.class)
1061 return (T) sourceDiagram;
1062 // Why is this here ??
1063 if (adapter == Session.class)
1064 return (T) sessionContext.getSession();
1065 if(adapter == RuntimeDiagramManager.class)
1066 return (T) runtimeDiagramManager;
1067 if (adapter == Resource.class)
1068 return (T) getRuntime();
1069 if (adapter == ICanvasChassis.class)
1075 //-------------------------------------------------------------------------
1078 protected static Object BEGIN(String name) {
1080 //return ThreadLog.BEGIN(name);
1085 protected static void END(Object task) {
1087 //((Task) task).end();
1091 //-------------------------------------------------------------------------
1092 // implement ListenerSupport
1095 public void exception(Throwable t) {
1096 ErrorLogger.defaultLogError(t);
1100 public boolean isDisposed() {
1104 protected void collectGarbage() {
1105 SessionGarbageCollectorJob.getInstance().schedule(0);
1106 AWTThread.getThreadAccess().asyncExec(new Runnable() {
1109 //System.out.println("BEFORE CLEAR: " + SVGCache.getSVGUniverse().report());
1110 SVGCache.getSVGUniverse().clearUnreferenced();
1111 //System.out.println("AFTER CLEAR: " + SVGCache.getSVGUniverse().report());
1116 //-------------------------------------------------------------------------
1117 // Listener for certain canvas context hint changes
1119 IHintListener canvasHintListener = new HintListenerAdapter() {
1121 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
1122 if (key == GridPainter.KEY_GRID_ENABLED) {
1123 boolean v = Boolean.TRUE.equals(newValue);
1124 if (diagramDesc.isGridVisible() != v)
1125 setGlobalPreference(DiagramResource.URIs.DisplayGrid, v);
1126 } else if (key == RulerPainter.KEY_RULER_ENABLED) {
1127 boolean v = Boolean.TRUE.equals(newValue);
1128 if (diagramDesc.isRulerVisible() != v)
1129 setGlobalPreference(DiagramResource.URIs.DisplayRuler, v);
1134 private <T> void setGlobalPreference(final String preferenceURI, boolean value) {
1135 TagUtil.execute(Simantics.getSession(), PREFERENCE_VIRTUAL_GRAPH, preferenceURI, value, Simantics.getProjectResource());
1139 * --------------------------------------------------------------------
1140 * Changes related to removal of EditorPart extension from here on down
1141 * --------------------------------------------------------------------
1144 private IWorkbenchPartSite partSite;
1145 private DiagramViewerHost host;
1146 private IEditorInput editorInput = null;
1149 * Method declared on IWorkbenchPart.
1151 public IWorkbenchPartSite getSite() {
1155 public IEditorSite getEditorSite() {
1156 return (IEditorSite) getSite();
1159 public IEditorInput getEditorInput() {
1163 // protected void setPartName(String partName) {
1164 //// if (compatibilityTitleListener != null) {
1165 //// removePropertyListener(compatibilityTitleListener);
1166 //// compatibilityTitleListener = null;
1169 //// super.setPartName(partName);
1172 // protected void setTitleToolTip(String toolTip) {
1173 //// toolTip = Util.safeString(toolTip);
1174 //// //Do not send changes if they are the same
1175 //// if (Util.equals(this.toolTip, toolTip)) {
1178 //// this.toolTip = toolTip;
1179 //// firePropertyChange(IWorkbenchPart.PROP_TITLE);
1182 protected void setHost(DiagramViewerHost host) {
1186 protected void setSite(IWorkbenchPartSite site) {
1187 this.partSite = site;
1190 protected void setInput(IEditorInput input) {
1191 Assert.isLegal(input != null);
1192 editorInput = input;
1195 public Composite getComposite() {