/******************************************************************************* * Copyright (c) 2007, 2019 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 * Semantum Oy - issue #4384, gitlab #399 *******************************************************************************/ package org.simantics.modeling.ui.diagramEditor; import java.awt.Color; import java.awt.dnd.DnDConstants; import java.util.Collections; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.simantics.Simantics; import org.simantics.browsing.ui.model.browsecontexts.BrowseContext; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.WriteGraph; import org.simantics.db.common.primitiverequest.PossibleAdapter; import org.simantics.db.common.primitiverequest.PossibleObject; import org.simantics.db.common.procedure.adapter.ListenerDelegate; import org.simantics.db.common.procedure.adapter.ListenerSupport; import org.simantics.db.common.request.ParametrizedRead; import org.simantics.db.common.request.TypeURIs; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.CommonDBUtils; import org.simantics.db.common.utils.TagUtil; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.combinations.Combinators; import org.simantics.db.management.ISessionContext; import org.simantics.db.management.ISessionContextProvider; import org.simantics.db.request.Read; import org.simantics.diagram.DiagramTypeUtils; import org.simantics.diagram.adapter.FlagClassFactory; import org.simantics.diagram.adapter.GraphToDiagramSynchronizer; import org.simantics.diagram.adapter.IDiagramLoader; import org.simantics.diagram.connection.ModelledConnectionAdvisor; import org.simantics.diagram.handler.ConnectionCommandHandler; 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.ExpandSelectionHandler; import org.simantics.diagram.handler.SimpleElementTransformHandler; import org.simantics.diagram.layer.ILayersViewPage; import org.simantics.diagram.participant.ContextUtil; import org.simantics.diagram.participant.PointerInteractor2; import org.simantics.diagram.participant.SGFocusParticipant; import org.simantics.diagram.query.DiagramRequests; import org.simantics.diagram.runtime.RuntimeDiagramManager; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.symbolcontribution.SymbolProviderFactory; import org.simantics.diagram.synchronization.CopyAdvisor; import org.simantics.diagram.synchronization.IModifiableSynchronizationContext; import org.simantics.diagram.synchronization.SynchronizationHints; import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.diagram.ui.DiagramModelHints; import org.simantics.diagram.ui.SWTPopupMenuParticipant; import org.simantics.diagram.ui.SWTPopupMenuParticipantAwt; import org.simantics.diagram.ui.WorkbenchSelectionProvider; import org.simantics.g2d.canvas.Hints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.ICanvasParticipant; import org.simantics.g2d.canvas.impl.CanvasContext; import org.simantics.g2d.chassis.AWTChassis; import org.simantics.g2d.chassis.ICanvasChassis; import org.simantics.g2d.chassis.IChassisListener; import org.simantics.g2d.chassis.SWTChassis; import org.simantics.g2d.connection.IConnectionAdvisor; import org.simantics.g2d.diagram.DiagramClass; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.PickRequest.PickFilter; import org.simantics.g2d.diagram.impl.Diagram; import org.simantics.g2d.diagram.participant.DelayedBatchElementPainter; 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.TerminalPainter; 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.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.IElementClassProvider; import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; import org.simantics.g2d.elementclass.connection.ConnectionClass; 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.KeyUtil; import org.simantics.g2d.participant.MouseUtil; 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.RenderingQualityInteractor; 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.WorkbenchStatusLine; import org.simantics.g2d.participant.ZoomToAreaHandler; import org.simantics.g2d.tooltip.TerminalTooltipParticipant; import org.simantics.g2d.utils.CanvasUtils; import org.simantics.layer0.utils.triggers.IActivation; import org.simantics.layer0.utils.triggers.IActivationManager; 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.handlers.LinkBrowsingHandler; import org.simantics.modeling.ui.diagramEditor.handlers.StructuralBrowsingHandler; import org.simantics.modeling.ui.preferences.DiagramPreferenceUtil; import org.simantics.modeling.ui.preferences.DiagramPreferences; import org.simantics.project.ontology.ProjectResource; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.g2d.snap.GridSnapAdvisor; import org.simantics.scenegraph.utils.NodeUtil; import org.simantics.selectionview.StandardPropertyPage; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.modelingRules.IModelingRules; import org.simantics.ui.SimanticsUI; import org.simantics.ui.jobs.SessionGarbageCollectorJob; import org.simantics.ui.workbench.IPropertyPage; import org.simantics.ui.workbench.IResourceEditorInput; import org.simantics.ui.workbench.IResourceEditorInput2; import org.simantics.ui.workbench.TitleRequest; import org.simantics.ui.workbench.TitleUpdater; import org.simantics.ui.workbench.ToolTipRequest; import org.simantics.ui.workbench.editor.input.InputValidationCombinators; import org.simantics.utils.DataContainer; import org.simantics.utils.datastructures.hints.HintContext; import org.simantics.utils.datastructures.hints.HintListenerAdapter; import org.simantics.utils.datastructures.hints.IHintContext; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintListener; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.threads.AWTThread; import org.simantics.utils.threads.IThreadWorkQueue; import org.simantics.utils.threads.SWTThread; import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.ui.ErrorLogger; import org.simantics.utils.ui.ExceptionUtils; import com.kitfox.svg.SVGCache; /** * @author Toni Kalajainen * @author Tuukka Lehtonen */ public class DiagramViewer //extends EditorPart implements IResourceEditorPart2, implements ListenerSupport, IAdaptable { public interface DiagramViewerHost { void doSetPartName(String name); void doSetTitleToolTip(String name); } public static final String DIAGRAMMING_CONTEXT = "org.simantics.modeling.ui.diagramming"; //$NON-NLS-1$ private static final String PREFERENCE_VIRTUAL_GRAPH = "preferences"; //$NON-NLS-1$ private static final boolean PROFILE = false; ParametrizedRead INPUT_VALIDATOR = Combinators.compose( InputValidationCombinators.or( // Normal configuration diagrams of a model Combinators.compose( InputValidationCombinators.hasURI(), InputValidationCombinators.partialFunction(ModelingResources.URIs.DiagramToComposite) ), // Configuration diagrams of a component type Combinators.compose( InputValidationCombinators.hasURI(), Combinators.compose( InputValidationCombinators.partialFunction(StructuralResource2.URIs.Defines), InputValidationCombinators.partialFunction(ModelingResources.URIs.DiagramToComposite) ) ) ), InputValidationCombinators.extractInputResource() ); protected EditorState editorState; protected boolean disposed = false; protected IThreadWorkQueue swt; protected IStatusLineManager statusLineManager; protected Display display; protected LocalResourceManager resourceManager; protected SWTChassis c; protected IDiagram sourceDiagram; protected DataContainer sourceDiagramContainer; protected CanvasContext canvasContext; protected ISessionContextProvider sessionContextProvider; protected ISessionContext sessionContext; protected Resource diagramResource; protected GraphToDiagramSynchronizer synchronizer; protected IActivation activation; protected ContextUtil contextUtil; protected SWTPopupMenuParticipant popupMenuParticipant; protected DiagramPreferences diagramPreferences; protected DiagramDesc diagramDesc; protected GridSnapAdvisor snapAdvisor; private RuntimeDiagramManager runtimeDiagramManager; private Resource runtimeDiagramResourceCache; private HasDiagramSourceListener hasDiagramSourceListener; /** * Set externally in * {@link #init(DiagramViewerHost, IEditorSite, IEditorInput, DataContainer, WorkbenchSelectionProvider)} * . */ protected WorkbenchSelectionProvider selectionProvider; protected Resource getRuntimeFromManager() { RuntimeDiagramManager rtdm = runtimeDiagramManager; return (rtdm == null) ? null : rtdm.getRuntimeDiagram(); } public Resource getRuntime() { if (runtimeDiagramResourceCache != null) return runtimeDiagramResourceCache; runtimeDiagramResourceCache = getRuntimeFromManager(); return runtimeDiagramResourceCache; } public ParametrizedRead getInputValidator() { return INPUT_VALIDATOR; } protected void addDropParticipants(ICanvasContext ctx) { ctx.getDefaultHintContext().setHint(Hints.KEY_ALLOWED_DRAG_ACTIONS, DnDConstants.ACTION_COPY); ctx.add(new PopulateElementDropParticipant(synchronizer, getSite())); ctx.add(new PopulateElementMonitorDropParticipant(synchronizer, 0.5, 0.5)); } protected CopyPasteStrategy getCopyPasteStrategy() { try { CopyPasteStrategy cpStrategy = Simantics.getSession().syncRequest(new PossibleAdapter(getInputResource(), CopyPasteStrategy.class)); if(cpStrategy != null) return cpStrategy; } catch (DatabaseException e) { } return getDefaultCopyPasteStrategy(); } protected CopyPasteStrategy getDefaultCopyPasteStrategy() { return new DefaultCopyPasteStrategy(); } protected CopyAdvisor getCopyAdvisor() { try { CopyAdvisor advisor = Simantics.getSession().syncRequest(new PossibleAdapter(getInputResource(), CopyAdvisor.class)); if(advisor != null) return advisor; } catch (DatabaseException e) { } return new MappedElementCopyAdvisor(new ElementCopyAdvisor(), new ComponentCopyAdvisor()); } /** * @param ctx * TODO: change argument from CanvasContext to ICanvasContext */ protected void addKeyBindingParticipants(CanvasContext ctx) { //ctx.add( new KeyToCommand( CommandKeyBinding.DEFAULT_BINDINGS ) ); ctx.add(new DeleteHandler(statusLineManager)); ctx.add(new CopyPasteHandler(getCopyPasteStrategy(), statusLineManager).setWorkbenchSite(getEditorSite())); ctx.add(new ConnectionCommandHandler()); } protected void addPopupmenu(ICanvasContext ctx) { ctx.add(popupMenuParticipant); } protected void addWorkbenchSelectionProvider(ICanvasContext ctx) { if (selectionProvider != null) ctx.add(selectionProvider); } protected void addViewManipulationParticipants(CanvasContext ctx) { ctx.add(new PanZoomRotateHandler()); //ctx.add(new MousePanZoomInteractor()); //ctx.add(new MultitouchPanZoomRotateInteractor()); // ctx.add( new OrientationRestorer() ); ctx.add(new ZoomToAreaHandler()); } protected void addDiagramParticipants(ICanvasContext ctx) { ctx.add(new ZOrderHandler()); ctx.add(getPointerInteractor()); ctx.add(new ElementInteractor()); ctx.add(new Selection()); ctx.add(new DiagramParticipant()); ctx.add(new ElementPainter()); //ctx.add(new ElementHeartbeater()); //ctx.add(new ZoomTransitionParticipant(TransitionFunction.SIGMOID)); //ctx.add(new TooltipParticipant()); ctx.add(new TerminalTooltipParticipant()); } protected void addPainterParticipants(ICanvasContext ctx) { ctx.add(new RenderingQualityInteractor()); ctx.add(new TerminalPainter(true, true, false, true)); ctx.add(new DelayedBatchElementPainter(PickFilter.FILTER_MONITORS, 500, TimeUnit.MILLISECONDS)); } protected void addStructureParticipants(ICanvasContext ctx) { addWorkbenchSelectionProvider(ctx); // Add visual browsing capabilities for structural models ctx.add(new StructuralBrowsingHandler(getSite(), sessionContext, getResourceInput2())); ctx.add(new LinkBrowsingHandler(getSite(), this, sessionContext)); } /** * Override to add any diagram/canvas participants to the canvas context * initialized for the editor. * * @param ctx */ protected void addOtherParticipants(CanvasContext ctx) { } public static Set defaultPropertyBrowseContexts = Collections.singleton(ProjectResource.URIs.ProjectBrowseContext); protected Set getPropertyPageContexts() { try { return BrowseContext.getBrowseContextClosure(Simantics.getSession(), defaultPropertyBrowseContexts); } catch (DatabaseException e) { ExceptionUtils.logAndShowError(Messages.DiagramViewer_FailedtoLoadModeled, e); return defaultPropertyBrowseContexts; } } protected IPropertyPage createPropertyPage(IWorkbenchPartSite site, Set contexts) { return new StandardPropertyPage(site, contexts); } protected String getPopupId() { return "#ModelingDiagramPopup"; //$NON-NLS-1$ } protected void getPreferences() { this.diagramPreferences = DiagramPreferenceUtil.getPreferences(); } protected void initSession() { sessionContextProvider = Simantics.getSessionContextProvider(); sessionContext = sessionContextProvider.getSessionContext(); } protected void readNames() { String name = getEditorInput().getName(); host.doSetPartName(name); host.doSetTitleToolTip(name); } class ChassisListener implements IChassisListener { @Override public void chassisClosed(ICanvasChassis sender) { // Prevent deadlock while disposing which using syncExec would result in. final ICanvasContext ctx = canvasContext; ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() { @Override public void run() { if (ctx != null) { saveEditorState(ctx); ctx.getHintStack().removeKeyHintListener(GridPainter.KEY_GRID_ENABLED, canvasHintListener); ctx.getHintStack().removeKeyHintListener(RulerPainter.KEY_RULER_ENABLED, canvasHintListener); final AWTChassis awtChassis = c.getAWTComponent(); if (awtChassis != null) awtChassis.setCanvasContext(null); ctx.dispose(); } if (sourceDiagramContainer != null) { sourceDiagramContainer.set(null); sourceDiagramContainer = null; } if (sourceDiagram != null) sourceDiagram.dispose(); if(synchronizer != null) { synchronizer.dispose(); // Let GC work. synchronizer = null; } if (runtimeDiagramManager != null) { runtimeDiagramManager.dispose(); runtimeDiagramManager = null; } } }); c.removeChassisListener(ChassisListener.this); } } protected void createChassis(Composite parent) { resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent); c = new SWTChassis(parent, 0); Object task = BEGIN("DV.precreateParticipants"); //$NON-NLS-1$ createCustomParticipants(); END(task); c.populate(component -> { if (!disposed) { c.addChassisListener(new ChassisListener()); initializeCanvas(); } }); } protected void beforeSetCanvasContext(ICanvasContext canvasContext2) { } /** * Invoke this only from the AWT thread. * @param context */ protected void setCanvasContext(ICanvasContext context) { // Cannot directly invoke SWTChassis.setCanvasContext only because it // needs to be invoked in the SWT thread and AWTChassis.setCanvasContext in the // AWT thread, but directly invoking SWTChassis.setCanvasContext would call both // in the SWT thread which would cause synchronous scheduling of AWT // runnables which is always a potential source of deadlocks. c.getAWTComponent().setCanvasContext(canvasContext); swt.asyncExec(new Runnable() { @Override public void run() { if (!c.isDisposed()) // For AWT, this is a no-operation. c.setCanvasContext(canvasContext); } }); } public void createPartControl(Composite parent) { //ProfileObserver.begin = System.nanoTime(); display = parent.getDisplay(); swt = SWTThread.getThreadAccess(display); statusLineManager = getEditorSite().getActionBars().getStatusLineManager(); Object task = BEGIN("DV.initSession"); //$NON-NLS-1$ initSession(); END(task); diagramResource = getInputResource(); readNames(); getPreferences(); try { this.runtimeDiagramManager = RuntimeDiagramManager.track(sessionContext.getSession(), diagramResource, getEditorInput(), this); // Create the canvas context here before finishing createPartControl // to give DiagramViewerActionContributor a chance to work. // The context can be created in SWT thread without scheduling // to the context thread and having potential deadlocks. IThreadWorkQueue thread = AWTThread.getThreadAccess(); this.canvasContext = new CanvasContext(thread); this.canvasContext.setLocked(true); task = BEGIN("DV.createChassis"); //$NON-NLS-1$ createChassis(parent); END(task); } catch (DatabaseException e) { ErrorLogger.defaultLogError(e); } } /** * A method invoked before chassis construction for creating such * {@link ICanvasParticipant}s that need to be constructed in the SWT * thread. * * Use it for creating any such canvas participants during the viewer * construction and add them to the {@link ICanvasContext} later on from * the AWT thread. */ protected void createCustomParticipants() { if (SimanticsUI.isLinuxGTK()) { popupMenuParticipant = new SWTPopupMenuParticipantAwt(getSite(), c, display, getPopupId()); } else { popupMenuParticipant = new SWTPopupMenuParticipant(getSite(), c, display, getPopupId()); } } /** * Invoke this only from the AWT thread. */ protected void initializeCanvas() { Object canvasInit = BEGIN("DV.canvasInitialization"); //$NON-NLS-1$ Object task = BEGIN("DV.createViewerCanvas"); //$NON-NLS-1$ initializeCanvasContext(canvasContext); END(task); beforeSetCanvasContext(canvasContext); //FullscreenUtils.addFullScreenHandler(canvasContext, s, canvasProvider); // Without this all diagram participants will crash like there's no tomorrow. // Just trying to keep the behavior a bit more sane in case of // errors instead of flooding the console with exceptions. canvasContext.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, Diagram.spawnNew(DiagramClass.DEFAULT)); // Changes in ruler/grid activity shall be written as // workspace-persistent diagram-specific preferences. canvasContext.getHintStack().addKeyHintListener(GridPainter.KEY_GRID_ENABLED, canvasHintListener); canvasContext.getHintStack().addKeyHintListener(RulerPainter.KEY_RULER_ENABLED, canvasHintListener); task = BEGIN("DV.setCanvasContext"); //$NON-NLS-1$ setCanvasContext(canvasContext); END(task); // Finish loading in a worker thread because it may be a time consuming // process to load a large diagram and we don't want unnecessary AWT // thread contention. Job loadJob = new DiagramViewerLoadJob(this); loadJob.schedule(); END(canvasInit); } protected void activateUiContexts(ContextUtil util) { util.activate(DIAGRAMMING_CONTEXT); } /** * @param monitor the progress monitor to use for reporting progress to the * user. It is the caller's responsibility to call done() on the * given monitor. Accepts null, indicating that no * progress should be reported and that the operation cannot be * cancelled. */ protected void performActivation(IProgressMonitor monitor) { SubMonitor progress = SubMonitor.convert(monitor, Messages.DiagramViewer_MonitorActivateMapping, 100); IActivationManager activationManager = sessionContext.getSession().peekService(IActivationManager.class); if (activationManager != null) { activation = activationManager.activate(diagramResource); } progress.worked(100); } protected void onCreated() { } /** * @param diagram */ protected void scheduleZoomToFit(IDiagram diagram) { if (diagram == null) throw new IllegalStateException("diagram is null"); //$NON-NLS-1$ CanvasUtils.scheduleZoomToFit(swt, () -> disposed, canvasContext, diagram); } /** * Subclasses may override but should always invoke super. * * @param diagram * @param initialHints * @throws DatabaseException */ protected void fillInitialDiagramHints(Resource diagram, IHintContext initialHints) throws DatabaseException { IModelingRules modelingRules = sessionContext.getSession().syncRequest( DiagramRequests.getModelingRules(diagram, null)); if (modelingRules != null) { initialHints.setHint(DiagramModelHints.KEY_MODELING_RULES, modelingRules); initialHints.setHint(DiagramHints.CONNECTION_ADVISOR, getConnectionAdvisor(modelingRules, sessionContext.getSession())); } initialHints.setHint(SynchronizationHints.COPY_ADVISOR, getCopyAdvisor()); initialHints.setHint(DiagramHints.KEY_USE_CONNECTION_FLAGS, Boolean.TRUE); initialHints.setHint(DiagramHints.KEY_ALLOW_CONNECTION_BRANCHING, Boolean.TRUE); initialHints.setHint(DiagramHints.KEY_ALLOW_ROUTE_POINTS, Boolean.TRUE); } /** * @param monitor the progress monitor to use for reporting progress to the * user. It is the caller's responsibility to call done() on the * given monitor. Accepts null, indicating that no * progress should be reported and that the operation cannot be * cancelled. * @param r * @return * @throws DatabaseException */ protected IDiagram loadDiagram(IProgressMonitor monitor, final Resource r) throws DatabaseException { // Pre-load modeling rules and possibly other hints too since they are // needed already while loading the diagram contents. IHintContext initialHints = new HintContext(); fillInitialDiagramHints(r, initialHints); IDiagram d = loadDiagram(monitor, r, initialHints); return d; } /** * @param monitor the progress monitor to use for reporting progress to the * user. It is the caller's responsibility to call done() on the * given monitor. Accepts null, indicating that no * progress should be reported and that the operation cannot be * cancelled. * @param diagram * @param initialHints * @return * @throws DatabaseException */ protected IDiagram loadDiagram(IProgressMonitor monitor, Resource diagram, IHintContext initialHints) throws DatabaseException { RuntimeDiagramManager rtdm = runtimeDiagramManager; Resource runtimeDiagram = rtdm != null ? rtdm.getRuntimeDiagram() : null; IDiagramLoader loader = synchronizer; if (rtdm == null || runtimeDiagram == null || loader == null) return null; IDiagram d = sessionContext.getSession().syncRequest((Read) graph -> { IDiagram result = DiagramRequests.loadDiagram(monitor, getResourceInput2().getModel(null), diagram, runtimeDiagram, null, loader, initialHints).perform(graph); // #399: Enable certain PropertyTester implementation without database transactions ModelingResources MOD = ModelingResources.getInstance(graph); Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite); result.setHint( DiagramModelHints.KEY_DIAGRAM_RESOURCE_TYPE_URIS, graph.syncRequest(new TypeURIs(diagram))); result.setHint( DiagramModelHints.KEY_MAPPED_COMPOSITE_RESOURCE_TYPE_URIS, composite != null ? graph.syncRequest(new TypeURIs(composite)) : Collections.emptySet()); Resource diagramSource = graph.syncRequest( new PossibleObject(diagram, MOD.HasDiagramSource), hasDiagramSourceListener = new HasDiagramSourceListener(sourceDiagramContainer)); ElementUtils.setOrRemoveHint( result, DiagramModelHints.KEY_HAS_DIAGRAM_SOURCE, diagramSource); return result; }); return d; } protected void beforeSetDiagram(IDiagram diagram) { } protected PointerInteractor getPointerInteractor() { return new PointerInteractor2(true, true, true, false, true, false, synchronizer.getElementClassProvider()); } protected IConnectionAdvisor getConnectionAdvisor(IModelingRules modelingRules, Session session) { return new ModelledConnectionAdvisor(modelingRules, sessionContext.getSession()); } protected GraphToDiagramSynchronizer createSynchronizer(final ICanvasContext ctx, final ISessionContext sessionContext) { try { return sessionContext.getSession().syncRequest(new Read() { @Override public GraphToDiagramSynchronizer perform(ReadGraph graph) throws DatabaseException { GraphToDiagramSynchronizer sync = new GraphToDiagramSynchronizer(graph, ctx, createElementClassProvider(graph)); initializeSynchronizationContext(graph, sync); return sync; } }); } catch (DatabaseException e) { throw new UnsupportedOperationException("Failed to initialize data model synchronizer", e); //$NON-NLS-1$ } } protected void initializeSynchronizationContext(ReadGraph graph, IModifiableSynchronizationContext context) { context.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph)); } protected IElementClassProvider createElementClassProvider(ReadGraph graph) { DiagramResource dr = DiagramResource.getInstance(graph); return ElementClassProviders.mappedProvider( ElementClasses.CONNECTION, ConnectionClass.CLASS.newClassWith(new StaticObjectAdapter(dr.RouteGraphConnection)), ElementClasses.FLAG, FlagClassFactory.createFlagClass(dr.Flag, dr.Flag_Terminal) ); } protected SimpleElementTransformHandler getTransformHandler() { return new SimpleElementTransformHandler(true, true, true); } public void initializeCanvasContext(CanvasContext ctx) { IHintContext h = ctx.getDefaultHintContext(); // The canvas context should not be painted until it is ready to avoid // unnecessary visual glitches. h.setHint(Hints.KEY_DISABLE_PAINTING, Boolean.TRUE); Object task = BEGIN("createSynchronizer"); //$NON-NLS-1$ this.synchronizer = createSynchronizer(ctx, sessionContext); END(task); IContextService contextService = (IContextService) getSite().getService(IContextService.class); contextUtil = new ContextUtil(contextService, swt); // Support & Util Participants ctx.add(new TransformUtil()); ctx.add(new MouseUtil()); ctx.add(new KeyUtil()); ctx.add(contextUtil); ctx.add(new WorkbenchStatusLine(statusLineManager)); ctx.add(new CanvasGrab()); ctx.add(new SymbolUtil()); ctx.add(new TimeParticipant()); ctx.add(new CanvasBoundsParticipant()); ctx.add(new Notifications()); ctx.add(new SGFocusParticipant(c, DIAGRAMMING_CONTEXT)); // Debug participant(s) // ctx.add( new PointerPainter() ); // ctx.add( new HandPainter() ); h.setHint(PointerPainter.KEY_PAINT_POINTER, true); // Pan & Zoom & Rotate addViewManipulationParticipants(ctx); ctx.add(getTransformHandler()); ctx.add(new ExpandSelectionHandler(getEditorSite().getActionBars().getStatusLineManager())); // Key bindings addKeyBindingParticipants(ctx); // Grid & Ruler & Background addGridRulerBackgroundParticipants(ctx); h.setHint(Hints.KEY_DISPLAY_PAGE, diagramPreferences.get(DiagramPreferences.P_DISPLAY_PAGE_SIZE)); h.setHint(Hints.KEY_DISPLAY_MARGINS, diagramPreferences.get(DiagramPreferences.P_DISPLAY_MARGINS)); ctx.add(new PageBorderParticipant()); // h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f)); // h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.LIGHT_GRAY); h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f)); 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 ////// addDiagramParticipants(ctx); addPainterParticipants(ctx); /////// D'n'D /////// addDropParticipants(ctx); h.setHint(ElementPainter.KEY_SELECTION_FRAME_COLOR, Color.MAGENTA); h.setHint(Hints.KEY_TOOL, Hints.POINTERTOOL); h.setHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT, 100000.0); h.setHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT, 10.0); Double snapResolution = diagramPreferences.get(DiagramPreferences.P_SNAP_GRID_SIZE); this.snapAdvisor = new GridSnapAdvisor(snapResolution); h.setHint(DiagramHints.SNAP_ADVISOR, this.snapAdvisor); h.setHint(GridPainter.KEY_GRID_SIZE, snapResolution); h.setHint(GridPainter.KEY_GRID_ENABLED, Boolean.FALSE); // Workbench selection provider addStructureParticipants(ctx); // Pop-up menu addPopupmenu(ctx); // Load page frame description settings loadPageSettings(ctx); addOtherParticipants(ctx); ctx.assertParticipantDependencies(); ctx.setLocked(false); } protected void addGridRulerBackgroundParticipants(CanvasContext ctx) { ctx.add(new GridPainter()); ctx.add(new RulerPainter()); ctx.add(new BackgroundPainter()); } protected void loadPageSettings(ICanvasContext ctx) { DiagramDesc diagramDesc = null; // load diagram page configuration if (diagramResource != null) { try { diagramDesc = sessionContext.getSession().syncRequest(DiagramRequests.getDiagramDesc(diagramResource)); } catch (DatabaseException e) { ErrorLogger.defaultLogError(e); } } if (diagramDesc == null) { // Take page description from the preferences if nothing else is available. final DiagramDesc desc = diagramDesc = diagramPreferences.getDiagramDesc(); // Write page configuration to graph sessionContext.getSession().asyncRequest(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { if (graph.isImmutable(diagramResource)) return; CommonDBUtils.selectClusterSet(graph, diagramResource); DiagramGraphUtil.setDiagramDesc(graph, diagramResource, desc); } }, parameter -> { if (parameter != null) ErrorLogger.defaultLogError("Failed to write default diagram page description to database, see exception for details", parameter); //$NON-NLS-1$ }); } setDiagramDesc(ctx, diagramDesc); // Create a listener to react to page setting changes. sessionContext.getSession().asyncRequest(DiagramRequests.getDiagramDesc(diagramResource), new ListenerDelegate(this) { @Override public void execute(final DiagramDesc result) { if (result != null && canvasContext != null) { ThreadUtils.asyncExec(canvasContext.getThreadAccess(), new Runnable() { @Override public void run() { if (!disposed) setDiagramDesc(canvasContext, result); } }); } } }); } protected void setDiagramDesc(ICanvasContext ctx, DiagramDesc diagramDesc) { if (diagramDesc == null) throw new NullPointerException("null diagram desc"); //$NON-NLS-1$ if (diagramDesc.equals(this.diagramDesc)) return; this.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()); hints.setHint(GridPainter.KEY_GRID_ENABLED, diagramDesc.isGridVisible()); hints.setHint(RulerPainter.KEY_RULER_ENABLED, diagramDesc.isRulerVisible()); snapAdvisor.setResolution(diagramDesc.getGridSize()); hints.setHint(GridPainter.KEY_GRID_SIZE, diagramDesc.getGridSize()); } /** * Must be invoked from the AWT thread only. * * @param state * @param ctx */ protected void applyEditorState(final EditorState state, final ICanvasContext ctx) { final IDiagram diagram = ctx.getHintStack().getHint(DiagramHints.KEY_DIAGRAM); if (state.viewTransform != null && state.viewTransform.getDeterminant() != 0) { for (PanZoomRotateHandler h : ctx.getItemsByClass(PanZoomRotateHandler.class)) { h.setTransform(state.viewTransform); } } if (diagram != null) { if (state.viewTransform != null) diagram.removeHint(DiagramHints.KEY_INITIAL_ZOOM_TO_FIT); if (state.toolMode != null) ctx.getDefaultHintContext().setHint(Hints.KEY_TOOL, state.toToolMode()); else ctx.getDefaultHintContext().setHint(Hints.KEY_TOOL, Hints.POINTERTOOL); final Set selected = DiagramEditorStates.toElements(state.selection, diagram); if (!selected.isEmpty()) { for (Selection s : ctx.getItemsByClass(Selection.class)) { s.setSelection(0, selected); } } } } protected EditorState getSavedEditorState(ICanvasContext ctx) { return DiagramEditorStates.toEditorState(ctx, true, true, true); } protected void saveEditorState(ICanvasContext ctx) { DiagramEditorStates.saveEditorState(PREFERENCE_VIRTUAL_GRAPH, diagramResource, getSavedEditorState(ctx) , DiagramViewer.this); } private boolean firstFocus = true; public void setFocus() { if(c != null) { c.setFocus(); if (firstFocus) { // This is a workaround for using the symbol viewer in multi-page // editors which causes the first zoom-to-fit scheduling to happen // already before the controls have been laid out properly. firstFocus = false; firstTimeSetFocus(); } } } protected void firstTimeSetFocus() { // [Tuukka@2010-02-11] // Removed since this is actually a workaround for multi-page editors. //scheduleZoomToFit(); } public void dispose() { // Deactivate all contexts here because after this the context service // in question will not be available. if (contextUtil != null) { contextUtil.deactivateAll(); } disposed = true; if (hasDiagramSourceListener != null) { hasDiagramSourceListener.dispose(); hasDiagramSourceListener = null; } if (activation != null) { activation.deactivate(); activation = null; } if (resourceManager != null) { resourceManager.dispose(); resourceManager = null; } //super.dispose(); } protected Resource getInputResource() { return getResourceInput().getResource(); } public IResourceEditorInput getResourceInput() { return (IResourceEditorInput) getEditorInput(); } public IResourceEditorInput2 getResourceInput2() { return (IResourceEditorInput2) getEditorInput(); } public void init(DiagramViewerHost _host, IEditorSite site, IEditorInput input, DataContainer diagramContainer, WorkbenchSelectionProvider selectionProvider) { if (!(input instanceof IResourceEditorInput)) throw new RuntimeException("Invalid input: must be IResourceEditorInput"); //$NON-NLS-1$ setHost(_host); setSite(site); setInput(input); this.sourceDiagramContainer = diagramContainer; this.selectionProvider = selectionProvider; // Set initial part name according to the name given by IEditorInput host.doSetPartName(getEditorInput().getName()); Session session = Simantics.peekSession(); if (session != null) { Supplier disposedCallback = () -> disposed; session.asyncRequest( new TitleRequest(site.getId(), getResourceInput()), new TitleUpdater(site.getShell().getDisplay(), host::doSetPartName, disposedCallback)); session.asyncRequest( new ToolTipRequest(site.getId(), getResourceInput()), new TitleUpdater(site.getShell().getDisplay(), host::doSetTitleToolTip, disposedCallback)); } try { // Read previous editor state from the database editorState = DiagramEditorStates.readEditorState(getInputResource()); } catch (DatabaseException e) { exception(e); } } @SuppressWarnings("unchecked") @Override public T getAdapter(Class adapter) { // System.out.println("diagramViewer getAdapter " + adapter); // Property view support if (adapter == IPropertyPage.class) return (T) createPropertyPage(getSite(), getPropertyPageContexts()); // Provide symbols for the editor if (adapter == SymbolProviderFactory.class) { try { return (T) DiagramTypeUtils.readSymbolProviderFactory(sessionContext.getSession(), diagramResource); } catch (DatabaseException e) { ErrorLogger.defaultLogError(getClass() + " failed to adapt to SymbolProviderFactory, see exception for details.", e); //$NON-NLS-1$ return null; } } // Outline view support if (adapter == IContentOutlinePage.class) return (T) new DiagramOutlinePage(sessionContextProvider, getResourceInput2()); // Role view support if (adapter == ILayersViewPage.class) return (T) new DiagramLayersPage(sourceDiagram, canvasContext); // Support external steering of the diagram canvas, zooming etc. if (adapter == ICanvasContext.class) return (T) canvasContext; // Support scene graph debugger view if (adapter == INode.class) { if (canvasContext != null) { INode node = canvasContext.getCanvasNode(); if (node != null) return (T) NodeUtil.getRootNode(node); } return null; } // Support retrieval of the current diagram. if (adapter == IDiagram.class) return (T) sourceDiagram; // Why is this here ?? if (adapter == Session.class) return (T) sessionContext.getSession(); if(adapter == RuntimeDiagramManager.class) return (T) runtimeDiagramManager; if (adapter == Resource.class) return (T) getRuntime(); if (adapter == ICanvasChassis.class) return (T) c; return null; } //------------------------------------------------------------------------- // Profiling aid protected static Object BEGIN(String name) { if (PROFILE) { //return ThreadLog.BEGIN(name); } return null; } protected static void END(Object task) { if (PROFILE) { //((Task) task).end(); } } //------------------------------------------------------------------------- // implement ListenerSupport @Override public void exception(Throwable t) { ErrorLogger.defaultLogError(t); } @Override public boolean isDisposed() { return disposed; } protected void collectGarbage() { SessionGarbageCollectorJob.getInstance().schedule(0); AWTThread.getThreadAccess().asyncExec(new Runnable() { @Override public void run() { //System.out.println("BEFORE CLEAR: " + SVGCache.getSVGUniverse().report()); SVGCache.getSVGUniverse().clearUnreferenced(); //System.out.println("AFTER CLEAR: " + SVGCache.getSVGUniverse().report()); } }); } //------------------------------------------------------------------------- // Listener for certain canvas context hint changes IHintListener canvasHintListener = new HintListenerAdapter() { @Override public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { if (key == GridPainter.KEY_GRID_ENABLED) { boolean v = Boolean.TRUE.equals(newValue); if (diagramDesc.isGridVisible() != v) setGlobalPreference(DiagramResource.URIs.DisplayGrid, v); } else if (key == RulerPainter.KEY_RULER_ENABLED) { boolean v = Boolean.TRUE.equals(newValue); if (diagramDesc.isRulerVisible() != v) setGlobalPreference(DiagramResource.URIs.DisplayRuler, v); } } }; private void setGlobalPreference(final String preferenceURI, boolean value) { TagUtil.execute(Simantics.getSession(), PREFERENCE_VIRTUAL_GRAPH, preferenceURI, value, Simantics.getProjectResource()); } /* * -------------------------------------------------------------------- * Changes related to removal of EditorPart extension from here on down * -------------------------------------------------------------------- */ private IWorkbenchPartSite partSite; private DiagramViewerHost host; private IEditorInput editorInput = null; /* (non-Javadoc) * Method declared on IWorkbenchPart. */ public IWorkbenchPartSite getSite() { return partSite; } public IEditorSite getEditorSite() { return (IEditorSite) getSite(); } public IEditorInput getEditorInput() { return editorInput; } // protected void setPartName(String partName) { //// if (compatibilityTitleListener != null) { //// removePropertyListener(compatibilityTitleListener); //// compatibilityTitleListener = null; //// } //// //// super.setPartName(partName); // } // protected void setTitleToolTip(String toolTip) { //// toolTip = Util.safeString(toolTip); //// //Do not send changes if they are the same //// if (Util.equals(this.toolTip, toolTip)) { //// return; //// } //// this.toolTip = toolTip; //// firePropertyChange(IWorkbenchPart.PROP_TITLE); // } protected void setHost(DiagramViewerHost host) { this.host = host; } protected void setSite(IWorkbenchPartSite site) { this.partSite = site; } protected void setInput(IEditorInput input) { Assert.isLegal(input != null); editorInput = input; } public Composite getComposite() { return c; } }