/***************************************************************************** * Copyright (c) 2012 CEA LIST. * * * 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: * CEA LIST - Initial API and implementation * *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.common.draw2d; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.draw2d.AbstractHintLayout; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.draw2d.geometry.Rectangle; /** * All children are placed on top of each other. Gap may be set between * children. * * @author dstadnik */ public class PileLayout extends AbstractHintLayout { /** * Vertical gap between figures. */ public final int getGap() { return myGap; } public void setGap(int gap) { myGap = gap > 0 ? gap : 0; } public void setNegativeGap(int gap) { myGap = gap; } /** * The last (bottom) figure should be stretched to cover the rest of the * container client area. */ public final boolean isStretchBottom() { return myStretchBottom; } public void setStretchBottom(boolean stretchBottom) { myStretchBottom = stretchBottom; } public void remove(IFigure figure) { myConstraints.remove(figure); } public void setConstraint(IFigure figure, Object constraint) { if(constraint == null) { remove(figure); } else { myConstraints.put(figure, constraint); } } public Object getConstraint(IFigure figure) { return myConstraints.get(figure); } /** * Returns list constraint for specified figure. If constraint is null or * not list constraint returns FILL list constraint by default. */ protected PileConstraint getPileConstraint(IFigure figure) { Object constraint = getConstraint(figure); return constraint instanceof PileConstraint ? (PileConstraint)constraint : FILL; } protected Dimension calculateSize(IFigure container, int wHint, int hHint, SizeExtractor sizeExtractor) { int totalWidth = 0; // Width of all components int totalHeight = 0; // Height of all components List<?> children = container.getChildren(); for(int i = 0; i < children.size(); i++) { IFigure child = (IFigure)children.get(i); Dimension size = sizeExtractor.getSize(child, wHint, hHint); if(totalWidth < size.width) { totalWidth = size.width; } totalHeight += size.height + getGap(); } // Subtract extra gap totalHeight -= getGap(); // Add insets to preferred size (include border) Insets insets = container.getInsets(); totalWidth += insets.getWidth(); totalHeight += insets.getHeight(); return new Dimension(totalWidth, totalHeight); } protected Dimension calculateMinimumSize(IFigure container, int wHint, int hHint) { return calculateSize(container, wHint, hHint, MIN_SIZE_EXTRACTOR); } protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) { return calculateSize(container, wHint, hHint, PREF_SIZE_EXTRACTOR); } public void layout(IFigure container) { Rectangle clientArea = container.getClientArea(); int y = clientArea.y; final int maxY = clientArea.y + clientArea.height; List<?> children = container.getChildren(); for(int i = 0; i < children.size(); i++) { IFigure child = (IFigure)children.get(i); Dimension preferred = child.getPreferredSize(clientArea.width, clientArea.height); int height = preferred.height; if(y >= maxY) { Rectangle bounds = new Rectangle(clientArea.x, maxY, clientArea.width, 0); child.setBounds(bounds); continue; } else if(y + height > maxY || (isStretchBottom() && i == children.size() - 1)) { height = maxY - y; } Rectangle bounds = new Rectangle(clientArea.x, y, clientArea.width, height); getPileConstraint(child).setChildBounds(preferred, bounds); child.setBounds(bounds); y += bounds.height + getGap(); } } private int myGap; private Map<IFigure, Object> myConstraints = new HashMap<IFigure, Object>(2); private boolean myStretchBottom; private static final SizeExtractor MIN_SIZE_EXTRACTOR = new SizeExtractor() { public Dimension getSize(IFigure figure, int wHint, int hHint) { return figure.getMinimumSize(wHint, hHint); } }; private static final SizeExtractor PREF_SIZE_EXTRACTOR = new SizeExtractor() { public Dimension getSize(IFigure figure, int wHint, int hHint) { return figure.getPreferredSize(wHint, hHint); } }; private interface SizeExtractor { public Dimension getSize(IFigure figure, int wHint, int hHint); } /** * Constraint that is recognizable by this layout. */ public interface PileConstraint { /** * Changes bounds of child with specified preferred size. New bounds * should fit in specified bounds. * * @param preferred * Prefferred size of a child. * @param bounds * Max child bounds. */ public void setChildBounds(Dimension preferred, Rectangle bounds); } /** * Default constraint. */ public static final PileConstraint FILL = new PileConstraint() { public void setChildBounds(Dimension preferred, Rectangle bounds) { } }; public static final PileConstraint ALIGN_LEFT = new PileConstraint() { public void setChildBounds(Dimension preferred, Rectangle bounds) { bounds.width = Math.min(preferred.width, bounds.width); } }; public static final PileConstraint ALIGN_CENTER = new PileConstraint() { public void setChildBounds(Dimension preferred, Rectangle bounds) { if(bounds.width > preferred.width) { final int offset = (bounds.width - preferred.width) / 2; bounds.x += offset; bounds.width = preferred.width; } } }; public static final PileConstraint ALIGN_RIGHT = new PileConstraint() { public void setChildBounds(Dimension preferred, Rectangle bounds) { if(bounds.width > preferred.width) { bounds.x += bounds.width - preferred.width; bounds.width = preferred.width; } } }; }