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