/*******************************************************************************
* Copyright (c) 2008 itemis AG (http://www.itemis.eu) 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
*******************************************************************************/
package de.itemis.gmf.runtime.layout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.LayoutManager;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gmf.runtime.draw2d.ui.figures.ConstrainedToolbarLayout;
/**
* A layout that centers a figure's children inside scaled inner bounds. The
* scale factor can be given as CustomLayoutAttribute sclaeFactor. Use sqrt(2)
* for ellipses and 2 for diamond figures.
*
* @author koehnlein
*/
public class ScaleInnerFigureLayout extends ConstrainedToolbarLayout implements LayoutManager {
private double scaleFactor = Math.sqrt(2);
private int majorAlignment;
private boolean vertical;
private boolean stretchMinor;
public ScaleInnerFigureLayout() {
//changed for visualizers
setVertical(vertical);
setMajorAlignment(ALIGN_CENTER);
setMinorAlignment(ALIGN_CENTER);
setStretchMajorAxis(false);
//changed for visualizers
setStretchMinorAxis(true);
}
public void setScaleFactor(double scaleFactor) {
this.scaleFactor = scaleFactor;
}
public void setMajorAlignment(int majorAlignment) {
this.majorAlignment = majorAlignment;
}
public void setVertical(boolean vertical)
{
this.vertical = vertical;
}
public void setStretchMinor(boolean stretchMinor)
{
this.stretchMinor = stretchMinor;
}
@Override
public Dimension calculateMinimumSize(IFigure container, int hint, int hint2) {
final Dimension minimumSize = super.calculateMinimumSize(container,
hint, hint2);
minimumSize.scale(scaleFactor).expand(1, 1);
return minimumSize;
}
@Override
protected Dimension calculatePreferredSize(IFigure container, int hint,
int hint2) {
final Dimension preferredSize = super.calculatePreferredSize(container,
hint, hint2);
preferredSize.scale(scaleFactor).expand(1, 1);
return preferredSize;
}
@SuppressWarnings("unchecked")
@Override
public void layout(IFigure parent) {
if (!parent.isVisible())
return;
List children = getChildren(parent);
int numChildren = children.size();
Rectangle clientArea = transposer.t(parent.getClientArea());
int offsetX = (int) ((clientArea.width * (1. - 1. / scaleFactor)) / 2.);
int offsetY = (int) ((clientArea.height * (1. - 1. / scaleFactor)) / 2.);
int x = clientArea.x;
int y = clientArea.y + offsetY;
int availableWidth = (int) (clientArea.width / scaleFactor) + 1;
int availableHeight = (int) (clientArea.height / scaleFactor) + 1;
Dimension prefSizes[] = new Dimension[numChildren];
Dimension minSizes[] = new Dimension[numChildren];
Dimension maxSizes[] = new Dimension[numChildren];
// These hints will be passed to the children of the parent
// figure when getting their preferred size.
int wHint = (isStretchWidth()) ? parent
.getClientArea(Rectangle.SINGLETON).width : -1;
int hHint = (isStretchHeight()) ? parent
.getClientArea(Rectangle.SINGLETON).height : -1;
/*
* Calculate sum of preferred heights of all children(totalHeight).
* Calculate sum of minimum heights of all children(minHeight). Cache
* Preferred Sizes and Minimum Sizes of all children.
*
* totalHeight is the sum of the preferred heights of all children
* totalMinHeight is the sum of the minimum heights of all children
* prefMinSumHeight is the sum of the difference between all children's
* preferred heights and minimum heights. (This is used as a ratio to
* calculate how much each child will shrink).
*/
IFigure child;
int totalHeight = 0;
int totalMinHeight = 0;
double totalMaxHeight = 0;
int prefMinSumHeight = 0;
double prefMaxSumHeight = 0;
for (int i = 0; i < numChildren; i++) {
child = (IFigure) children.get(i);
prefSizes[i] = transposer.t(child.getPreferredSize(wHint, hHint));
minSizes[i] = transposer.t(child.getMinimumSize(wHint, hHint));
maxSizes[i] = transposer.t(child.getMaximumSize());
if (getConstraint(child) != null) {
double ratio = ((Double) getConstraint(child)).doubleValue();
int prefHeight = (int) (ratio * availableHeight);
prefHeight = Math.max(prefHeight, minSizes[i].height);
prefHeight = Math.min(prefHeight, maxSizes[i].height);
prefSizes[i].height = prefHeight;
}
totalHeight += prefSizes[i].height;
totalMinHeight += minSizes[i].height;
totalMaxHeight += maxSizes[i].height;
}
totalHeight += (numChildren - 1) * spacing;
totalMinHeight += (numChildren - 1) * spacing;
totalMaxHeight += (numChildren - 1) * spacing;
prefMinSumHeight = totalHeight - totalMinHeight;
prefMaxSumHeight = totalMaxHeight - totalHeight;
/*
* The total amount that the children must be shrunk is the sum of the
* preferred Heights of the children minus Max(the available area and
* the sum of the minimum heights of the children).
*
* amntShrinkHeight is the combined amount that the children must shrink
* amntShrinkCurrentHeight is the amount each child will shrink
* respectively
*/
int amntShrinkHeight = totalHeight
- Math.max(availableHeight, totalMinHeight);
if (!isStretchHeight()) {
int adjust = Math.max(availableHeight - totalHeight, 0);
switch (getVerticalAlignment()) {
case ALIGN_TOPLEFT:
break;
case ALIGN_BOTTOMRIGHT:
y += adjust;
break;
case ALIGN_CENTER:
default:
y += adjust / 2;
}
amntShrinkHeight += adjust;
}
for (int i = 0; i < numChildren; i++) {
int amntShrinkCurrentHeight = 0;
int prefHeight = prefSizes[i].height;
int minHeight = minSizes[i].height;
int maxHeight = maxSizes[i].height;
int prefWidth = prefSizes[i].width;
int maxWidth = maxSizes[i].width;
Rectangle newBounds = new Rectangle(x, y, prefWidth, prefHeight);
child = (IFigure) children.get(i);
if (isStretchHeight()) {
if (amntShrinkHeight > 0 && prefMinSumHeight != 0)
amntShrinkCurrentHeight = (prefHeight - minHeight)
* amntShrinkHeight / (prefMinSumHeight);
else if (amntShrinkHeight < 0 && totalHeight != 0)
amntShrinkCurrentHeight = (int) (((maxHeight - prefHeight) / prefMaxSumHeight) * amntShrinkHeight);
}
int width = Math.min(prefWidth, maxWidth);
if (isStretchWidth())
width = maxWidth;
width = Math.min(availableWidth, width);
newBounds.width = width;
int adjust = availableWidth - width;
switch (getHorizontalAlignment()) {
case ALIGN_TOPLEFT:
adjust = 0;
break;
case ALIGN_CENTER:
adjust /= 2;
break;
case ALIGN_BOTTOMRIGHT:
break;
}
newBounds.x += adjust + offsetX;
if (newBounds.height - amntShrinkCurrentHeight > maxHeight)
amntShrinkCurrentHeight = newBounds.height - maxHeight;
newBounds.height -= amntShrinkCurrentHeight;
child.setBounds(transposer.t(newBounds));
amntShrinkHeight -= amntShrinkCurrentHeight;
prefMinSumHeight -= (prefHeight - minHeight);
prefMaxSumHeight -= (maxHeight - prefHeight);
totalHeight -= prefHeight;
y += newBounds.height + spacing;
}
}
/**
* Gets the list of children after applying the layout options of ignore
* invisible children & reverse children
*/
@SuppressWarnings("unchecked")
private List getChildren(IFigure container) {
List children = new ArrayList(container.getChildren());
if (getIgnoreInvisibleChildren()) {
Iterator iter = children.iterator();
while (iter.hasNext()) {
IFigure f = (IFigure) iter.next();
if (!f.isVisible())
iter.remove();
}
}
if (isReversed())
Collections.reverse(children);
return children;
}
protected boolean isStretchHeight() {
return (isHorizontal() && getStretchMinorAxis())
|| getStretchMajorAxis();
}
protected boolean isStretchWidth() {
return (isHorizontal() && getStretchMajorAxis())
|| getStretchMinorAxis();
}
protected int getHorizontalAlignment() {
return isHorizontal() ? majorAlignment : minorAlignment;
}
protected int getVerticalAlignment() {
return isHorizontal() ? minorAlignment : majorAlignment;
}
}