/******************************************************************************* * 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.diagram.handler; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Set; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IContributionItem; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IWorkbenchPart; 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.request.IndexRoot; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.common.utils.OrderedSetUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.diagram.content.ConnectionUtil; import org.simantics.diagram.content.EdgeResource; import org.simantics.diagram.flag.FlagUtil; import org.simantics.diagram.flag.Splitter; import org.simantics.diagram.internal.Activator; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.element.IElement; import org.simantics.scl.commands.Command; import org.simantics.scl.commands.Commands; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.ui.contribution.DynamicMenuContribution; import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.ui.AdaptionUtils; import org.simantics.utils.ui.ExceptionUtils; import org.simantics.utils.ui.workbench.WorkbenchUtils; /** * @author Tuukka Lehtonen */ public class ConnectionSplitAndJoin extends DynamicMenuContribution { private static final IContributionItem[] NONE = {}; private ICanvasContext canvas; @Override public void fill(Menu menu, int index) { // Need to grab active part here, we're still in the SWT thread. IWorkbenchPart activePart = WorkbenchUtils.getActiveWorkbenchPart(); if (activePart == null) return; ICanvasContext ctx = (ICanvasContext) activePart.getAdapter(ICanvasContext.class); if (ctx == null) return; this.canvas = ctx; try { super.fill(menu, index); } finally { this.canvas = null; } } @Override protected IContributionItem[] getContributionItems(ReadGraph graph, Object[] selection) throws DatabaseException { // If selection contains: // a) only diagram-locally connected flags, that each are connected to something, allow joining of those flag into connections // b) a single connection edge element, allow splitting into a connected flag pair Collection localConnectedFlags = Collections.emptyList(); Resource routeGraphConnection = null; EdgeResource edge = null; if (selection.length == 1) { routeGraphConnection = getRouteGraphConnection(graph, selection[0]); if (routeGraphConnection == null) { edge = getEdge(graph, selection[0]); if (edge == null) localConnectedFlags = getLocalConnectedFlags(graph, selection); } } else { localConnectedFlags = getLocalConnectedFlags(graph, selection); } if (!localConnectedFlags.isEmpty()) { return new IContributionItem[] { new ActionContributionItem(new Join(graph.getSession(), canvas, localConnectedFlags)) }; } else if (edge != null) { Selection sel = canvas.getAtMostOneItemOfClass(Selection.class); if (sel == null) return NONE; Set elems = sel.getSelection(0); if (elems.size() != 1) return NONE; IElement edgeElement = ConnectionUtil.getSingleEdge(elems.iterator().next()); if (edgeElement == null) return NONE; Point2D canvasPosition = canvas.getHintStack().getHint(DiagramHints.POPUP_MENU_CANVAS_POSITION); if (canvasPosition == null) return NONE; return new IContributionItem[] { new ActionContributionItem(new Split(graph.getSession(), canvas, canvasPosition, edgeElement, edge)) }; } else if (routeGraphConnection != null) { Selection sel = canvas.getAtMostOneItemOfClass(Selection.class); if (sel == null) return NONE; Set elems = sel.getSelection(0); if (elems.size() != 1) return NONE; Point2D canvasPosition = canvas.getHintStack().getHint(DiagramHints.POPUP_MENU_CANVAS_POSITION); if (canvasPosition == null) return NONE; return new IContributionItem[] { new ActionContributionItem(new SplitRouteGraph(graph.getSession(), canvas, canvasPosition, routeGraphConnection)) }; } return NONE; } private Resource getRouteGraphConnection(ReadGraph graph, Object object) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(graph); Resource connection = AdaptionUtils.adaptToSingle(object, Resource.class); if (connection != null && graph.isInstanceOf(connection, DIA.RouteGraphConnection)) return connection; return null; } private Collection getLocalConnectedFlags(ReadGraph graph, Object[] selection) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(graph); ArrayList result = new ArrayList(4); for (Object s : selection) { Resource r = AdaptionUtils.adaptToSingle(s, Resource.class); if (r == null || !graph.isInstanceOf(r, DIA.Flag)) return Collections.emptyList(); if (!isConnectedToSomething(graph, r)) return Collections.emptyList(); Collection counterparts = FlagUtil.getCounterparts(graph, r); if (counterparts.isEmpty()) return Collections.emptyList(); Collection flagDiagrams = OrderedSetUtils.getOwnerLists(graph, r, DIA.Diagram); for (Resource counterpart : counterparts) { boolean joinedWithinSingleDiagram = !Collections.disjoint(flagDiagrams, OrderedSetUtils.getOwnerLists(graph, counterpart, DIA.Diagram)); if (!joinedWithinSingleDiagram) return Collections.emptyList(); if (!isConnectedToSomething(graph, counterpart)) return Collections.emptyList(); } result.add(r); } return result; } private boolean isConnectedToSomething(ReadGraph graph, Resource flag) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); for (Resource connector : graph.getObjects(flag, STR.IsConnectedTo)) { Resource cntr = graph.getPossibleObject(connector, DIA.AreConnected); Resource conn = ConnectionUtil.getConnection(graph, cntr); if (cntr == null || conn == null) return false; return true; } return false; } private EdgeResource getEdge(ReadGraph graph, Object object) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(graph); Resource connection = null; EdgeResource edge = AdaptionUtils.adaptToSingle(object, EdgeResource.class); if (edge != null) //connection = ConnectionUtil.getConnection(graph, edge); return edge; else { connection = AdaptionUtils.adaptToSingle(object, Resource.class); } if (connection != null && graph.isInstanceOf(connection, DiagramResource.getInstance(graph).Connection)) { Collection connectors = graph.getObjects(connection, DIA.HasConnector); Collection branchPoints = graph.getObjects(connection, DIA.HasBranchPoint); if (branchPoints.isEmpty() && connectors.size() == 2) { Iterator it = connectors.iterator(); Resource connector1 = it.next(); Resource connector2 = it.next(); return new EdgeResource(connector1, connector2); } } return null; } public static abstract class Helper extends Action { protected final Session session; protected final ICanvasContext context; public Helper(String label, Session session, ICanvasContext context) { super(label); this.session = session; this.context = context; } @Override public void run() { try { session.syncRequest(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { graph.markUndoPoint(); performAction(graph); } }); ThreadUtils.asyncExec(context.getThreadAccess(), new Runnable() { @Override public void run() { if (context.isDisposed()) return; Selection selection = context.getAtMostOneItemOfClass(Selection.class); if (selection != null) { // This prevents workbench selection from being left over. // Also prevents scene graph crap from being left on the screen. selection.clear(0); } } }); } catch (DatabaseException e) { ExceptionUtils.logError(e); } } /** * @param graph */ protected abstract void performAction(WriteGraph graph) throws DatabaseException; } public static class Split extends Helper { private final Point2D splitCanvasPos; private final IElement edgeElement; private final EdgeResource edge; public Split(Session session, ICanvasContext context, Point2D splitCanvasPos, IElement edgeElement, EdgeResource edge) { super("Split Connection", session, context); setImageDescriptor(Activator.LINK_BREAK_ICON); this.splitCanvasPos = splitCanvasPos; this.edgeElement = edgeElement; this.edge = edge; } @Override protected void performAction(WriteGraph graph) throws DatabaseException { new Splitter(graph).split(graph, edgeElement, edge, splitCanvasPos); } } public static class SplitRouteGraph extends Helper { private final Point2D splitCanvasPos; private final Resource connection; public SplitRouteGraph(Session session, ICanvasContext context, Point2D splitCanvasPos, Resource connection) { super("Split Connection", session, context); setImageDescriptor(Activator.LINK_BREAK_ICON); this.splitCanvasPos = splitCanvasPos; this.connection = connection; } @Override protected void performAction(WriteGraph graph) throws DatabaseException { Command command = Commands.get(graph, "Simantics/Diagram/splitConnection"); command.execute(graph, graph.syncRequest(new IndexRoot(connection)), connection, splitCanvasPos.getX(), splitCanvasPos.getY()); } } public static class Join extends Helper { private final Collection flags; public Join(Session session, ICanvasContext context, Collection flags) { super("Join Flags", session, context); setImageDescriptor(Activator.LINK_ICON); this.flags = flags; } @Override protected void performAction(WriteGraph graph) throws DatabaseException { Command command = Commands.get(graph, "Simantics/Diagram/joinFlagsLocal"); command.execute(graph, graph.syncRequest(new IndexRoot(flags.iterator().next())), flags); } } }