X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2FdiagramEditor%2FDiagramViewer.java;h=88d85cfbf6581dc996205ffdf0204d4e8e444e51;hp=8ac45cf021d31bb181dfe772207f418d05c62a60;hb=7c341eb5f602ee3c7255e933204f4f72a6d455fa;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramViewer.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramViewer.java index 8ac45cf02..88d85cfbf 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramViewer.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramViewer.java @@ -1,1154 +1,1199 @@ -/******************************************************************************* - * Copyright (c) 2007, 2013 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 - *******************************************************************************/ -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.procedure.adapter.ListenerDelegate; -import org.simantics.db.common.procedure.adapter.ListenerSupport; -import org.simantics.db.common.request.ParametrizedRead; -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.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.Callback; -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"; - private static final String PREFERENCE_VIRTUAL_GRAPH = "preferences"; - - 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; - - /** - * Set externally in - * {@link #init(DiagramViewerHost, IEditorSite, IEditorInput, DataContainer, WorkbenchSelectionProvider)} - * . - */ - protected WorkbenchSelectionProvider selectionProvider; - - public Resource getRuntime() { - RuntimeDiagramManager rtdm = runtimeDiagramManager; - return (rtdm == null) ? null : rtdm.getRuntimeDiagram(); - } - - 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(SimanticsUI.getSession(), defaultPropertyBrowseContexts); - } catch (DatabaseException e) { - ExceptionUtils.logAndShowError("Failed to load modeled browse contexts for property page, see exception for details.", e); - return defaultPropertyBrowseContexts; - } - } - - protected IPropertyPage createPropertyPage(IWorkbenchPartSite site, Set contexts) { - return new StandardPropertyPage(site, contexts); - } - - protected String getPopupId() { - return "#ModelingDiagramPopup"; - } - - protected void getPreferences() { - this.diagramPreferences = DiagramPreferenceUtil.getPreferences(); - } - - protected void initSession() { - sessionContextProvider = SimanticsUI.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"); - 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"); - 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"); - 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"); - - Object task = BEGIN("DV.createViewerCanvas"); - 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"); - 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, "Activate Mapping", 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"); - - 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( - DiagramRequests.loadDiagram(monitor, getResourceInput2().getModel(null), diagram, - runtimeDiagram, null, loader, initialHints)); - 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); - } - } - - 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"); - 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 - ctx.add(new GridPainter()); - ctx.add(new RulerPainter()); - ctx.add(new BackgroundPainter()); - - 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 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 { - CommonDBUtils.selectClusterSet(graph, diagramResource); - DiagramGraphUtil.setDiagramDesc(graph, diagramResource, desc); - } - }, new Callback() { - @Override - public void run(DatabaseException parameter) { - if (parameter != null) - ErrorLogger.defaultLogError("Failed to write default diagram page description to database, see exception for details", parameter); - } - }); - } - - 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"); - - 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 (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"); - - 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 = SimanticsUI.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); - 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; - } - -} +/******************************************************************************* + * 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.ConnectionCrossingsParticipant; +import org.simantics.diagram.participant.ContextUtil; +import org.simantics.diagram.participant.CopyAsSVGParticipant; +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()); + ctx.add(new ConnectionCrossingsParticipant(getInputResource())); + ctx.add(new CopyAsSVGParticipant()); + } + + 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; + } + +}