/******************************************************************************* * Copyright (c) 2005 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.draw2d.examples.tree; import java.util.List; import org.eclipse.draw2d.AbstractLayout; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.draw2d.geometry.Transposer; /** * Performs a layout on a container containing {@link AbstractBranch} figures. This layout is * similar to FlowLayout, except that the children are squeezed together to overlap by * comparing their left and right contours. * @author hudsonr * Created on Apr 18, 2003 */ public class TreeLayout extends AbstractLayout { private int pointOfContact; /** * @see org.eclipse.draw2d.AbstractLayout#calculatePreferredSize(org.eclipse.draw2d.IFigure, int, int) */ protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) { container.validate(); List children = container.getChildren(); Rectangle result = new Rectangle().setLocation(container.getClientArea().getLocation()); for (int i = 0; i < children.size(); i++) result.union(((IFigure)children.get(i)).getBounds()); result.resize(container.getInsets().getWidth(), container.getInsets().getHeight()); return result.getSize(); } private int[] calculateNewRightContour(int old[], int add[], int shift) { if (old == null) return add; // if (shift < 0) // shift = 0; int result[] = new int[Math.max(old.length, add.length)]; System.arraycopy(add, 0, result, 0, add.length); for (int i = add.length; i < result.length; i++) result[i] = old[i] + shift; return result; } private int calculateOverlap(int leftSubtree[], int rightSubtree[]) { pointOfContact = 0; if (leftSubtree == null) return 0; int min = Math.min(leftSubtree.length, rightSubtree.length); int result = Integer.MAX_VALUE; for (int i=0; i<min; i++) { int current = leftSubtree[i] + rightSubtree[i]; if (i > 0) current -= 5; if (current < result) { result = current; pointOfContact = i + 1; } } return result; } /** * @see org.eclipse.draw2d.LayoutManager#layout(org.eclipse.draw2d.IFigure) */ public void layout(IFigure container) { Animation.recordInitialState(container); if (Animation.playbackState(container)) return; TreeRoot root = ((TreeBranch)container.getParent()).getRoot(); Transposer transposer = root.getTransposer(); int gap = root.getMinorSpacing(); List subtrees = container.getChildren(); TreeBranch subtree; int previousSubtreeDepth = 0; int rightContour[] = null; int leftContour[]; int contactDepth; Point reference = transposer.t(container.getBounds().getLocation()); Point currentXY = reference.getCopy(); for (int i = 0; i < subtrees.size(); i++) { subtree = (TreeBranch)subtrees.get(i); //Give the subtree its preferred size before asking for contours Dimension subtreeSize = subtree.getPreferredSize(); subtree.setSize(subtreeSize); subtreeSize = transposer.t(subtreeSize); leftContour = subtree.getContourLeft(); int overlap = calculateOverlap(rightContour, leftContour); if (!subtree.getRoot().isCompressed()) overlap = 0; contactDepth = pointOfContact; subtree.setLocation(transposer.t(currentXY.getTranslated(-overlap, 0))); //Setup value for next sibling int advance = gap + subtreeSize.width - overlap; rightContour = calculateNewRightContour( rightContour, subtree.getContourRight(), advance); currentXY.x += advance; /* * In some cases, the current child may extend beyond the left edge of the * container because of the way it overlaps with the previous child. When this * happens, shift all children right. */ int shiftRight = reference.x - transposer.t(subtree.getBounds()).x; if (shiftRight > 0) { currentXY.x += shiftRight; Point correction = transposer.t(new Point(shiftRight, 0)); for (int j=0; j<=i; j++) ((IFigure)subtrees.get(j)).translate(correction.x, correction.y); } /* * In some cases, the current child "i" only touches the contour of a distant * sibling "i-n", where n>1. This means that there is extra space that can be * distributed among the intermediate siblings */ if (contactDepth > previousSubtreeDepth) { TreeBranch branch = (TreeBranch)subtrees.get(i-1); int slack = transposer.t(subtree.getBounds()).x - transposer.t(branch.getBounds()).right() - gap + calculateOverlap(branch.getContourRight(), subtree.getContourLeft()); int end = i; int begin = end - 1; while (begin > 0 && ((TreeBranch)subtrees.get(begin)).getDepth() < contactDepth) begin--; for (int j = begin + 1; j < end; j++) { branch = (TreeBranch)subtrees.get(j); Point shift = transposer.t(new Point(slack * (j - begin) / (end - begin), 0)); branch.translate(shift.x, shift.y); } } previousSubtreeDepth = subtree.getDepth(); } } }