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