Java tutorial
/****************************************************************************** * Copyright (c) 2002, 2013 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * Mariot Chauvin <mariot.chauvin@obeo.fr> - bug 243888 * Obeo - parts copied from org.eclipse.gmf.runtime.diagram.ui.providers.internal.DefaultProvider * and org.eclipse.gmf.runtime.diagram.ui.providers.internal.CompositeLayoutProvider * and adapted for Sirius. *******************************************************************************/ package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.ListIterator; import java.util.Map; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.draw2d.graph.DirectedGraph; import org.eclipse.draw2d.graph.DirectedGraphLayout; import org.eclipse.draw2d.graph.Edge; import org.eclipse.draw2d.graph.EdgeList; import org.eclipse.draw2d.graph.Node; import org.eclipse.draw2d.graph.NodeList; import org.eclipse.draw2d.graph.Subgraph; import org.eclipse.gef.ConnectionEditPart; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gmf.runtime.common.core.service.IOperation; import org.eclipse.gmf.runtime.diagram.ui.editparts.CompartmentEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.GroupEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeCompartmentEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; import org.eclipse.gmf.runtime.diagram.ui.providers.internal.CompositeLayoutProvider; import org.eclipse.gmf.runtime.draw2d.ui.internal.graph.AdvancedSubGraph; import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; import org.eclipse.gmf.runtime.notation.LayoutConstraint; import org.eclipse.gmf.runtime.notation.Size; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.sirius.diagram.DDiagramElement; import org.eclipse.sirius.diagram.tools.api.layout.PinHelper; import org.eclipse.sirius.diagram.ui.edit.api.part.AbstractDiagramElementContainerEditPart; import org.eclipse.sirius.diagram.ui.edit.api.part.IDiagramElementEditPart; import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutExtender; import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.ExtendableLayoutProvider; import org.eclipse.sirius.diagram.ui.tools.internal.layout.ArrangeAllWithAutoSize; import org.eclipse.sirius.diagram.ui.tools.internal.layout.AutoSizeAndRegionAwareGraphLayout; import org.eclipse.sirius.diagram.ui.tools.internal.layout.DiagramLayoutCustomization; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; /** * Abstract base class to factor out the common code between * {@link CompositeLeftRightLayoutProvider}, * {@link CompositeDownTopLayoutProvider} and * {@link CompositeTopDownLayoutProvider}. * * @author pcdavid */ @SuppressWarnings("restriction") public abstract class AbstractCompositeLayoutProvider extends CompositeLayoutProvider implements ExtendableLayoutProvider { private final LayoutExtender extender = new LayoutExtender(this); private final DiagramLayoutCustomization padder = new DiagramLayoutCustomization(); private final ArrangeAllWithAutoSize autoSize = new ArrangeAllWithAutoSize(); private Predicate<Object> validateAllElementInArrayListAreIDiagramElementEditPart = new Predicate<Object>() { public boolean apply(Object input) { return input instanceof IDiagramElementEditPart; } }; private ArrayList<IDiagramElementEditPart> elementsToKeepFixed = Lists.newArrayList(); /** * {@inheritDoc} * * Made public to be available from ArrangeAllWithAutoSize. */ @Override public abstract Rectangle translateToGraph(final Rectangle r); /** * {@inheritDoc} * * Made public to be available from ArrangeAllWithAutoSize. */ @Override public abstract Rectangle translateFromGraph(final Rectangle rect); /** * {@inheritDoc} */ @Override public boolean provides(final IOperation operation) { return true; } /** * {@inheritDoc} */ public boolean handleConnectableListItems() { return shouldHandleConnectableListItems(); } /** * {@inheritDoc} */ public Rectangle provideNodeMetrics(final Node node) { return getNodeMetrics(node); } /** * {@inheritDoc} */ public LayoutExtender getExtender() { return extender; } /** * {@inheritDoc} */ @Override public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint) { padder.initializePaddingWithEditParts(selectedObjects); extender.startLayouting(); // Clear the list of elements to keep fixed. elementsToKeepFixed.clear(); // Finds if there are unpinned diagram elements to keep fixed stored in // the LayoutHint as a Collection if (layoutHint.getAdapter(Collection.class) instanceof ArrayList<?> && Iterables.all((ArrayList<?>) layoutHint.getAdapter(Collection.class), validateAllElementInArrayListAreIDiagramElementEditPart)) { elementsToKeepFixed = new ArrayList<IDiagramElementEditPart>( (ArrayList<IDiagramElementEditPart>) layoutHint.getAdapter(Collection.class)); } // return super.layoutEditParts(selectedObjects, layoutHint); Command result = super.layoutEditParts(selectedObjects, layoutHint); // Clear the list of elements to keep fixed (to avoid memory leak) elementsToKeepFixed.clear(); return result; } /** * {@inheritDoc} */ @Override protected DirectedGraphLayout createGraphLayout() { return new AutoSizeAndRegionAwareGraphLayout(); } /** * {@inheritDoc} */ @SuppressWarnings("rawtypes") @Override protected List getRelevantConnections(final Hashtable editPartToNodeDict) { /* * We're wrapping the original hashtable with this forwarding one not * failling when called with a null get because we've got cases where * editparts are linked through connections not having source/target * (for instance during folding). */ @SuppressWarnings("serial") final List list = super.getRelevantConnections(new Hashtable() { @Override public synchronized Object get(Object key) { if (key != null) { return editPartToNodeDict.get(key); } return null; } @Override public synchronized Enumeration keys() { return editPartToNodeDict.keys(); } }); return extender.getRelevantConnections(editPartToNodeDict, list); } // CHECKSTYLE:OFF /* * The code in the methods below is copy/pasted and slightly adapted from * GMF, as it could not be overridden cleanly. */ @Override protected Command update_diagram(org.eclipse.gef.GraphicalEditPart diagramEP, DirectedGraph g, boolean isLayoutForSelected) { /* * We define the layout default margin at 0 otherwise consecutive calls * to the ArrangeAll actions will always move the nodes in containers. */ CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ final Point diff = getLayoutPositionDelta(g, isLayoutForSelected); layoutDefaultMargin = MapModeUtil.getMapMode(diagramEP.getFigure()).DPtoLP(25); Command cmd = createNodeChangeBoundCommands(g, diff); if (cmd != null) { cc.add(cmd); } cmd = createEdgesChangeBoundsCommands(g, diff); if (cmd != null) { cc.add(cmd); } return cc; } /** * Only used if ENABLE_PARTIAL_OVERLAP_FIX is true. This is an almost * verbatim copy of the version in the parent class, except for the use of * the custom getNodeMetricsConsideringBorderedNodes. */ private Point getLayoutPositionDelta(DirectedGraph g, boolean isLayoutForSelected) { // If laying out selected objects, use diff variables to // position objects at topleft corner of enclosing rectangle. if (isLayoutForSelected) { ListIterator vi; vi = g.nodes.listIterator(); Point ptLayoutMin = new Point(-1, -1); while (vi.hasNext()) { Node node = (Node) vi.next(); // ignore ghost node if (node.data != null) { Rectangle nodeExt = getNodeMetricsConsideringBorderedNodes(node); if (ptLayoutMin.x == -1) { ptLayoutMin.x = nodeExt.x; ptLayoutMin.y = nodeExt.y; } else { ptLayoutMin.x = Math.min(ptLayoutMin.x, nodeExt.x); ptLayoutMin.y = Math.min(ptLayoutMin.y, nodeExt.y); } } } return new Point(this.minX - ptLayoutMin.x, this.minY - ptLayoutMin.y); } return new Point(layoutDefaultMargin, layoutDefaultMargin); } private Rectangle getNodeMetricsConsideringBorderedNodes(Node node) { Rectangle result = getNodeMetrics(node); if (node instanceof Subgraph && node.data instanceof IGraphicalEditPart) { Subgraph subGraph = (Subgraph) node; NodeList children = subGraph.members; for (int i = 0; i < children.size(); i++) { Node child = children.getNode(i); if (child instanceof IBorderItemEditPart) { result.union(getNodeMetrics(child)); } } } return result; } /** * {@inheritDoc} * * @see org.eclipse.gmf.runtime.diagram.ui.providers.internal.DefaultProvider#build_edges(java.util.List, * java.util.Map) */ @Override protected EdgeList buildEdges(final List selectedObjects, final Map editPartToNodeDict) { return super.buildEdges(extender.filterEdges(selectedObjects, editPartToNodeDict), editPartToNodeDict); } @Override protected NodeList buildNodes(List selectedObjects, Map editPartToNodeDict, Subgraph rootGraph) { ListIterator li = selectedObjects.listIterator(); // <added for auto-size> autoSize.prepareForArrangeAll(Iterators.filter(li, AbstractDiagramElementContainerEditPart.class), elementsToKeepFixed); // </added for auto-size> NodeList nodes = new NodeList(); li = selectedObjects.listIterator(); while (li.hasNext()) { IGraphicalEditPart gep = (IGraphicalEditPart) li.next(); boolean hasChildren = hasChildren(gep); if (!(gep instanceof IBorderItemEditPart) && (gep instanceof ShapeEditPart || gep instanceof ShapeCompartmentEditPart)) { GraphicalEditPart ep = (GraphicalEditPart) gep; Point position = ep.getFigure().getBounds().getLocation(); if (minX == -1) { minX = position.x; minY = position.y; } else { minX = Math.min(minX, position.x); minY = Math.min(minY, position.y); } Node n = null; if (hasChildren && !(gep instanceof GroupEditPart)) { AdvancedSubGraph subGraph = null; if (rootGraph != null) { subGraph = new AdvancedSubGraph(ep, rootGraph); } else { subGraph = new AdvancedSubGraph(ep); } subGraph.setAutoSize(isAutoSizeOn(subGraph, ep)); if (gep instanceof CompartmentEditPart) { subGraph.setHasBufferedZone(true); } subGraph.setDirection(getLayoutDirection(ep)); n = subGraph; } else { if (rootGraph != null) { n = new Node(ep, rootGraph); } else { n = new Node(ep); } } adjustNodePadding(n, editPartToNodeDict); // <modified for auto-size> Dimension size = autoSize.getSizeToConsiderDuringArrangeAll(ep); // </modified for auto-size> setNodeMetrics(n, new Rectangle(position.x, position.y, size.width, size.height)); editPartToNodeDict.put(ep, n); nodes.add(n); if (hasChildren && !(gep instanceof GroupEditPart)) { buildNodes(gep.getChildren(), editPartToNodeDict, (Subgraph) n); } } } return nodes; } private boolean isAutoSizeOn(AdvancedSubGraph subGraph, IGraphicalEditPart gEP) { if (gEP instanceof CompartmentEditPart && subGraph.getParent() instanceof AdvancedSubGraph) { if (((AdvancedSubGraph) subGraph.getParent()).isAutoSize()) { return true; } } else { View notationView = gEP.getNotationView(); if (notationView != null && notationView instanceof org.eclipse.gmf.runtime.notation.Node) { org.eclipse.gmf.runtime.notation.Node node = (org.eclipse.gmf.runtime.notation.Node) notationView; LayoutConstraint contraint = node.getLayoutConstraint(); if (contraint instanceof Size) { Size size = (Size) contraint; if (size.getHeight() != -1 && size.getWidth() != -1) { return ArrangeAllWithAutoSize.isEnabled(); } return true; } } } return false; } @Override protected void createSubCommands(Point diff, ListIterator vi, CompoundCommand cc) { final List nodes = Lists.newArrayList(vi); if (ArrangeAllWithAutoSize.isEnabled()) { autoSize.createSubCommands(diff, nodes.listIterator(), cc, this, minX, minY); } else { // No auto-size : region will not be moved. super.createSubCommands(diff, nodes.listIterator(), cc); } extender.keepLocationChanges(nodes, diff); } /** * {@inheritDoc} */ @Override protected void adjustNodePadding(final Node node, final Map editPartToNodeDict) { // <modification> final GraphicalEditPart ep = (GraphicalEditPart) node.data; final Insets padding = padder.getNodePadding(ep); // </modification> // check if the direct parent is added already to the graph final GraphicalEditPart parent = (GraphicalEditPart) ep.getParent(); if (parent != null && node.getParent() != null && editPartToNodeDict.get(parent) != node.getParent()) { // now the direct parent is not added to the graph so, we had // to adjust the padding of the node to consider the parent final IFigure thisFigure = parent.getFigure(); final IFigure parentFigure = ((GraphicalEditPart) node.getParent().data).getFigure(); final Point parentLocation = parentFigure.getBounds().getLocation(); final Point nodeLocation = thisFigure.getBounds().getLocation(); thisFigure.translateToAbsolute(nodeLocation); parentFigure.translateToAbsolute(parentLocation); final Dimension delta = nodeLocation.getDifference(parentLocation); final Rectangle rect = translateToGraph(new Rectangle(delta.width, delta.height, 0, 0)); padding.top += rect.y; padding.left += rect.x; } node.setPadding(padding); } /** * Method override to avoid move of edges that have considered as fixed. * * {@inheritDoc} * * @see org.eclipse.gmf.runtime.diagram.ui.providers.internal.CompositeLayoutProvider#routeThrough(org.eclipse.draw2d.graph.Edge, * org.eclipse.gef.ConnectionEditPart, org.eclipse.draw2d.graph.Node, * org.eclipse.draw2d.graph.Node, * org.eclipse.draw2d.geometry.PointList, * org.eclipse.draw2d.geometry.Point) */ @Override protected Command routeThrough(Edge edge, ConnectionEditPart connectEP, Node source, Node target, PointList points, Point diff) { if (connectEP instanceof IGraphicalEditPart && isPinned((IGraphicalEditPart) connectEP) || (elementsToKeepFixed != null && elementsToKeepFixed.contains(connectEP))) { return null; } return super.routeThrough(edge, connectEP, source, target, points, diff); } /** * Tests whether an edit part should be considered as pinned (fixed size and * location) during the layout. * * @param part * the edit part. * @return <code>true</code> if the edit part should be considered as * pinned. */ protected boolean isPinned(final IGraphicalEditPart part) { boolean isPinned = false; if (part.resolveSemanticElement() instanceof DDiagramElement) { DDiagramElement dDiagramElement = (DDiagramElement) part.resolveSemanticElement(); isPinned = new PinHelper().isPinned(dDiagramElement); } return isPinned; } }