/** * Copyright (c) 2012 committers of YAKINDU 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: * Markus Muehlbrandt - initial API and implementation * */ package org.yakindu.base.gmf.runtime.treelayout; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.eclipse.draw2d.Connection; import org.eclipse.draw2d.ConnectionLayer; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.gef.LayerConstants; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.notation.View; /** * * @author Markus Muehlbrandt * */ public class TreeLayoutUtil { public static final String TREE_LAYOUT_ANNOTATION = "TreeLayoutAnnotation"; public static final String TREE_NODE_POSITION = "TreeNodePosition"; public static final String TREE_NODE_PARENT_URI = "TreeNodeParentURI"; /** * Util for TreeLayouting */ private TreeLayoutUtil() { // not intended to be instantiated. } public static List<IGraphicalEditPart> getSiblings(IGraphicalEditPart child) { final View parentView = TreeLayoutUtil.getTreeNodeParentView(child .getNotationView()); if (parentView != null) { final IGraphicalEditPart parentEditPart = (IGraphicalEditPart) child .findEditPart(child.getParent(), parentView.getElement()); if (parentEditPart != null) { final List<IGraphicalEditPart> treeChildren = getOrderedTreeChildren(parentEditPart); if (treeChildren.remove(child)) { return treeChildren; } } } return Collections.emptyList(); } public static ArrayList<IGraphicalEditPart> getOrderedTreeChildren( IGraphicalEditPart parentTreeNodeEditPart) { final List<IGraphicalEditPart> children = new ArrayList<IGraphicalEditPart>(); final DiagramEditPart diagramEditPart = UIUtils.getDiagramEditPart(); if (diagramEditPart != null) { final ConnectionLayer connectionLayer = (ConnectionLayer) diagramEditPart .getLayer(LayerConstants.CONNECTION_LAYER); for (final Connection connection : getTreeFigureIncomingConnections( connectionLayer, parentTreeNodeEditPart.getFigure())) { final Object object = parentTreeNodeEditPart.getParent() .getViewer().getVisualPartMap() .get(connection.getSourceAnchor().getOwner()); if (object instanceof IGraphicalEditPart) { children.add((IGraphicalEditPart) object); } } } final IGraphicalEditPart[] orderedChildren = new IGraphicalEditPart[children .size()]; final List<IGraphicalEditPart> unorderedChildren = new ArrayList<IGraphicalEditPart>(); for (final IGraphicalEditPart editPart : children) { final int treePosition = getTreeNodePosition(editPart); if (treePosition == -1 || treePosition >= orderedChildren.length || orderedChildren[treePosition] != null) { unorderedChildren.add(editPart); } else { orderedChildren[treePosition] = editPart; } } for (final IGraphicalEditPart editPart : unorderedChildren) { orderedChildren[getNextEmptyIndex(orderedChildren)] = editPart; } return new ArrayList<IGraphicalEditPart>(Arrays.asList(orderedChildren)); } public static int getNextEmptyIndex(IGraphicalEditPart[] list) { for (int index = 0; index < list.length; index++) { if (list[index] == null) { return index; } } return -1; } public static int getConstrainedTreeNodePosition(IFigure figure) { final Object constraint = figure.getParent().getLayoutManager() .getConstraint(figure); if (constraint instanceof TreeLayoutConstraint) { return ((TreeLayoutConstraint) constraint).getTreeInnerRankIndex(); } return -1; } /** * Returns the parent view of a tree node from the parent view annotation. * * @param editPart * @return */ public static View getTreeNodeParentView(View view) { final EAnnotation xmiIdAnnotation = view .getEAnnotation(TREE_LAYOUT_ANNOTATION); if (xmiIdAnnotation != null) { return (View) view.eResource().getEObject( xmiIdAnnotation.getDetails().get(TREE_NODE_PARENT_URI)); } return null; } /** * Returns the index of a tree node from the view position annotation. * * @param editPart * @return */ public static int getTreeNodePosition(IGraphicalEditPart editPart) { final EAnnotation xmiIdAnnotation = editPart.getNotationView() .getEAnnotation(TREE_LAYOUT_ANNOTATION); if (xmiIdAnnotation != null) { final String pos = xmiIdAnnotation.getDetails().get( TREE_NODE_POSITION); if (pos != null) { return Integer.parseInt(pos); } } return getConstrainedTreeNodePosition(editPart.getFigure()); } /** * Returns a list of {@link View} elements of the given edit parts. * * @param editParts * @return */ public static List<View> getViews(List<IGraphicalEditPart> editParts) { final List<View> viewList = new ArrayList<View>(); for (final IGraphicalEditPart editPart : editParts) { viewList.add(editPart.getNotationView()); } return viewList; } /** * Calculates the new tree node list index based on the given location. * Currently works only with horizontal tree node alignment! * * @param location * @param siblings * @return */ public static int getNewTreeNodePosition(Point location, List<IGraphicalEditPart> siblings) { int position = -1; if (siblings.isEmpty()) { return 0; } if (location != null) { int minPosDelta = Integer.MAX_VALUE; int minNegDelta = Integer.MIN_VALUE; int upperIndex = siblings.size() - 1; int lowerIndex = 0; for (int i = 0; i < siblings.size(); i++) { // convert to absolute coordinates to consider zoom level. final IFigure figure = siblings.get(i).getFigure(); final Rectangle absoluteBounds = figure.getBounds().getCopy(); figure.translateToAbsolute(absoluteBounds); // TODO: Look for Layout settings. Maybe layout is vertical // aligned. final int deltaY = location.y - absoluteBounds.y; if (deltaY > 0 && deltaY < minPosDelta) { if (siblings.size() == 1) { lowerIndex = 1; upperIndex = 1; } else { minPosDelta = deltaY; lowerIndex = i; } } else if (deltaY < 0 && deltaY > minNegDelta) { if (siblings.size() == 1) { lowerIndex = 0; upperIndex = 0; } else { minNegDelta = deltaY; upperIndex = i; } } } if (lowerIndex == 0 && upperIndex == 0) { position = 0; } else if (lowerIndex < upperIndex || (siblings.size() == 1 && lowerIndex == 1 && upperIndex == 1)) { position = upperIndex; } else if (lowerIndex == siblings.size() - 1 && upperIndex == siblings.size() - 1) { position = upperIndex + 1; } } return position; } public static void setTreeNodesPositionAnnotation(List<View> viewElements) { if (viewElements != null) { for (int index = 0; index < viewElements.size(); index++) { final View view = viewElements.get(index); EAnnotation xmiIdAnnotation = view .getEAnnotation(TREE_LAYOUT_ANNOTATION); if (xmiIdAnnotation == null) { xmiIdAnnotation = EcoreFactory.eINSTANCE .createEAnnotation(); xmiIdAnnotation.setSource(TREE_LAYOUT_ANNOTATION); } xmiIdAnnotation.getDetails().put(TREE_NODE_POSITION, Integer.toString(index)); xmiIdAnnotation.setEModelElement(view); } } } public static void setTreeNodeParentAnnotation(View child, View parent) { EAnnotation xmiIdAnnotation = child .getEAnnotation(TREE_LAYOUT_ANNOTATION); if (xmiIdAnnotation == null) { xmiIdAnnotation = EcoreFactory.eINSTANCE.createEAnnotation(); xmiIdAnnotation.setSource(TREE_LAYOUT_ANNOTATION); } xmiIdAnnotation.getDetails().put(TREE_NODE_PARENT_URI, parent.eResource().getURIFragment(parent)); xmiIdAnnotation.setEModelElement(child); } // private static void setParentAnnotation(IFigure childFigure, // IFigure parentFigure, EditPartViewer viewer) { // final IGraphicalEditPart parentEditPart = (IGraphicalEditPart) viewer // .getVisualPartMap().get(parentFigure); // final IGraphicalEditPart childEditPart = (IGraphicalEditPart) viewer // .getVisualPartMap().get(childFigure); // try { // CommandUtil // .executeUndoableOperation(new SetTreeParentAnnotationCommand( // childEditPart.getNotationView(), parentEditPart // .getNotationView())); // } catch (final ExecutionException e) { // e.printStackTrace(); // } // } // // private static IFigure getAnnotatedParentFigure(IFigure figure, // EditPartViewer viewer) { // final IGraphicalEditPart editPart = (IGraphicalEditPart) viewer // .getVisualPartMap().get(figure); // final View view = editPart.getNotationView(); // // final EAnnotation xmiIdAnnotation = view // .getEAnnotation(TREE_LAYOUT_ANNOTATION); // // if (xmiIdAnnotation != null) { // final String parentURI = xmiIdAnnotation.getDetails().get( // TREE_NODE_PARENT_URI); // if (parentURI != null) { // final EObject object = view.eResource().getEObject(parentURI); // final IGraphicalEditPart parentEditPart = (IGraphicalEditPart) viewer // .getEditPartRegistry().get(object); // return parentEditPart != null ? parentEditPart.getFigure() // : null; // } // } // return null; // } /** * Returns only elements parent figure is a direct parent. Indirect * connections are filtered out. Use if children can have many parents. * * @param connectionLayer * @param parentFigure * @return */ public static List<Connection> getTreeFigureIncomingConnections( ConnectionLayer connectionLayer, IFigure parentFigure) { final List<Connection> connectionList = getIncomingConnections( connectionLayer, parentFigure); final List<Connection> indirectChildren = new ArrayList<Connection>(); final int parentFigureTreeLevel = getDeepestTreeLevel(connectionLayer, parentFigure); for (final Connection connection : connectionList) { final IFigure childFigure = connection.getSourceAnchor().getOwner(); final int childTreeLevel = getDeepestTreeLevel(connectionLayer, childFigure); if (childTreeLevel == parentFigureTreeLevel + 1) { //get LayoutConstraint of child Figure; Object object = parentFigure.getParent().getLayoutManager() .getConstraint(childFigure); if (object instanceof TreeLayoutConstraint) { TreeLayoutConstraint childConstraint = (TreeLayoutConstraint) object; final IFigure constrainedParentFig = childConstraint .getTreeParentFigure(); if (constrainedParentFig == null) { childConstraint.setTreeParentFigure(parentFigure); } else if (constrainedParentFig != parentFigure) { indirectChildren.add(connection); } } } else { indirectChildren.add(connection); } } connectionList.removeAll(indirectChildren); return connectionList; } private static int getDeepestTreeLevel(ConnectionLayer connectionLayer, IFigure figure) { final List<Connection> connectionList = getOutgoingConnections( connectionLayer, figure); if (connectionList.size() > 0) { final int[] ret = new int[connectionList.size()]; for (int i = 0; i < connectionList.size(); i++) { ret[i] = 1 + getDeepestTreeLevel(connectionLayer, connectionList.get(i).getTargetAnchor().getOwner()); } int maxLevel = ret[0]; if (ret.length > 1) { for (int i = 1; i < ret.length; i++) { if (ret[i] > maxLevel) { maxLevel = ret[i]; } } } return maxLevel; } return 0; } /** * Returns incoming connection figures of the figure parameter. * * @param connectionLayer * @param figure * @return */ public static List<Connection> getIncomingConnections( ConnectionLayer connectionLayer, IFigure figure) { final List<Connection> incomingConnectionList = new ArrayList<Connection>(); for (final Object object : connectionLayer.getChildren()) { if (object instanceof Connection) { final Connection connection = (Connection) object; if (connection.getTargetAnchor().getOwner() == figure) { incomingConnectionList.add(connection); } } } return incomingConnectionList; } /** * Returns outgoing connection figures of the figure parameter. * * @param connectionLayer * @param figure * @return */ public static List<Connection> getOutgoingConnections( ConnectionLayer connectionLayer, IFigure figure) { final List<Connection> outgoingConnectionList = new ArrayList<Connection>(); for (final Object object : connectionLayer.getChildren()) { if (object instanceof Connection) { final Connection connection = (Connection) object; if (connection.getSourceAnchor().getOwner() == figure) { outgoingConnectionList.add(connection); } } } return outgoingConnectionList; } }