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