X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.modeling%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Factions%2FNavigateToTarget.java;fp=bundles%2Forg.simantics.modeling%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Factions%2FNavigateToTarget.java;h=7805a46887ed88c0777268be2a570800d147ee00;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hp=0599267ace8936f339e7d4c839826cb70c4e1d44;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/actions/NavigateToTarget.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/actions/NavigateToTarget.java index 0599267ac..7805a4688 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/actions/NavigateToTarget.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/actions/NavigateToTarget.java @@ -1,440 +1,440 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 Association for Decentralized Information Management - * in Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.modeling.actions; - -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.jface.action.IStatusLineManager; -import org.eclipse.jface.window.Window; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IWorkbenchPart; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.PartInitException; -import org.simantics.Simantics; -import org.simantics.databoard.Bindings; -import org.simantics.databoard.util.URIStringUtils; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.Session; -import org.simantics.db.common.NamedResource; -import org.simantics.db.common.procedure.adapter.ProcedureAdapter; -import org.simantics.db.common.request.ReadRequest; -import org.simantics.db.common.utils.NameUtils; -import org.simantics.db.common.utils.OrderedSetUtils; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.layer0.variable.RVI; -import org.simantics.db.layer0.variable.Variable; -import org.simantics.db.layer0.variable.Variables; -import org.simantics.diagram.content.ConnectionUtil; -import org.simantics.diagram.flag.FlagUtil; -import org.simantics.diagram.stubs.DiagramResource; -import org.simantics.g2d.canvas.ICanvasContext; -import org.simantics.g2d.diagram.DiagramHints; -import org.simantics.g2d.diagram.IDiagram; -import org.simantics.g2d.diagram.handler.DataElementMap; -import org.simantics.g2d.diagram.participant.Selection; -import org.simantics.g2d.element.ElementUtils; -import org.simantics.g2d.element.IElement; -import org.simantics.g2d.elementclass.FlagClass; -import org.simantics.g2d.participant.CanvasBoundsParticipant; -import org.simantics.g2d.participant.TransformUtil; -import org.simantics.g2d.utils.GeometryUtils; -import org.simantics.layer0.Layer0; -import org.simantics.layer0.utils.operations.Operation; -import org.simantics.modeling.ModelingOperationConstants; -import org.simantics.modeling.ModelingResources; -import org.simantics.modeling.utils.Monitors; -import org.simantics.structural.stubs.StructuralResource2; -import org.simantics.ui.workbench.ResourceEditorInput; -import org.simantics.ui.workbench.ResourceEditorInput2; -import org.simantics.utils.datastructures.persistent.IContextMap; -import org.simantics.utils.page.MarginUtils; -import org.simantics.utils.strings.AlphanumComparator; -import org.simantics.utils.threads.ThreadUtils; -import org.simantics.utils.ui.ErrorLogger; -import org.simantics.utils.ui.workbench.WorkbenchUtils; - -public class NavigateToTarget extends Operation { - - public NavigateToTarget() { - super("Navigate to Target"); - } - - @Override - public void exec(Session session, IContextMap parameters) { - final Resource r = (Resource) parameters.get(SUBJECT); - final IWorkbenchWindow window = (IWorkbenchWindow) parameters.get(ModelingOperationConstants.WORKBENCH_WINDOW); - final IWorkbenchPart part = (IWorkbenchPart) parameters.get(ModelingOperationConstants.WORKBENCH_PART); - - session.asyncRequest(new ReadRequest() { - @Override - public void run(ReadGraph g) throws DatabaseException { - final Resource thisDiagram = getOwnerList(g, r); - if (thisDiagram == null) - return; - - Set counterparts = FlagUtil.getCounterparts(g, r); - if (counterparts.size() > 1) { - // Ask user which target to navigate to. - asyncQueryTarget(window.getShell(), g, thisDiagram, counterparts, - target -> navigateToTarget( window, part, thisDiagram, target)); - } else if (counterparts.size() == 1) { - // Target is defined. - final String error = navigateToTarget( g, window, part, thisDiagram, counterparts.iterator().next() ); - if (error != null && !error.isEmpty()) { - window.getShell().getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - IStatusLineManager status = WorkbenchUtils.getStatusLine(part); - status.setErrorMessage(error); - } - }); - } - } else { - // Try other methods of getting a navigation target besides flags. - Resource target = Monitors.getMonitoredElement(g, r); - if (target != null) - navigateToTarget( g, window, part, thisDiagram, target ); - } - } - }, new ProcedureAdapter() { - @Override - public void exception(Throwable t) { - ErrorLogger.defaultLogError(t); - } - }); - } - - protected void navigateToTarget(final IWorkbenchWindow window, final IWorkbenchPart sourceEditor, - final Resource sourceDiagram, final Resource target) { - Simantics.getSession().asyncRequest(new ReadRequest() { - @Override - public void run(ReadGraph graph) throws DatabaseException { - navigateToTarget(graph, window, sourceEditor, sourceDiagram, target); - } - }, new ProcedureAdapter() { - public void exception(Throwable t) { - ErrorLogger.defaultLogError(t); - } - }); - } - - /** - * @param graph - * @param window - * @param sourceEditor - * @param sourceDiagram - * @param target - * @return null if everything's OK - * @throws DatabaseException - */ - protected String navigateToTarget(ReadGraph graph, IWorkbenchWindow window, IWorkbenchPart sourceEditor, - Resource sourceDiagram, final Resource target) throws DatabaseException { - ModelingResources MOD = ModelingResources.getInstance(graph); - - final Resource otherDiagram = getOwnerList(graph, target); - if (otherDiagram == null) - return ""; - - if (!sourceDiagram.equals(otherDiagram)) { - - // Find the structural path - Resource otherComposite = graph.getPossibleObject(otherDiagram, MOD.DiagramToComposite); - if (otherComposite == null) - return ""; - - Variable compositeVariable = Variables.getVariable(graph, otherComposite); - final Resource model = Variables.getPossibleModel(graph, compositeVariable); - final RVI rvi = model == null ? null : compositeVariable.getPossibleRVI(graph); - if (model == null || rvi == null) - return "Navigating via flags only possible under model configuration"; - - window.getShell().getDisplay().asyncExec( - editorActivator(sourceEditor, otherDiagram, model, rvi, part -> { - final ICanvasContext openedCanvas = (ICanvasContext) part.getAdapter(ICanvasContext.class); - assert openedCanvas != null; - // CanvasContext-wide denial of initial zoom-to-fit on diagram open. - openedCanvas.getDefaultHintContext().setHint(DiagramHints.KEY_INITIAL_ZOOM_TO_FIT, Boolean.FALSE); - ThreadUtils.asyncExec( openedCanvas.getThreadAccess(), - elementSelectorZoomer( openedCanvas, Collections.singleton( target ), false ) ); - })); - } else { - ICanvasContext canvas = (ICanvasContext) sourceEditor.getAdapter( ICanvasContext.class ); - assert canvas != null; - ThreadUtils.asyncExec( canvas.getThreadAccess(), - elementSelectorZoomer( canvas, Collections.singleton( target ), true )); - } - return null; - } - - protected void asyncQueryTarget(final Shell parentShell, ReadGraph graph, Resource sourceDiagram, - Set counterparts, final Consumer navigationCallback) throws DatabaseException { - ModelingResources MOD = ModelingResources.getInstance(graph); - StructuralResource2 STR = StructuralResource2.getInstance(graph); - ConnectionUtil cu = new ConnectionUtil(graph); - - final List outputs = new ArrayList(counterparts.size()); - final List inputs = new ArrayList(counterparts.size()); - for (Resource counterpart : counterparts) { - final Resource diagram = getOwnerList(graph, counterpart); - if (diagram == null) - continue; - - Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite); - if (composite == null) - continue; - Variable v = Variables.getPossibleVariable(graph, composite); - if (v == null) - continue; - - String rvi = Variables.getRVI(graph, v); - rvi = URIStringUtils.unescape(rvi); - - Resource connectedComponent = null; - - for (Resource connector : graph.getObjects(counterpart, STR.IsConnectedTo)) { - Resource diagramConnection = ConnectionUtil.tryGetConnection(graph, connector); - if (diagramConnection != null) { - Collection connectors = cu.getConnectors(diagramConnection, new HashSet()); - connectors.remove(connector); - if (connectors.isEmpty()) { - continue; - } else { - connectedComponent = graph.getPossibleObject(diagramConnection, MOD.ElementToComponent); - if (connectedComponent == null) { - for (Resource conn : connectors) { - Resource element = cu.getConnectedComponent(diagramConnection, conn); - if (element == null) - continue; - connectedComponent = graph.getPossibleObject(element, MOD.ElementToComponent); - if (connectedComponent != null) - break; - } - } - } - } - } - if (connectedComponent != null) { - rvi += Variables.Role.CHILD.getIdentifier(); - rvi += NameUtils.getSafeName(graph, connectedComponent); - } - - boolean localFlag = sourceDiagram.equals(diagram); - if (localFlag) { - Layer0 L0 = Layer0.getInstance(graph); - rvi += " (local"; - String label = graph.getPossibleRelatedValue2(counterpart, L0.HasLabel, Bindings.STRING); - if (label != null && !label.isEmpty()) - rvi += " " + label; - rvi += ")"; - } - - FlagClass.Type type = FlagUtil.getFlagType(graph, counterpart, FlagClass.Type.In); - switch (type) { - case In: - inputs.add( new NamedResource(rvi, counterpart) ); - break; - case Out: - outputs.add( new NamedResource(rvi, counterpart) ); - break; - } - } - - // To make result ordering stable and logical - Collections.sort(outputs, COMPARATOR); - Collections.sort(inputs, COMPARATOR); - - outputs.addAll(inputs); - if (outputs.isEmpty()) - return; - - parentShell.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - if (parentShell.isDisposed()) - return; - NavigationTargetChooserDialog dialog = new NavigationTargetChooserDialog( - parentShell, outputs.toArray(new NamedResource[0]), - "Choose Navigation Target", - "Select single navigation target from list"); - if (dialog.open() != Window.OK) - return; - if (dialog.getSelection() != null) - navigationCallback.accept( dialog.getSelection().getResource() ); - } - }); - } - - private static final Comparator COMPARATOR = new Comparator() { - @Override - public int compare(NamedResource o1, NamedResource o2) { - return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.getName(), o2.getName()); - } - }; - - public static Runnable editorActivator(final IWorkbenchPart part, final Resource diagram, final Resource model, final RVI rvi, final Consumer successCallback) { - String sourcePartId = part.getSite().getId(); - return editorActivator(sourcePartId, diagram, model, rvi, successCallback); - } - - public static Runnable editorActivator(final String editorPartId, final Resource diagram, final Resource model, final RVI rvi, final Consumer successCallback) { - return () -> { - try { - // open and activate new editor - IEditorPart newPart = WorkbenchUtils.openEditor(editorPartId, createEditorInput(editorPartId, diagram, model, rvi)); - newPart.getSite().getPage().activate(newPart); - successCallback.accept(newPart); - } catch (PartInitException e) { - ErrorLogger.defaultLogError(e); - } - }; - } - - private static IEditorInput createEditorInput(String editorPartId, Resource diagram, Resource model, RVI rvi) { - if (model != null) - return new ResourceEditorInput2(editorPartId, diagram, model, rvi); - else - return new ResourceEditorInput(editorPartId, diagram); - } - - /** - * @param editorPartId - * @param diagram - * @param model - * @param rvi - * @param successCallback - * @return - * @deprecated use {@link #editorActivator(String, Resource, Resource, RVI, Callback)} instead - */ - @Deprecated - public static Runnable editorActivator(final String editorPartId, final Resource diagram, final Resource model, final String rvi, final Consumer successCallback) { - return () -> { - try { - // open and activate new editor - IEditorPart newPart = WorkbenchUtils.openEditor(editorPartId, new ResourceEditorInput2(editorPartId, diagram, model, rvi)); - newPart.getSite().getPage().activate(newPart); - successCallback.accept(newPart); - } catch (PartInitException e) { - ErrorLogger.defaultLogError(e); - } - }; - } - - public static Runnable elementSelectorZoomer(final ICanvasContext canvas, final Collection elementObjects, final boolean keepZoom) { - return new Runnable() { - int tries = 0; - @Override - public void run() { - //System.out.println(tries + ": elementSelectorZoomer: " + canvas.isDisposed() + ", " + elementObjects); - if (canvas.isDisposed()) - return; - - // This will prevent eternal looping in unexpected situations. - if (++tries > 10) { - ErrorLogger.defaultLog(new Status(IStatus.INFO, "", - "NavigateToTarget.elementSelectorZoomer failed to find any of the requested elements " - + elementObjects + ". Giving up.")); - return; - } - - IDiagram diagram = canvas.getHintStack().getHint(DiagramHints.KEY_DIAGRAM); - if (diagram == null || !zoomToSelection(canvas, diagram, selectElement(canvas, diagram, elementObjects), keepZoom)) { - // Reschedule for later in hopes that initialization is complete. - ThreadUtils.getNonBlockingWorkExecutor().schedule(this, 200, TimeUnit.MILLISECONDS); - } - } - }; - } - - public static Set selectElement(final ICanvasContext canvas, final IDiagram diagram, final Collection elementObjects) { - // Select element - Set selection = new HashSet(elementObjects.size()); - DataElementMap dataMap = diagram.getDiagramClass().getSingleItem(DataElementMap.class); - for (Object obj : elementObjects) { - IElement element = dataMap.getElement(diagram, obj); - if (element != null) { - selection.add(element); - } - } - if (!selection.isEmpty()) { - for (Selection s : canvas.getItemsByClass(Selection.class)) { - s.setSelection(0, selection); - } - } - return selection; - } - - public static boolean zoomToSelection(final ICanvasContext canvas, final IDiagram diagram, Set selection, final boolean keepZoom) { - final TransformUtil util = canvas.getSingleItem(TransformUtil.class); - CanvasBoundsParticipant boundsParticipant = canvas.getAtMostOneItemOfClass(CanvasBoundsParticipant.class); - if (boundsParticipant == null) - return false; - - final Rectangle2D controlBounds = boundsParticipant.getControlBounds().getFrame(); - if (controlBounds == null || controlBounds.isEmpty()) - return false; - - final Shape shp = ElementUtils.getElementBoundsOnDiagram(selection); - if (shp == null) - return false; - - ThreadUtils.asyncExec(canvas.getThreadAccess(), new Runnable() { - @Override - public void run() { - if (canvas.isDisposed()) - return; - - Rectangle2D diagramRect = shp.getBounds2D(); - - // Make sure that even empty bounds can be zoomed into. - org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect, 1); - - if (keepZoom) { - double scaleFactor = GeometryUtils.getScale(util.getTransform()); - double cwh = controlBounds.getWidth() / (scaleFactor*2); - double chh = controlBounds.getHeight() / (scaleFactor*2); - - AffineTransform view = new AffineTransform(); - view.scale(scaleFactor, scaleFactor); - view.translate(-diagramRect.getCenterX()+cwh, -diagramRect.getCenterY()+chh); - - util.setTransform(view); - } else { - MarginUtils.Margin margin = MarginUtils.marginOf(40, 0, 0); - MarginUtils.Margins margins = new MarginUtils.Margins(margin, margin, margin, margin); - util.fitArea(controlBounds, diagramRect, margins); - } - } - }); - return true; - } - - public static Resource getOwnerList(ReadGraph g, Resource listElement) throws DatabaseException { - return OrderedSetUtils.getSingleOwnerList(g, listElement, DiagramResource.getInstance(g).Composite); - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.modeling.actions; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.databoard.util.URIStringUtils; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.common.NamedResource; +import org.simantics.db.common.procedure.adapter.ProcedureAdapter; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.common.utils.OrderedSetUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.RVI; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.Variables; +import org.simantics.diagram.content.ConnectionUtil; +import org.simantics.diagram.flag.FlagUtil; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.DiagramHints; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.DataElementMap; +import org.simantics.g2d.diagram.participant.Selection; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.elementclass.FlagClass; +import org.simantics.g2d.participant.CanvasBoundsParticipant; +import org.simantics.g2d.participant.TransformUtil; +import org.simantics.g2d.utils.GeometryUtils; +import org.simantics.layer0.Layer0; +import org.simantics.layer0.utils.operations.Operation; +import org.simantics.modeling.ModelingOperationConstants; +import org.simantics.modeling.ModelingResources; +import org.simantics.modeling.utils.Monitors; +import org.simantics.structural.stubs.StructuralResource2; +import org.simantics.ui.workbench.ResourceEditorInput; +import org.simantics.ui.workbench.ResourceEditorInput2; +import org.simantics.utils.datastructures.persistent.IContextMap; +import org.simantics.utils.page.MarginUtils; +import org.simantics.utils.strings.AlphanumComparator; +import org.simantics.utils.threads.ThreadUtils; +import org.simantics.utils.ui.ErrorLogger; +import org.simantics.utils.ui.workbench.WorkbenchUtils; + +public class NavigateToTarget extends Operation { + + public NavigateToTarget() { + super("Navigate to Target"); + } + + @Override + public void exec(Session session, IContextMap parameters) { + final Resource r = (Resource) parameters.get(SUBJECT); + final IWorkbenchWindow window = (IWorkbenchWindow) parameters.get(ModelingOperationConstants.WORKBENCH_WINDOW); + final IWorkbenchPart part = (IWorkbenchPart) parameters.get(ModelingOperationConstants.WORKBENCH_PART); + + session.asyncRequest(new ReadRequest() { + @Override + public void run(ReadGraph g) throws DatabaseException { + final Resource thisDiagram = getOwnerList(g, r); + if (thisDiagram == null) + return; + + Set counterparts = FlagUtil.getCounterparts(g, r); + if (counterparts.size() > 1) { + // Ask user which target to navigate to. + asyncQueryTarget(window.getShell(), g, thisDiagram, counterparts, + target -> navigateToTarget( window, part, thisDiagram, target)); + } else if (counterparts.size() == 1) { + // Target is defined. + final String error = navigateToTarget( g, window, part, thisDiagram, counterparts.iterator().next() ); + if (error != null && !error.isEmpty()) { + window.getShell().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + IStatusLineManager status = WorkbenchUtils.getStatusLine(part); + status.setErrorMessage(error); + } + }); + } + } else { + // Try other methods of getting a navigation target besides flags. + Resource target = Monitors.getMonitoredElement(g, r); + if (target != null) + navigateToTarget( g, window, part, thisDiagram, target ); + } + } + }, new ProcedureAdapter() { + @Override + public void exception(Throwable t) { + ErrorLogger.defaultLogError(t); + } + }); + } + + protected void navigateToTarget(final IWorkbenchWindow window, final IWorkbenchPart sourceEditor, + final Resource sourceDiagram, final Resource target) { + Simantics.getSession().asyncRequest(new ReadRequest() { + @Override + public void run(ReadGraph graph) throws DatabaseException { + navigateToTarget(graph, window, sourceEditor, sourceDiagram, target); + } + }, new ProcedureAdapter() { + public void exception(Throwable t) { + ErrorLogger.defaultLogError(t); + } + }); + } + + /** + * @param graph + * @param window + * @param sourceEditor + * @param sourceDiagram + * @param target + * @return null if everything's OK + * @throws DatabaseException + */ + protected String navigateToTarget(ReadGraph graph, IWorkbenchWindow window, IWorkbenchPart sourceEditor, + Resource sourceDiagram, final Resource target) throws DatabaseException { + ModelingResources MOD = ModelingResources.getInstance(graph); + + final Resource otherDiagram = getOwnerList(graph, target); + if (otherDiagram == null) + return ""; + + if (!sourceDiagram.equals(otherDiagram)) { + + // Find the structural path + Resource otherComposite = graph.getPossibleObject(otherDiagram, MOD.DiagramToComposite); + if (otherComposite == null) + return ""; + + Variable compositeVariable = Variables.getVariable(graph, otherComposite); + final Resource model = Variables.getPossibleModel(graph, compositeVariable); + final RVI rvi = model == null ? null : compositeVariable.getPossibleRVI(graph); + if (model == null || rvi == null) + return "Navigating via flags only possible under model configuration"; + + window.getShell().getDisplay().asyncExec( + editorActivator(sourceEditor, otherDiagram, model, rvi, part -> { + final ICanvasContext openedCanvas = (ICanvasContext) part.getAdapter(ICanvasContext.class); + assert openedCanvas != null; + // CanvasContext-wide denial of initial zoom-to-fit on diagram open. + openedCanvas.getDefaultHintContext().setHint(DiagramHints.KEY_INITIAL_ZOOM_TO_FIT, Boolean.FALSE); + ThreadUtils.asyncExec( openedCanvas.getThreadAccess(), + elementSelectorZoomer( openedCanvas, Collections.singleton( target ), false ) ); + })); + } else { + ICanvasContext canvas = (ICanvasContext) sourceEditor.getAdapter( ICanvasContext.class ); + assert canvas != null; + ThreadUtils.asyncExec( canvas.getThreadAccess(), + elementSelectorZoomer( canvas, Collections.singleton( target ), true )); + } + return null; + } + + protected void asyncQueryTarget(final Shell parentShell, ReadGraph graph, Resource sourceDiagram, + Set counterparts, final Consumer navigationCallback) throws DatabaseException { + ModelingResources MOD = ModelingResources.getInstance(graph); + StructuralResource2 STR = StructuralResource2.getInstance(graph); + ConnectionUtil cu = new ConnectionUtil(graph); + + final List outputs = new ArrayList(counterparts.size()); + final List inputs = new ArrayList(counterparts.size()); + for (Resource counterpart : counterparts) { + final Resource diagram = getOwnerList(graph, counterpart); + if (diagram == null) + continue; + + Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite); + if (composite == null) + continue; + Variable v = Variables.getPossibleVariable(graph, composite); + if (v == null) + continue; + + String rvi = Variables.getRVI(graph, v); + rvi = URIStringUtils.unescape(rvi); + + Resource connectedComponent = null; + + for (Resource connector : graph.getObjects(counterpart, STR.IsConnectedTo)) { + Resource diagramConnection = ConnectionUtil.tryGetConnection(graph, connector); + if (diagramConnection != null) { + Collection connectors = cu.getConnectors(diagramConnection, new HashSet()); + connectors.remove(connector); + if (connectors.isEmpty()) { + continue; + } else { + connectedComponent = graph.getPossibleObject(diagramConnection, MOD.ElementToComponent); + if (connectedComponent == null) { + for (Resource conn : connectors) { + Resource element = cu.getConnectedComponent(diagramConnection, conn); + if (element == null) + continue; + connectedComponent = graph.getPossibleObject(element, MOD.ElementToComponent); + if (connectedComponent != null) + break; + } + } + } + } + } + if (connectedComponent != null) { + rvi += Variables.Role.CHILD.getIdentifier(); + rvi += NameUtils.getSafeName(graph, connectedComponent); + } + + boolean localFlag = sourceDiagram.equals(diagram); + if (localFlag) { + Layer0 L0 = Layer0.getInstance(graph); + rvi += " (local"; + String label = graph.getPossibleRelatedValue2(counterpart, L0.HasLabel, Bindings.STRING); + if (label != null && !label.isEmpty()) + rvi += " " + label; + rvi += ")"; + } + + FlagClass.Type type = FlagUtil.getFlagType(graph, counterpart, FlagClass.Type.In); + switch (type) { + case In: + inputs.add( new NamedResource(rvi, counterpart) ); + break; + case Out: + outputs.add( new NamedResource(rvi, counterpart) ); + break; + } + } + + // To make result ordering stable and logical + Collections.sort(outputs, COMPARATOR); + Collections.sort(inputs, COMPARATOR); + + outputs.addAll(inputs); + if (outputs.isEmpty()) + return; + + parentShell.getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + if (parentShell.isDisposed()) + return; + NavigationTargetChooserDialog dialog = new NavigationTargetChooserDialog( + parentShell, outputs.toArray(new NamedResource[0]), + "Choose Navigation Target", + "Select single navigation target from list"); + if (dialog.open() != Window.OK) + return; + if (dialog.getSelection() != null) + navigationCallback.accept( dialog.getSelection().getResource() ); + } + }); + } + + private static final Comparator COMPARATOR = new Comparator() { + @Override + public int compare(NamedResource o1, NamedResource o2) { + return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.getName(), o2.getName()); + } + }; + + public static Runnable editorActivator(final IWorkbenchPart part, final Resource diagram, final Resource model, final RVI rvi, final Consumer successCallback) { + String sourcePartId = part.getSite().getId(); + return editorActivator(sourcePartId, diagram, model, rvi, successCallback); + } + + public static Runnable editorActivator(final String editorPartId, final Resource diagram, final Resource model, final RVI rvi, final Consumer successCallback) { + return () -> { + try { + // open and activate new editor + IEditorPart newPart = WorkbenchUtils.openEditor(editorPartId, createEditorInput(editorPartId, diagram, model, rvi)); + newPart.getSite().getPage().activate(newPart); + successCallback.accept(newPart); + } catch (PartInitException e) { + ErrorLogger.defaultLogError(e); + } + }; + } + + private static IEditorInput createEditorInput(String editorPartId, Resource diagram, Resource model, RVI rvi) { + if (model != null) + return new ResourceEditorInput2(editorPartId, diagram, model, rvi); + else + return new ResourceEditorInput(editorPartId, diagram); + } + + /** + * @param editorPartId + * @param diagram + * @param model + * @param rvi + * @param successCallback + * @return + * @deprecated use {@link #editorActivator(String, Resource, Resource, RVI, Callback)} instead + */ + @Deprecated + public static Runnable editorActivator(final String editorPartId, final Resource diagram, final Resource model, final String rvi, final Consumer successCallback) { + return () -> { + try { + // open and activate new editor + IEditorPart newPart = WorkbenchUtils.openEditor(editorPartId, new ResourceEditorInput2(editorPartId, diagram, model, rvi)); + newPart.getSite().getPage().activate(newPart); + successCallback.accept(newPart); + } catch (PartInitException e) { + ErrorLogger.defaultLogError(e); + } + }; + } + + public static Runnable elementSelectorZoomer(final ICanvasContext canvas, final Collection elementObjects, final boolean keepZoom) { + return new Runnable() { + int tries = 0; + @Override + public void run() { + //System.out.println(tries + ": elementSelectorZoomer: " + canvas.isDisposed() + ", " + elementObjects); + if (canvas.isDisposed()) + return; + + // This will prevent eternal looping in unexpected situations. + if (++tries > 10) { + ErrorLogger.defaultLog(new Status(IStatus.INFO, "", + "NavigateToTarget.elementSelectorZoomer failed to find any of the requested elements " + + elementObjects + ". Giving up.")); + return; + } + + IDiagram diagram = canvas.getHintStack().getHint(DiagramHints.KEY_DIAGRAM); + if (diagram == null || !zoomToSelection(canvas, diagram, selectElement(canvas, diagram, elementObjects), keepZoom)) { + // Reschedule for later in hopes that initialization is complete. + ThreadUtils.getNonBlockingWorkExecutor().schedule(this, 200, TimeUnit.MILLISECONDS); + } + } + }; + } + + public static Set selectElement(final ICanvasContext canvas, final IDiagram diagram, final Collection elementObjects) { + // Select element + Set selection = new HashSet(elementObjects.size()); + DataElementMap dataMap = diagram.getDiagramClass().getSingleItem(DataElementMap.class); + for (Object obj : elementObjects) { + IElement element = dataMap.getElement(diagram, obj); + if (element != null) { + selection.add(element); + } + } + if (!selection.isEmpty()) { + for (Selection s : canvas.getItemsByClass(Selection.class)) { + s.setSelection(0, selection); + } + } + return selection; + } + + public static boolean zoomToSelection(final ICanvasContext canvas, final IDiagram diagram, Set selection, final boolean keepZoom) { + final TransformUtil util = canvas.getSingleItem(TransformUtil.class); + CanvasBoundsParticipant boundsParticipant = canvas.getAtMostOneItemOfClass(CanvasBoundsParticipant.class); + if (boundsParticipant == null) + return false; + + final Rectangle2D controlBounds = boundsParticipant.getControlBounds().getFrame(); + if (controlBounds == null || controlBounds.isEmpty()) + return false; + + final Shape shp = ElementUtils.getElementBoundsOnDiagram(selection); + if (shp == null) + return false; + + ThreadUtils.asyncExec(canvas.getThreadAccess(), new Runnable() { + @Override + public void run() { + if (canvas.isDisposed()) + return; + + Rectangle2D diagramRect = shp.getBounds2D(); + + // Make sure that even empty bounds can be zoomed into. + org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect, 1); + + if (keepZoom) { + double scaleFactor = GeometryUtils.getScale(util.getTransform()); + double cwh = controlBounds.getWidth() / (scaleFactor*2); + double chh = controlBounds.getHeight() / (scaleFactor*2); + + AffineTransform view = new AffineTransform(); + view.scale(scaleFactor, scaleFactor); + view.translate(-diagramRect.getCenterX()+cwh, -diagramRect.getCenterY()+chh); + + util.setTransform(view); + } else { + MarginUtils.Margin margin = MarginUtils.marginOf(40, 0, 0); + MarginUtils.Margins margins = new MarginUtils.Margins(margin, margin, margin, margin); + util.fitArea(controlBounds, diagramRect, margins); + } + } + }); + return true; + } + + public static Resource getOwnerList(ReadGraph g, Resource listElement) throws DatabaseException { + return OrderedSetUtils.getSingleOwnerList(g, listElement, DiagramResource.getInstance(g).Composite); + } + +}