| /****************************************************************************** |
| * Copyright (c) 2002, 2004 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 |
| ****************************************************************************/ |
| |
| package org.eclipse.gmf.runtime.diagram.ui.providers.internal; |
| |
| import java.security.InvalidParameterException; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.IAdaptable; |
| 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.EditPart; |
| import org.eclipse.gef.GraphicalEditPart; |
| import org.eclipse.gef.Request; |
| import org.eclipse.gef.commands.Command; |
| import org.eclipse.gef.commands.CompoundCommand; |
| import org.eclipse.gef.requests.ChangeBoundsRequest; |
| import org.eclipse.gmf.runtime.common.core.service.IOperation; |
| import org.eclipse.gmf.runtime.common.core.util.Trace; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderedShapeEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.ListCompartmentEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.ListItemEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.internal.services.layout.LayoutNodesOperation; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; |
| import org.eclipse.gmf.runtime.diagram.ui.requests.SetAllBendpointRequest; |
| import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; |
| import org.eclipse.gmf.runtime.diagram.ui.services.layout.LayoutType; |
| import org.eclipse.gmf.runtime.draw2d.ui.geometry.PointListUtilities; |
| import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; |
| import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; |
| import org.eclipse.gmf.runtime.notation.View; |
| import org.eclipse.jface.util.Assert; |
| |
| /** |
| * Provider that creates a command for the DirectedGraph layout in GEF. |
| * |
| * @author sshaw |
| * @canBeSeenBy org.eclipse.gmf.runtime.diagram.ui.providers.* |
| * |
| */ |
| public abstract class DefaultProvider |
| extends AbstractLayoutEditPartProvider { |
| |
| // Minimum sep between icon and bottommost horizontal arc |
| protected int minX = -1; |
| protected int minY = -1; |
| protected int layoutDefaultMargin = 0; |
| protected IMapMode mm; |
| |
| protected static final int NODE_PADDING = 30; |
| protected static final int MIN_EDGE_PADDING = 5; |
| protected static final int MAX_EDGE_PADDING = NODE_PADDING * 3; |
| |
| |
| |
| /** |
| * @return the <code>IMapMode</code> that maps coordinates from |
| * device to logical and vice-versa. |
| */ |
| protected IMapMode getMapMode() { |
| return mm; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.common.core.service.IProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) |
| */ |
| public boolean provides(IOperation operation) { |
| Assert.isNotNull(operation); |
| |
| View cview = getContainer(operation); |
| if (cview == null) |
| return false; |
| |
| IAdaptable layoutHint = ((LayoutNodesOperation) operation) |
| .getLayoutHint(); |
| String layoutType = (String) layoutHint.getAdapter(String.class); |
| return LayoutType.DEFAULT.equals(layoutType); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(org.eclipse.gef.GraphicalEditPart, org.eclipse.core.runtime.IAdaptable) |
| */ |
| public Command layoutEditParts(GraphicalEditPart containerEditPart, |
| IAdaptable layoutHint) { |
| if (containerEditPart == null) { |
| InvalidParameterException ipe = new InvalidParameterException(); |
| Trace.throwing(DiagramProvidersPlugin.getInstance(), |
| DiagramProvidersDebugOptions.EXCEPTIONS_THROWING, getClass(), |
| "layout()", //$NON-NLS-1$ |
| ipe); |
| throw ipe; |
| } |
| mm = MapModeUtil.getMapMode(containerEditPart.getFigure()); |
| // setup graph |
| DirectedGraph g = createGraph(); |
| build_graph(g, containerEditPart.getChildren()); |
| createGraphLayout().visit(g); |
| // update the diagram based on the graph |
| Command cmd = update_diagram(containerEditPart, g, false); |
| |
| // reset mm mapmode to avoid memory leak |
| mm = null; |
| return cmd; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(java.util.List, org.eclipse.core.runtime.IAdaptable) |
| */ |
| public Command layoutEditParts(List selectedObjects, IAdaptable layoutHint) { |
| |
| if (selectedObjects.size() == 0) { |
| return null; |
| } |
| |
| // get the container edit part for the children |
| GraphicalEditPart editPart = (GraphicalEditPart) selectedObjects.get(0); |
| GraphicalEditPart containerEditPart = (GraphicalEditPart) editPart |
| .getParent(); |
| |
| mm = MapModeUtil.getMapMode(containerEditPart.getFigure()); |
| |
| DirectedGraph g = createGraph(); |
| build_graph(g, selectedObjects); |
| createGraphLayout().visit(g); |
| // update the diagram based on the graph |
| Command cmd = update_diagram(containerEditPart, g, true); |
| |
| // reset mm mapmode to avoid memory leak |
| mm = null; |
| return cmd; |
| } |
| |
| /** |
| * layoutTopDown Utility function that is commonly subclasses by domain |
| * specific layouts to determine whether a specific connection type is layed |
| * out in a top down manner. |
| * |
| * @param poly |
| * <code>ConnectionEditPart</code> to determine whether it is to be layed |
| * out in a top-down fashion. |
| * @return true if connection is to be layed out top-down, false otherwise. |
| */ |
| protected boolean layoutTopDown(ConnectionEditPart poly) { |
| return false; |
| } |
| |
| /** |
| * build_nodes Method to build up the nodes in the temporary Graph structure |
| * which the algorithm is executed on. |
| * |
| * @param selectedObjects |
| * List of selected objects to be layed out. |
| * @param editPartToNodeDict |
| * Map of editparts from the GEF to the temporary Nodes used for |
| * layout. |
| * @return NodeList list of Nodes that is passed to the graph structure. |
| */ |
| protected NodeList build_nodes(List selectedObjects, Map editPartToNodeDict, Subgraph root) { |
| ListIterator li = selectedObjects.listIterator(); |
| |
| NodeList nodes = new NodeList(); |
| |
| while (li.hasNext()) { |
| |
| IGraphicalEditPart gep = (IGraphicalEditPart) li.next(); |
| if (gep instanceof ShapeEditPart) { |
| |
| ShapeEditPart shapeEP = (ShapeEditPart) gep; |
| |
| Point position = shapeEP.getLocation(); |
| |
| // determine topleft most point, layout of items will be placed |
| // starting at topleft point |
| if (minX == -1) { |
| minX = position.x; |
| minY = position.y; |
| } else { |
| minX = Math.min(minX, position.x); |
| minY = Math.min(minY, position.y); |
| } |
| |
| Node n = new Node(shapeEP); |
| n.setPadding(new Insets(NODE_PADDING)); |
| Dimension size = shapeEP.getSize(); |
| |
| setNodeMetrics(n, new Rectangle(position.x, position.y, |
| size.width, size.height)); |
| |
| editPartToNodeDict.put(shapeEP, n); |
| nodes.add(n); |
| } |
| } |
| |
| return nodes; |
| } |
| |
| /** |
| * setNodeMetrics Sets the extend and position into the node object. Defined |
| * as abstract to allow subclasses to implement to perform a transformation |
| * on the values stored in the node. i.e. support for Left-Right layout as |
| * opposed to Top-Down. |
| * |
| * @param n |
| * Node that will receive the metrics values to be set. |
| * @param r |
| * Rectangle that represents the location and extend of the Node. |
| */ |
| final protected void setNodeMetrics(Node n, Rectangle r) { |
| Rectangle rectGraph = translateToGraph(r); |
| n.x = rectGraph.x; |
| n.y = rectGraph.y; |
| n.width = rectGraph.width; |
| n.height = rectGraph.height; |
| } |
| |
| /** |
| * getNodeMetrics Retrieves the node extend and position from the node |
| * object. Defined as abstract to allow subclasses to implement to perform a |
| * transformation on the values stored in the node. i.e. support for |
| * Left-Right layout as opposed to Top-Down. |
| * |
| * @param n |
| * Node that has the metrics values to be retrieved. |
| * @return Rectangle that represents the location and extend of the Node. |
| */ |
| protected Rectangle getNodeMetrics(Node n) { |
| Rectangle rect = null; |
| Node parent = n.getParent(); |
| while (parent!=null &&!(parent.data instanceof IGraphicalEditPart)) |
| parent = parent.getParent(); |
| if (parent!=null){ |
| rect = new Rectangle(n.x - parent.x, n.y - parent.y, n.width, n.height); |
| } |
| else |
| rect = new Rectangle(n.x, n.y, n.width, n.height); |
| return translateFromGraph(rect); |
| } |
| |
| /** |
| * Retrieves the extent and position from the given logical rectangle in |
| * GEF graph coordinates. Defined as abstract to allow subclasses to implement |
| * to perform a transformation on the values stored in the node. i.e. support for |
| * Left-Right layout as opposed to Top-Down. |
| * |
| * @param rect |
| * <code>Rectangle</code> that has the values to be translated in |
| * logical (relative) coordinates. |
| * |
| * @return <code>Rectangle</code> in graph coordinates. |
| */ |
| abstract protected Rectangle translateToGraph(Rectangle r); |
| |
| /** |
| * Retrieves the logical extent and position from the given rectangle. |
| * Defined as abstract to allow subclasses to implement to perform a |
| * transformation on the values stored in the node. i.e. support for |
| * Left-Right layout as opposed to Top-Down. |
| * |
| * @param rect |
| * <code>Rectangle</code> that has the values to be translated in |
| * graph (pixel) coordinates. |
| * @return <code>Rectangle</code> in logical coordinates. |
| */ |
| abstract protected Rectangle translateFromGraph(Rectangle rect); |
| |
| /** |
| * build_edges Method to build up the edges in the temporary Graph structure |
| * which the algorithm is executed on. |
| * |
| * selectedObjects List of selected objects to be layed out. |
| * |
| * @param editPartToNodeDict |
| * Map of editparts from the GEF to the temporary Nodes used for |
| * layout. |
| * @return EdgeList list of Edges that is passed to the graph structure. |
| */ |
| protected EdgeList build_edges(List selectedObjects, Map editPartToNodeDict) { |
| |
| EdgeList edges = new EdgeList(); |
| |
| // Do "topdown" relationships first. Since they traditionally |
| // go upward on a diagram, they are reversed when placed in the graph |
| // for |
| // layout. Also, layout traverses the arcs from each node in the order |
| // of their insertion when finding a spanning tree for its constructed |
| // hierarchy. Therefore, arcs added early are less likely to be |
| // reversed. |
| // In fact, since there are no cycles in these arcs, adding |
| // them to the graph first should guarantee that they are never |
| // reversed, |
| // i.e., the inheritance hierarchy is preserved graphically. |
| ArrayList objects = new ArrayList(); |
| objects.addAll(selectedObjects); |
| ListIterator li = objects.listIterator(); |
| ArrayList notTopDownEdges = new ArrayList(); |
| boolean shouldHandleListItems = shouldHandleConnectableListItems(); |
| while (li.hasNext()) { |
| EditPart gep = (EditPart) li.next(); |
| if (gep instanceof ConnectionEditPart) { |
| ConnectionEditPart poly = (ConnectionEditPart) gep; |
| |
| if (layoutTopDown(poly)) { |
| EditPart from = poly.getSource(); |
| EditPart to = poly.getTarget(); |
| if (from instanceof IBorderItemEditPart) |
| from = from.getParent(); |
| else if (shouldHandleListItems && from instanceof ListItemEditPart) |
| from = getFirstAnscestorinNodesMap(from, editPartToNodeDict); |
| if (to instanceof IBorderItemEditPart) |
| to = to.getParent(); |
| else if (shouldHandleListItems && to instanceof ListItemEditPart) |
| to = getFirstAnscestorinNodesMap(to, editPartToNodeDict); |
| Node fromNode = (Node) editPartToNodeDict.get(from); |
| Node toNode = (Node) editPartToNodeDict.get(to); |
| |
| if (fromNode != null && toNode != null |
| && !fromNode.equals(toNode)) { |
| addEdge(edges, poly, toNode, fromNode); |
| } |
| }else{ |
| notTopDownEdges.add(poly); |
| } |
| } |
| } |
| |
| // third pass for all other connections |
| li = notTopDownEdges.listIterator(); |
| while (li.hasNext()) { |
| ConnectionEditPart poly = (ConnectionEditPart) li.next(); |
| EditPart from = poly.getSource(); |
| EditPart to = poly.getTarget(); |
| if (from instanceof IBorderItemEditPart) |
| from = from.getParent(); |
| else if (shouldHandleListItems && from instanceof ListItemEditPart) |
| from = getFirstAnscestorinNodesMap(from, editPartToNodeDict); |
| if (to instanceof IBorderItemEditPart) |
| to = to.getParent(); |
| else if (shouldHandleListItems && to instanceof ListItemEditPart) |
| to = getFirstAnscestorinNodesMap(to, editPartToNodeDict); |
| Node fromNode = (Node) editPartToNodeDict.get(from); |
| Node toNode = (Node) editPartToNodeDict.get(to); |
| |
| if (fromNode != null && toNode != null |
| && !fromNode.equals(toNode)) { |
| addEdge(edges, poly, fromNode, toNode); |
| } |
| } |
| return edges; |
| } |
| |
| /** |
| * @param edges |
| * @param gep |
| * @param fromNode |
| * @param toNode |
| */ |
| private void addEdge(EdgeList edges, ConnectionEditPart connectionEP, |
| Node fromNode, Node toNode) { |
| Edge edge = new Edge(connectionEP, fromNode, toNode); |
| initializeEdge(connectionEP, edge); |
| |
| edges.add(edge); |
| } |
| |
| /** |
| * initializeEdge Method used as a hook to initialize the Edge layout |
| * object. LayoutProvider subclasses may wish to initialize the edge |
| * different to customize the layout for their diagram domain. |
| * |
| * @param connectionEP |
| * EditPart used as a seed to initialize the edge properties |
| * @param edge |
| * Edge to initialize with default values for the layout |
| */ |
| protected void initializeEdge(ConnectionEditPart connectionEP, Edge edge) { |
| List affectingChildren = getAffectingChildren(connectionEP); |
| |
| // set the padding based on the extent of the children. |
| edge.setPadding(Math.max(edge.getPadding(), calculateEdgePadding(connectionEP, affectingChildren))); |
| edge.setDelta(Math.max(edge.getDelta(), affectingChildren.size() / 2)); |
| } |
| |
| /** |
| * Calculates the edge padding needed to initialize edge with. Uses the number of children as a factor in |
| * determine the dynamic padding value. |
| */ |
| private int calculateEdgePadding(ConnectionEditPart connectionEP, List affectingChildren) { |
| ListIterator li = affectingChildren.listIterator(); |
| |
| int padding = 0; |
| |
| // union the children widths |
| while (li.hasNext()) { |
| GraphicalEditPart gep = (GraphicalEditPart)li.next(); |
| |
| padding = Math.max(padding, Math.max(gep.getFigure().getBounds().width, gep.getFigure().getBounds().height)); |
| } |
| |
| Rectangle.SINGLETON.x = 0; |
| Rectangle.SINGLETON.y = 0; |
| Rectangle.SINGLETON.width = padding; |
| Rectangle.SINGLETON.height = padding; |
| return Math.min(Math.max(Math.round(translateToGraph(Rectangle.SINGLETON).width * 1.5f), MIN_EDGE_PADDING), MAX_EDGE_PADDING); |
| } |
| |
| /** |
| * Retrieve the associated children from the given connection editpart that will affect |
| * the layout. |
| * |
| * @param conn the <code>ConnectionEditPart</code> to retrieve the children from |
| * @return a <code>List</code> that contains <code>GraphicalEditPart</code> objects |
| */ |
| private List getAffectingChildren(ConnectionEditPart conn) { |
| List children = conn.getChildren(); |
| ListIterator lli = children.listIterator(); |
| List affectingChildrenList = new ArrayList(); |
| while (lli.hasNext()) { |
| Object obj = lli.next(); |
| if (obj instanceof GraphicalEditPart) { |
| GraphicalEditPart lep = (GraphicalEditPart)obj; |
| Rectangle lepBox = lep.getFigure().getBounds().getCopy(); |
| |
| if (!lep.getFigure().isVisible() || |
| lepBox.width == 0 || lepBox.height == 0) |
| continue; |
| |
| affectingChildrenList.add(lep); |
| } |
| } |
| return affectingChildrenList; |
| } |
| |
| /** |
| * getRelevantConnections Given the editpart to Nodes Map this will |
| * calculate the connections in the diagram that are important to the |
| * layout. |
| * |
| * @param editPartToNodeDict |
| * Hashtable of editparts from the GEF to the temporary Nodes |
| * used for layout. |
| * @return List of ConnectionEditPart that are to be part of the layout |
| * routine. |
| */ |
| protected List getRelevantConnections(Hashtable editPartToNodeDict) { |
| Enumeration enumeration = editPartToNodeDict.keys(); |
| ArrayList connectionsToMove = new ArrayList(); |
| while (enumeration.hasMoreElements()) { |
| Object e = enumeration.nextElement(); |
| GraphicalEditPart shapeEP = (GraphicalEditPart) e; |
| Set sourceConnections = new HashSet(shapeEP.getSourceConnections()); |
| if (shapeEP instanceof IBorderedShapeEditPart){ |
| List borderItems = getBorderItemEditParts(shapeEP); |
| for (Iterator iter = borderItems.iterator(); iter.hasNext();) { |
| GraphicalEditPart element = (GraphicalEditPart) iter.next(); |
| sourceConnections.addAll(element.getSourceConnections()); |
| } |
| } |
| |
| for (Iterator iter = sourceConnections.iterator(); |
| iter.hasNext();) { |
| ConnectionEditPart connectionEP = (ConnectionEditPart) iter.next(); |
| EditPart target = connectionEP.getTarget(); |
| // check to see if the toView is in the shapesDict, if yes, |
| // the associated connectionView should be included on graph |
| if (target instanceof IBorderItemEditPart) |
| target = target.getParent(); |
| Object o = editPartToNodeDict.get(target); |
| if (o != null) { |
| connectionsToMove.add(connectionEP); |
| } |
| } |
| |
| if (shouldHandleConnectableListItems()){ |
| handleConnectableListItems(shapeEP,editPartToNodeDict,connectionsToMove); |
| } |
| } |
| |
| return connectionsToMove; |
| } |
| |
| private void handleConnectableListItems(GraphicalEditPart shapeEP, Map editPartToNodeDict, ArrayList connectionsToMove) { |
| List children = shapeEP.getChildren(); |
| for (Iterator iter = children.iterator(); iter.hasNext();) { |
| EditPart ep = (EditPart) iter.next(); |
| if (ep instanceof ListCompartmentEditPart){ |
| List listItems = ep.getChildren(); |
| for (Iterator iterator = listItems.iterator(); iterator |
| .hasNext();) { |
| GraphicalEditPart listItem = (GraphicalEditPart) iterator.next(); |
| List connections =listItem.getSourceConnections(); |
| for (Iterator connectionIterator = connections.iterator(); connectionIterator |
| .hasNext();) { |
| ConnectionEditPart connectionEP = (ConnectionEditPart) connectionIterator.next(); |
| EditPart ancestor = getFirstAnscestorinNodesMap(connectionEP.getTarget(),editPartToNodeDict); |
| if (ancestor!=null) |
| connectionsToMove.add(connectionEP); |
| } |
| } |
| } |
| |
| } |
| |
| } |
| |
| private EditPart getFirstAnscestorinNodesMap(EditPart editPart,Map editPartToNodeDict) { |
| EditPart ancestor = editPart; |
| while (ancestor!=null){ |
| if (editPartToNodeDict.get(ancestor)!=null) |
| return ancestor; |
| ancestor = ancestor.getParent(); |
| } |
| return null; |
| } |
| |
| /** |
| * This method searches an edit part for a child that is a border item edit part |
| * @param parent part needed to search |
| * @param set to be modified of border item edit parts that are direct children of the parent |
| */ |
| private List getBorderItemEditParts(EditPart parent) { |
| Iterator iter = parent.getChildren().iterator(); |
| List list = new ArrayList(); |
| while(iter.hasNext()) { |
| EditPart child = (EditPart)iter.next(); |
| if( child instanceof IBorderItemEditPart ) { |
| list.add(child); |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Method build_graph. This method will build the proxy graph that the |
| * layout is based on. |
| * |
| * @param g |
| * DirectedGraph structure that will be populated with Nodes and |
| * Edges in this method. |
| * @param selectedObjects |
| * List of editparts that the Nodes and Edges will be calculated |
| * from. |
| */ |
| protected void build_graph(DirectedGraph g, List selectedObjects) { |
| Hashtable editPartToNodeDict = new Hashtable(500); |
| this.minX = -1; |
| this.minY = -1; |
| NodeList nodes = build_nodes(selectedObjects, editPartToNodeDict,null); |
| |
| // append edges that should be added to the graph |
| ArrayList objects = new ArrayList(); |
| objects.addAll(selectedObjects); |
| objects.addAll(getRelevantConnections(editPartToNodeDict)); |
| EdgeList edges = build_edges(objects, editPartToNodeDict); |
| g.nodes = nodes; |
| g.edges = edges; |
| postProcessGraph(g,editPartToNodeDict); |
| //printGraph(g); |
| } |
| |
| protected void postProcessGraph(DirectedGraph g, Hashtable editPartToNodeDict) { |
| //default do nothing |
| } |
| |
| /** |
| * reverse Utility function to reverse the order of points in a list. |
| * |
| * @param c |
| * PointList that is passed to the routine. |
| * @param rc |
| * PointList that is reversed. |
| */ |
| private void reverse(PointList c, PointList rc) { |
| rc.removeAllPoints(); |
| |
| for (int i = c.size() - 1; i >= 0; i--) { |
| rc.addPoint(c.getPoint(i)); |
| } |
| } |
| |
| /** |
| * Computes the command that will route the given connection editpart with the given points. |
| */ |
| Command routeThrough(Edge edge, ConnectionEditPart connectEP, Node source, Node target, PointList points, int diffX, int diffY) { |
| |
| if (connectEP == null) |
| return null; |
| |
| PointList routePoints = points; |
| if (source.data != connectEP.getSource()) { |
| routePoints = new PointList(points.size()); |
| reverse(points, routePoints); |
| Node tmpNode = source; |
| source = target; |
| target = tmpNode; |
| } |
| |
| PointList allPoints = new PointList(routePoints.size()); |
| for (int i = 0; i < routePoints.size(); i++) { |
| allPoints.addPoint(routePoints.getPoint(i).x + diffX, routePoints |
| .getPoint(i).y |
| + diffY); |
| } |
| |
| Rectangle sourceExt = getNodeMetrics(source); |
| Point ptSourceReference = sourceExt.getCenter().getTranslated(diffX, |
| diffY); |
| Rectangle targetExt = getNodeMetrics(target); |
| Point ptTargetReference = targetExt.getCenter().getTranslated(diffX, |
| diffY); |
| |
| SetAllBendpointRequest request = new SetAllBendpointRequest( |
| RequestConstants.REQ_SET_ALL_BENDPOINT, allPoints, |
| ptSourceReference, ptTargetReference); |
| |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| Command cmd = connectEP.getCommand(request); |
| if (cmd != null) |
| cc.add(cmd); |
| |
| // set the snapback position for all children owned by the connection |
| List affectingChildren = getAffectingChildren(connectEP); |
| Request snapBackRequest = new Request(RequestConstants.REQ_SNAP_BACK); |
| ListIterator li = affectingChildren.listIterator(); |
| while (li.hasNext()) { |
| EditPart ep = (EditPart)li.next(); |
| cmd = ep.getCommand(snapBackRequest); |
| if (cmd != null) |
| cc.add(cmd); |
| } |
| |
| return cc; |
| } |
| |
| /** |
| * Method update_diagram. Once the layout has been calculated with the GEF |
| * graph structure, the new layout values need to be propogated into the |
| * diagram. This is accomplished by creating a compound command that |
| * contains sub commands to change shapes positions and connection bendpoints |
| * positions. The command is subsequently executed by the calling action and |
| * then through the command infrastructure is undoable and redoable. |
| * |
| * @param diagramEP |
| * IGraphicalEditPart container that is target for the commands. |
| * @param g |
| * DirectedGraph structure that contains the results of the |
| * layout operation. |
| * @param isLayoutForSelected |
| * boolean indicating that the layout is to be performed on |
| * selected objects only. At this stage this is relevant only to |
| * calculate the offset in the diagram where the layout will |
| * occur. |
| * @return Command usually a command command that will set the locations of |
| * nodes and bendpoints for connections. |
| */ |
| protected Command update_diagram(GraphicalEditPart diagramEP, DirectedGraph g, |
| boolean isLayoutForSelected) { |
| |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| |
| 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; |
| } |
| |
| /* |
| * Find all of the arcs and set their intermediate points. This |
| * loop does not set the icon positions yet, because that causes |
| * recalculation of the arc connection points. The intermediate |
| * points of both outgoing and incomping arcs must be set before |
| * recalculating connection points. |
| */ |
| protected Command createEdgesChangeBoundsCommands(DirectedGraph g, Point diff) { |
| |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| PointList points = new PointList(10); |
| |
| ListIterator vi = g.edges.listIterator(); |
| while (vi.hasNext()) { |
| Edge edge = (Edge) vi.next(); |
| |
| if (edge.data == null || edge.getPoints()==null) |
| continue; |
| |
| points.removeAllPoints(); |
| |
| ConnectionEditPart cep = null; |
| Node source = null, target = null; |
| |
| collectPoints(points, edge); |
| cep = (ConnectionEditPart)edge.data; |
| source = edge.source; |
| target = edge.target; |
| |
| if (cep != null) { |
| PointListUtilities.normalizeSegments(points, MapModeUtil.getMapMode(cep.getFigure()).DPtoLP(3)); |
| |
| // Reset the points list |
| Command cmd = routeThrough(edge, cep, source, target, points, diff.x, diff.y); |
| if (cmd != null) |
| cc.add(cmd); |
| } |
| } |
| |
| if (cc.isEmpty()) |
| return null; |
| return cc; |
| } |
| |
| private void collectPoints(PointList points, Edge edge) { |
| PointList pointList = edge.getPoints(); |
| Rectangle start = translateFromGraph( |
| new Rectangle(pointList.getFirstPoint().x, |
| pointList.getFirstPoint().y, 0, 0)); |
| points.addPoint(start.getTopLeft()); |
| NodeList vnodes = edge.vNodes; |
| if (vnodes != null) { |
| for (int i = 0; i < vnodes.size(); i++) { |
| Node vn = vnodes.getNode(i); |
| Rectangle nodeExt = getNodeMetrics(vn); |
| int x = nodeExt.x; |
| int y = nodeExt.y; |
| |
| points.addPoint(x + nodeExt.width / 2, y + nodeExt.height / 2); |
| } |
| } |
| Rectangle end = translateFromGraph(new Rectangle(pointList.getLastPoint().x, |
| pointList.getLastPoint().y, 0, 0)); |
| points.addPoint(end.getTopLeft()); |
| } |
| |
| protected Command createNodeChangeBoundCommands(DirectedGraph g, Point diff) { |
| ListIterator vi = g.nodes.listIterator(); |
| CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ |
| createSubCommands(diff, vi, cc); |
| if (cc.isEmpty()) |
| return null; |
| return cc; |
| } |
| |
| protected void createSubCommands(Point diff, ListIterator vi, CompoundCommand cc) { |
| // Now set the position of the icons. This causes the |
| // arc connection points to be recalculated |
| while (vi.hasNext()) { |
| Node node = (Node) vi.next(); |
| if (node.data instanceof ShapeEditPart) { |
| IGraphicalEditPart gep = (IGraphicalEditPart)node.data; |
| |
| ChangeBoundsRequest request = new ChangeBoundsRequest( |
| RequestConstants.REQ_MOVE); |
| Rectangle nodeExt = getNodeMetrics(node); |
| Point ptLocation = new Point(nodeExt.x + diff.x, nodeExt.y |
| + diff.y); |
| |
| Point ptOldLocation = gep.getFigure().getBounds().getLocation(); |
| gep.getFigure().translateToAbsolute(ptOldLocation); |
| |
| gep.getFigure().translateToAbsolute(ptLocation); |
| Dimension delta = ptLocation.getDifference(ptOldLocation); |
| |
| request.setEditParts(gep); |
| request.setMoveDelta(new Point(delta.width, delta.height)); |
| request.setLocation(ptLocation); |
| |
| Command cmd = gep.getCommand(request); |
| if (cmd != null && cmd.canExecute()) |
| cc.add(cmd); |
| } |
| } |
| } |
| |
| 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 = getNodeMetrics(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); |
| } |
| |
| /** |
| * Creates the graph that will be used by the layouy provider |
| * Clients can override this method create different kind of graphs |
| * This method is called by {@link DefaultProvider#layoutEditParts(GraphicalEditPart, IAdaptable) } |
| * and {@link DefaultProvider#layoutEditParts(List, IAdaptable)} |
| * @return the Graph that will be used by the layout algorithm |
| */ |
| protected DirectedGraph createGraph(){ |
| return new DirectedGraph(); |
| } |
| |
| /** |
| * Creates the graph layout algorithm that will be used to layout the diagram |
| * This method is called by {@link DefaultProvider#layoutEditParts(GraphicalEditPart, IAdaptable) } |
| * and {@link DefaultProvider#layoutEditParts(List, IAdaptable)} |
| * @return the graph layout |
| */ |
| protected DirectedGraphLayout createGraphLayout() { |
| return new DirectedGraphLayout(); |
| } |
| |
| /** |
| * Indicates if the provider will consider the connections between ListItems |
| * while doing the arrange action |
| * @return true or false |
| */ |
| protected boolean shouldHandleConnectableListItems() { |
| return false; |
| } |
| |
| /* private void printGraph(DirectedGraph g){ |
| int depth = 0; |
| if (g instanceof CompoundDirectedGraph){ |
| NodeList subGraphs = ((CompoundDirectedGraph)g).nodes; |
| for (Iterator iter = subGraphs.iterator(); iter.hasNext();) { |
| Node node = (Node)iter.next(); |
| if (node.getParent()!=null) |
| continue; |
| if (node instanceof Subgraph){ |
| printSubGraph((Subgraph)node,depth); |
| }else { |
| printNode(node,depth); |
| } |
| } |
| } |
| } |
| |
| private void printNode(Node node, int depth) { |
| StringBuffer buffer = new StringBuffer(); |
| for (int i =0 ; i<depth ; i++) |
| buffer.append("\t"); |
| buffer.append("Node"); |
| System.out.println(buffer); |
| } |
| |
| private void printSubGraph(Subgraph subgraph, int depth) { |
| StringBuffer buffer = new StringBuffer(); |
| for (int i =0 ; i<depth ; i++) |
| buffer.append("\t"); |
| buffer.append("SubGraph"); |
| if (!subgraph.members.isEmpty()){ |
| buffer.append(" : "); |
| System.out.println(buffer); |
| NodeList nodes = subgraph.members; |
| depth++; |
| for (Iterator iter = nodes.iterator(); iter.hasNext();) { |
| Node element = (Node) iter.next(); |
| if (element instanceof Subgraph){ |
| printSubGraph((Subgraph)element,depth); |
| }else { |
| printNode(element,depth); |
| } |
| } |
| }else { |
| System.out.println(buffer); |
| } |
| |
| }*/ |
| } |