/** License (BSD Style License): * Copyright (c) 2011 * Software Engineering * Department of Computer Science * Technische Universität Darmstadt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Software Engineering Group or Technische * Universität Darmstadt nor the names of its contributors may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package de.tud.cs.st.vespucci.diagram.supports; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.util.EList; import org.eclipse.gef.EditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeCompartmentEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeNodeEditPart; import org.eclipse.gmf.runtime.notation.impl.ConnectorImpl; import org.eclipse.gmf.runtime.notation.impl.EdgeImpl; import org.eclipse.gmf.runtime.notation.impl.NodeImpl; import org.eclipse.gmf.runtime.notation.impl.ShapeImpl; import org.eclipse.swt.graphics.Color; import de.tud.cs.st.vespucci.vespucci_model.Connection; import de.tud.cs.st.vespucci.vespucci_model.Shape; /** * Collapsement supporter for EnsembleEditPart. * * @author Artem Vovk */ public class CompartmentEditPartSupporter { /** * Color for connection pointing to or from collapsed ensembles. */ public static final Color TMP_CONNECTION_COLOR = org.eclipse.draw2d.ColorConstants.red; /** * Default color for connections. */ public static final Color CONNECTION_COLOR = org.eclipse.draw2d.ColorConstants.black; private final ShapeCompartmentEditPart compartmentToSupport; private ShapeNodeEditPart editPartOfCompartment; private List<EditPart> compartmentChildren; /** * * @param compartmentToSupport * The ensemble for which collapsement support will be provided. */ public CompartmentEditPartSupporter(final ShapeCompartmentEditPart compartmentToSupport) { this.compartmentToSupport = compartmentToSupport; } /** * Update connections after EditPart was collapsed/opened. * * @param collapseEvent */ public void updateConnections(final Notification collapseEvent) { editPartOfCompartment = (ShapeNodeEditPart) compartmentToSupport.getParent(); compartmentChildren = EditPartService.getAllShapesInSideCompartment(compartmentToSupport); if (collapseEvent.getNewBooleanValue()) { collapseEditPart(); } else { openEditPart(); } } /** * Collapse edit part */ private void collapseEditPart() { final Set<ConnectionEditPart> childrenConnections = getChildrenConnections(); final Set<ConnectionEditPart> outsideConnections = excludeInternConnections(childrenConnections); final Set<ConnectionEditPart> edges = filterForDependencyConstraints(outsideConnections); for (final ConnectionEditPart outConnection : edges) { final EdgeImpl edge = (EdgeImpl) outConnection.getModel(); // it's for undo/redo operations if (outConnection.getSource().getModel() != edge.getSource() || outConnection.getTarget().getModel() != edge.getTarget()) { return; } if (compartmentChildren.contains(outConnection.getSource())) { // readjust connection and update original source history // readjust at source edge.setSource((ShapeImpl) editPartOfCompartment.getModel()); final Connection con = (Connection) edge.getElement(); con.setTemp(true); final EList<Shape> oSources = con.getOriginalSource(); oSources.add(con.getSource()); con.setSource((Shape) ((ShapeImpl) editPartOfCompartment.getModel()).getElement()); } else { // readjust at target edge.setTarget((ShapeImpl) editPartOfCompartment.getModel()); final Connection con = (Connection) edge.getElement(); con.setTemp(true); final EList<Shape> oTargets = con.getOriginalTarget(); oTargets.add(con.getTarget()); con.setTarget((Shape) ((ShapeImpl) editPartOfCompartment.getModel()).getElement()); } } } /** * @return Returns view for corresponding model element. * * @param containerEditPart * Edit part containing the given shape. * @param shapeOfNode * Model shape of the view to be searched and returned. */ private static NodeImpl getViewFromModel(final EditPart containerEditPart, final Shape shapeOfNode) { final List<EditPart> editParts = EditPartService.getAllShapesInSideCompartment(containerEditPart); for (final EditPart i : editParts) { if (i.getModel() instanceof NodeImpl) { final NodeImpl shapeImpl = (NodeImpl) i.getModel(); final Shape element = (Shape) shapeImpl.getElement(); if (element.equals(shapeOfNode)) { return shapeImpl; } } } return null; } /** * Opens the edit part and restores the connections. */ private void openEditPart() { final Set<ConnectionEditPart> connections = getAllConnections(); for (final ConnectionEditPart i : connections) { final EdgeImpl edgeToRestore = (EdgeImpl) i.getModel(); final Connection con = (Connection) edgeToRestore.getElement(); if (con.isTemp()) { if (edgeToRestore.getSource() == editPartOfCompartment.getModel() && (!con.getOriginalSource().isEmpty())) { // readjust source final EList<Shape> oSources = con.getOriginalSource(); final Shape source = oSources.remove(oSources.size() - 1); final NodeImpl shapeImpl = getViewFromModel(compartmentToSupport, source); edgeToRestore.setSource(shapeImpl); con.setSource(source); } else if (edgeToRestore.getTarget() == editPartOfCompartment.getModel() && (!con.getOriginalTarget().isEmpty())) { // readjust target final EList<Shape> oTargets = con.getOriginalTarget(); final Shape target = oTargets.remove(oTargets.size() - 1); final NodeImpl shapeImpl = getViewFromModel(compartmentToSupport, target); edgeToRestore.setTarget(shapeImpl); con.setTarget(target); } if (con.getOriginalSource().isEmpty() && con.getOriginalTarget().isEmpty()) { // connection is points direct from original source to original target, thus it's not temporal // anymore. con.setTemp(false); } } } } /** * @return Returns all connections from the children. */ @SuppressWarnings("unchecked") private Set<ConnectionEditPart> getChildrenConnections() { final List<EditPart> children = EditPartService.getAllShapesInSideCompartment(compartmentToSupport); final Set<ConnectionEditPart> childrenConnections = new HashSet<ConnectionEditPart>(); // all connections from inside for (final EditPart o : children) { if (o instanceof GraphicalEditPart) { childrenConnections.addAll(((GraphicalEditPart) o).getSourceConnections()); childrenConnections.addAll(((GraphicalEditPart) o).getTargetConnections()); } } return childrenConnections; } /** * * @param connections * @return Returns the given connections without intra-connections, i.e. connections between children or connections * between child and parent. */ private Set<ConnectionEditPart> excludeInternConnections(final Set<ConnectionEditPart> connections) { final List<EditPart> internalParts = EditPartService.getAllShapesInSideCompartment(compartmentToSupport); // connections from children to its parent are also internal internalParts.add(editPartOfCompartment); for (final ConnectionEditPart c : connections) { // check if connection has one end outside of compartment if (internalParts.contains(c.getSource()) ^ internalParts.contains(c.getTarget())) { connections.add(c); } } return connections; } /** * @return Returns all connections that belong to this editPart. Connections of children are not included. */ @SuppressWarnings("unchecked") private Set<ConnectionEditPart> getAllConnections() { final Set<ConnectionEditPart> connections = new HashSet<ConnectionEditPart>(); connections.addAll(this.editPartOfCompartment.getSourceConnections()); connections.addAll(this.editPartOfCompartment.getTargetConnections()); return filterForDependencyConstraints(connections); } /** * Filter connections for EdgeImpl; Subclass ConnectorImpl will be left out. * * @param connections * Connections to be filtered. * @return Returns filtered connections. */ private static Set<ConnectionEditPart> filterForDependencyConstraints(final Set<ConnectionEditPart> connections) { final Set<ConnectionEditPart> result = new HashSet<ConnectionEditPart>(); for (final ConnectionEditPart conn : connections) { final EdgeImpl edge = (EdgeImpl) conn.getModel(); if (edge.getElement() != null && edge.getElement() instanceof Connection) { result.add(conn); } } return result; } }