/**
* 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;
}
}