blob: 7e7a5a31df8f7ba5b1af4635df16832a32c90211 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2012 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.bpel.ui.util;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;
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.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
/**
* class that implements a row/column layout used for BPEL Flows
*/
public class RowColumnLayout extends AbstractLayout {
protected Map<IFigure, Object> constraints = new HashMap<IFigure, Object>();
private static final int MIN_WIDTH_HEIGHT = 30;
private int totalHeight, totalWidth;
private Rectangle region;
private int[] rowHeights;
private int[] colWidths;
boolean spanned = false;
private int gridWidth, gridHeight;
private int[] widthOffsets;
private int[] heightOffsets;
private int rows = 0, cols = 0;
private boolean debugThis = false;
public int getNumberOfColumns() {
return cols;
}
public int getNumberOfRows() {
return rows;
}
public boolean isSpanned() {
return spanned;
}
public void setSpanned(boolean spanned) {
//TODO: setSpanned(true) not supported yet
this.spanned = spanned;
}
private Rectangle getChildRegion(IFigure f, int availableX, int availableY) {
if (spanned) {
Dimension sz = f.getPreferredSize();
int w = (int) Math.round((float) (sz.width) / this.gridWidth + 0.5);
int h = (int) Math.round((float) (sz.height) / this.gridHeight + 0.5);
return new Rectangle(availableX, availableY, w, h);
}
return new Rectangle(availableX, availableY, 1, 1);
}
public void characterizeGrid(IFigure f) {
initVars();
region = new Rectangle(0, 0, -1, -1);
int unresolved = 0;
ListIterator<IFigure> children = f.getChildren().listIterator();
while (children.hasNext()) {
IFigure child = children.next();
Rectangle r = (Rectangle) constraints.get(child);
if (r == null || r.width < 0 || r.height < 0) {
unresolved++;
if (debugThis) {
if (r != null) System.out.println("unresolved figure " + r + " " + child); //$NON-NLS-1$ //$NON-NLS-2$
else System.out.println("unresolved figure " + child); //$NON-NLS-1$
}
continue;
}
if (debugThis) System.out.println("resolved figure " + r + " " + child); //$NON-NLS-1$ //$NON-NLS-2$
if (region.height < 0)
region.setBounds(r);
else
region.union(r);
}
if (region.height >= 0) {
rows = region.getBottomRight().y;
cols = region.getBottomRight().x;
}
// we have to resolve the unresolved items by calculating a new row-column for it
if (unresolved > 0) {
children = f.getChildren().listIterator();
while (children.hasNext()) {
IFigure child = children.next();
Rectangle r = (Rectangle) constraints.get(child);
if (r == null || r.width < 0 || r.height < 0) {
if (debugThis) System.out.println("trying to resolve figure at " + child); //$NON-NLS-1$
r = getChildRegion(child, cols, 0);
constraints.put(child, r);
if (debugThis) System.out.println("new rect " + r); //$NON-NLS-1$
if (region.height < 0)
region.setBounds(r);
else
region.union(r);
cols = region.getBottomRight().x;
}
}
}
// update again
if (region.x >= 0) {
rows = region.getBottomRight().y;
cols = region.getBottomRight().x;
}
else {
return;
}
if (rows < 0)
return;
if (debugThis) {
System.out.println("bounds " + region); //$NON-NLS-1$
System.out.println("rows " + rows); //$NON-NLS-1$
System.out.println("cols " + cols); //$NON-NLS-1$
}
// allocate an array of row, col sizes;
rowHeights = new int[rows];
colWidths = new int[cols];
for (int i = 0; i < cols; i++) {
colWidths[i] = MIN_WIDTH_HEIGHT;
}
for (int i = 0; i < rows; i++) {
rowHeights[i] = MIN_WIDTH_HEIGHT;
}
if (!isSpanned()) {
children = f.getChildren().listIterator();
while (children.hasNext()) {
IFigure child = children.next();
Rectangle rowcol = (Rectangle) constraints.get(child);
if (debugThis) {
System.out.println("child " + child + " " + rowcol); //$NON-NLS-1$ //$NON-NLS-2$
}
Dimension sz = getChildSize(child, -1, -1);
if (rowcol.x >= cols || rowcol.y >= rows)
throw new IllegalArgumentException();
if (rowcol.x < 0 || rowcol.y < 0)
throw new IllegalArgumentException();
rowHeights[rowcol.y] = Math.max(rowHeights[rowcol.y], sz.height+10);
colWidths[rowcol.x] = Math.max(colWidths[rowcol.x], sz.width+10);
}
}
for (int i = 0; i < rows; i++)
totalHeight += rowHeights[i];
for (int i = 0; i < cols; i++)
totalWidth += colWidths[i];
// calculate the offsets of each cell in the coordinate space
widthOffsets = new int[cols];
heightOffsets = new int[rows];
for (int i = 1; i < cols; i++)
widthOffsets[i] = colWidths[i - 1] + widthOffsets[i - 1];
for (int i = 1; i < rows; i++)
heightOffsets[i] = rowHeights[i - 1] + heightOffsets[i - 1];
}
/**
* Provides the given child's preferred size
* @param child The Figure whose preferred size needs to be calculated
* @param wHint The width hint to be used when calculating the child's preferred size
* @param hHint The height hint to be used when calculating the child's preferred size
* @return the child's preferred size
*/
protected Dimension getChildSize(IFigure child, int wHint, int hHint) {
return child.getPreferredSize(wHint, hHint);
}
@Override
public Object getConstraint(IFigure figure) {
return constraints.get(figure);
}
public Point getOrigin(IFigure parent) {
return parent.getClientArea().getLocation();
}
private void initVars() {
totalHeight = 0;
totalWidth = 0;
rowHeights = null;
colWidths = null;
gridWidth = 40;
gridHeight = 40;
widthOffsets = null;
heightOffsets = null;
rows = 0;
cols = 0;
}
@Override
public void remove(IFigure figure) {
super.remove(figure);
constraints.remove(figure);
}
/**
* Sets the given bounds for the child figure input.
*
* @param parent Parent Figure which holds the child.
* @param child Child Figure whose bounds are to be set.
* @param bounds The size of the child to be set.
* @since 2.0
*/
protected void setBoundsOfChild(IFigure parent, IFigure child, Rectangle bounds) {
parent.getClientArea(Rectangle.SINGLETON);
bounds.translate(Rectangle.SINGLETON.x, Rectangle.SINGLETON.y);
child.setBounds(bounds);
}
/**
* Sets the layout constraint of the given figure.
*
* @param figure Figure for which the constarint is being set.
* @param newConstraint Constraint for the input figure.
* @see #getConstraint(IFigure)
* @since 2.0
*/
@Override
public void setConstraint(IFigure figure, Object newConstraint) {
super.setConstraint(figure, newConstraint);
if (newConstraint != null)
constraints.put(figure, newConstraint);
if (debugThis) {
if (newConstraint != null)
System.out.println("setConstraint("+ figure + " " + newConstraint + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
@Override
protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
// Subtract out the insets from the hints
characterizeGrid(container);
Dimension prefSize = new Dimension();
prefSize.height += totalHeight;
prefSize.width += totalWidth;
prefSize.width += container.getInsets().getWidth();
prefSize.height += container.getInsets().getHeight();
prefSize.union(getBorderPreferredSize(container));
return prefSize;
}
/**
* @see org.eclipse.draw2d.LayoutManager#layout(IFigure)
*/
public void layout(IFigure parent) {
characterizeGrid(parent);
if (rows == 0 || cols == 0)
return;
int row, col;
Point offset = getOrigin(parent);
Point centeringOffset = new Point();
IFigure f;
Iterator<IFigure> children;
children = parent.getChildren().iterator();
while (children.hasNext()) {
f = children.next();
Rectangle bounds = (Rectangle) getConstraint(f);
if (bounds == null)
continue;
if (debugThis) System.out.println("bounds " + " " + f + " " + bounds); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Rectangle newChildArea = new Rectangle();
row = bounds.y;
col = bounds.x;
newChildArea.x = widthOffsets[col];
newChildArea.y = heightOffsets[row];
if (isSpanned()) {
newChildArea.height = gridHeight * bounds.height;
newChildArea.width = gridWidth * bounds.width;
} else {
newChildArea.height = rowHeights[row];
newChildArea.width = colWidths[col];
}
centeringOffset.x = (newChildArea.width - f.getPreferredSize().width) / 2;
centeringOffset.y = (newChildArea.height - f.getPreferredSize().height) / 2;
newChildArea = newChildArea.getTranslated(offset);
newChildArea = newChildArea.getTranslated(centeringOffset);
newChildArea.width = f.getPreferredSize().width;
newChildArea.height = f.getPreferredSize().height;
f.setBounds(newChildArea);
}
}
/**
* @param pt - location to test
* @param gridInfo - returns the grid information from that point
* @param pixelRegionInfo - returns the pixel region of that grid
* @return true if the point pass the hittest.
*/
public boolean getGridInfoFromLocation(Point pt, Rectangle gridInfo, Rectangle pixRegion) {
Rectangle hitTest = new Rectangle();
for (int i = 0; i < cols; i++)
for (int k = 0; k < rows; k++) {
hitTest.x = widthOffsets[i];
hitTest.y = heightOffsets[k];
hitTest.width = colWidths[i];
hitTest.height = rowHeights[k];
if (hitTest.contains(pt)) {
gridInfo.x = i;
gridInfo.y = k;
gridInfo.setSize(1, 1);
pixRegion.setBounds(hitTest);
return true;
}
}
return false;
}
public boolean isDroppableAt(EditPart parentPart, EditPart test, Rectangle gridInfo) {
IFigure f = ((GraphicalEditPart)parentPart).getContentPane();
ListIterator<IFigure> children = f.getChildren().listIterator();
while (children.hasNext()) {
IFigure child = children.next();
Rectangle r = (Rectangle) constraints.get(child);
if (r.getLocation().equals(gridInfo.getLocation())) {
return false;
}
}
return true;
}
public Rectangle [] getDividers() {
if (rows <= 1 && cols <= 1)
return null;
Rectangle [] rects = new Rectangle[(rows-1)+(cols-1)];
int n = 0;
for (int i = 0; i < (cols-1); i++) {
rects[n++] = new Rectangle(new Point(widthOffsets[i+1], 0), new Dimension(0, totalHeight));
}
for (int i = 0; i < (rows-1); i++) {
rects[n++] = new Rectangle(new Point(0, heightOffsets[i+1]), new Dimension(totalWidth, 0));
}
return rects;
}
}