import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.draw2d.AbstractLayout;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
/**
* @author Klaus Wenger
*/
public final class GridLayout extends AbstractLayout {
/**
* Constructs a new instance of this layout manager.
*/
public GridLayout() {
super();
}
/**
* Constructs a new instance of this class given the number of columns, and
* whether or not the columns should be forced to have the same width.
*
* @param numColumns The number of columns in the grid.
* @param makeColumnsEqualWidth Whether or not the columns will have equal width.
*
* @since 2.0
*/
public GridLayout(int numColumns, boolean makeColumnsEqualWidth) {
this.numColumns = numColumns;
this.makeColumnsEqualWidth = makeColumnsEqualWidth;
}
/**
* Sets the layout constraint of the given figure. The constraints can only be of type
* {@link Rectangle}.
*
* @see org.eclipse.draw2d.LayoutManager#setConstraint(org.eclipse.draw2d.IFigure, java.lang.Object)
* @since 2.0
*/
public void setConstraint(IFigure figure, Object newConstraint) {
super.setConstraint(figure, newConstraint);
if (newConstraint != null) {
this.constraints.put(figure, newConstraint);
}
}
/**
* @see org.eclipse.draw2d.LayoutManager#getConstraint(org.eclipse.draw2d.IFigure)
*/
public Object getConstraint(IFigure figure) {
return (GridData) this.constraints.get(figure);
}
/**
* @see org.eclipse.draw2d.AbstractLayout#calculatePreferredSize(org.eclipse.draw2d.IFigure, int, int)
*/
protected Dimension calculatePreferredSize(IFigure containerFigure,
int wHint, int hHint) {
return calculatePreferredSize(containerFigure, wHint, hHint, false);
}
/**
* @param containerFigure
* @param wHint
* @param hHint
* @param flushCache
* @return The calculated Dimension
.
*/
protected Dimension calculatePreferredSize(
IFigure containerFigure, int wHint, int hHint, boolean flushCache) {
Dimension size = layout(
containerFigure, false, 0, 0, wHint, hHint, flushCache);
if (wHint != SWT.DEFAULT) {
size.width = wHint;
}
if (hHint != SWT.DEFAULT) {
size.height = hHint;
}
return size;
}
/**
* @param control
* @return true
in any case.
*/
protected boolean flushCache(Figure control) {
Object constraint = getConstraint(control);
if (constraint != null) {
((GridData) constraint).flushCache();
}
return true;
}
/**
* @param grid
* @param row
* @param column
* @param rowCount
* @param columnCount
* @param first
* @return The GridData
instance if available or null
.
*/
/* package */ GridData getData(IFigure[][] grid, int row, int column,
int rowCount, int columnCount, boolean first) {
IFigure figure = grid[row][column];
if (figure != null) {
GridData gridData = (GridData) getConstraint(figure);
int hSpan = Math.max(1,
Math.min(gridData.horizontalSpan, columnCount));
int vSpan = Math.max(1, gridData.verticalSpan);
int i = first ? row + vSpan - 1 : row - vSpan + 1;
int j = first ? column + hSpan - 1 : column - hSpan + 1;
if (0 <= i && i < rowCount) {
if (0 <= j && j < columnCount) {
if (figure == grid[i][j]) return gridData;
}
}
}
return null;
}
/**
* @see org.eclipse.draw2d.LayoutManager#layout(org.eclipse.draw2d.IFigure)
*/
public void layout(IFigure container) {
layout(container, false);
}
/**
* @param containerFigure
* @param flushCache
*/
protected void layout(IFigure containerFigure, boolean flushCache) {
Rectangle clientArea = containerFigure.getClientArea();
layout(containerFigure, true,
clientArea.x, clientArea.y, clientArea.width, clientArea.height,
flushCache);
}
/**
* @param containerFigure
* @param move
* @param x
* @param y
* @param width
* @param height
* @param flushCache
* @return The Dimension
the layout filled.
*/
/* package */ Dimension layout(IFigure containerFigure, boolean move,
int x, int y, int width, int height, boolean flushCache) {
if (this.numColumns < 1) {
return new Dimension(
this.marginLeft + this.marginWidth * 2 + this.marginRight,
this.marginTop + this.marginHeight * 2 + this.marginBottom);
}
List childList = containerFigure.getChildren();
for (int i = 0; i < childList.size(); i++) {
IFigure child = (IFigure) childList.get(i);
GridData gridData = (GridData) getConstraint(child);
if (gridData == null) {
setConstraint(child, gridData = new GridData());
}
if (flushCache) {
gridData.flushCache();
}
gridData.getPreferredSize(child,
gridData.widthHint, gridData.heightHint, flushCache);
if (gridData.grabExcessHorizontalSpace && gridData.minimumWidth > 0) {
if (gridData.cacheWidth < gridData.minimumWidth) {
int trim = 0;
//TEMPORARY CODE
// if (child instanceof Scrollable) {
// Rectangle rect = ((Scrollable) child).computeTrim(0, 0, 0, 0);
// trim = rect.width;
// } else {
// trim = child.getBorderWidth() * 2;
// }
gridData.cacheWidth = gridData.cacheHeight = SWT.DEFAULT;
gridData.getPreferredSize(child, Math.max(0,
gridData.minimumWidth - trim), gridData.heightHint, false);
}
}
if (gridData.grabExcessVerticalSpace && gridData.minimumHeight > 0) {
gridData.cacheHeight = Math.max(gridData.cacheHeight,
gridData.minimumHeight);
}
}
/* Build the grid */
int row = 0, column = 0, rowCount = 0, columnCount = this.numColumns;
IFigure[][] grid = new IFigure[4][columnCount];
for (int i = 0; i < childList.size(); i++) {
IFigure child = (IFigure) childList.get(i);
GridData gridData = (GridData) getConstraint(child);
int hSpan = Math.max(1, Math.min(gridData.horizontalSpan, columnCount));
int vSpan = Math.max(1, gridData.verticalSpan);
while (true) {
int lastRow = row + vSpan;
if (lastRow >= grid.length) {
IFigure[][] newGrid = new IFigure[lastRow + 4][columnCount];
System.arraycopy(grid, 0, newGrid, 0, grid.length);
grid = newGrid;
}
if (grid[row] == null) {
grid[row] = new IFigure[columnCount];
}
while (column < columnCount && grid[row][column] != null) {
column++;
}
int endCount = column + hSpan;
if (endCount <= columnCount) {
int index = column;
while (index < endCount && grid[row][index] == null) {
index++;
}
if (index == endCount) {
break;
}
column = index;
}
if (column + hSpan >= columnCount) {
column = 0;
row++;
}
}
for (int j = 0; j < vSpan; j++) {
if (grid[row + j] == null) {
grid[row + j] = new IFigure[columnCount];
}
for (int k = 0; k < hSpan; k++) {
grid[row + j][column + k] = child;
}
}
rowCount = Math.max(rowCount, row + vSpan);
column += hSpan;
}
/* Column widths */
int availableWidth = width - this.horizontalSpacing * (columnCount - 1) -
(this.marginLeft + this.marginWidth * 2 + this.marginRight);
int expandCount = 0;
int[] widths = new int[columnCount];
int[] minWidths = new int[columnCount];
boolean[] expandColumn = new boolean[columnCount];
for (int j = 0; j < columnCount; j++) {
for (int i = 0; i < rowCount; i++) {
GridData gridData =
getData(grid, i, j, rowCount, columnCount, true);
if (gridData != null) {
int hSpan = Math.max(1, Math.min(
gridData.horizontalSpan, columnCount));
if (hSpan == 1) {
int w = gridData.cacheWidth + gridData.horizontalIndent;
widths[j] = Math.max(widths[j], w);
if (gridData.grabExcessHorizontalSpace) {
if (!expandColumn[j]) {
expandCount++;
}
expandColumn[j] = true;
}
if (!gridData.grabExcessHorizontalSpace
|| gridData.minimumWidth != 0) {
w = !gridData.grabExcessHorizontalSpace
|| gridData.minimumWidth == SWT.DEFAULT
? gridData.cacheWidth : gridData.minimumWidth;
w += gridData.horizontalIndent;
minWidths[j] = Math.max(minWidths[j], w);
}
}
}
}
for (int i = 0; i < rowCount; i++) {
GridData gridData =
getData(grid, i, j, rowCount, columnCount, false);
if (gridData != null) {
int hSpan = Math.max(1, Math.min(
gridData.horizontalSpan, columnCount));
if (hSpan > 1) {
int spanWidth = 0, spanMinWidth = 0, spanExpandCount = 0;
for (int k = 0; k < hSpan; k++) {
spanWidth += widths[j-k];
spanMinWidth += minWidths[j-k];
if (expandColumn[j-k]) {
spanExpandCount++;
}
}
if (gridData.grabExcessHorizontalSpace
&& spanExpandCount == 0) {
expandCount++;
expandColumn[j] = true;
}
int w = gridData.cacheWidth + gridData.horizontalIndent -
spanWidth -(hSpan - 1) * this.horizontalSpacing;
if (w > 0) {
if (spanExpandCount == 0) {
widths[j] += w;
} else {
int delta = w / spanExpandCount;
int remainder = w % spanExpandCount, last = -1;
for (int k = 0; k < hSpan; k++) {
if (expandColumn[j-k]) {
widths[last=j-k] += delta;
}
}
if (last > -1) widths[last] += remainder;
}
}
if (!gridData.grabExcessHorizontalSpace
|| gridData.minimumWidth != 0) {
w = !gridData.grabExcessHorizontalSpace
|| gridData.minimumWidth == SWT.DEFAULT
? gridData.cacheWidth : gridData.minimumWidth;
w += gridData.horizontalIndent - spanMinWidth -
(hSpan - 1) * this.horizontalSpacing;
if (w > 0) {
if (spanExpandCount == 0) {
minWidths[j] += w;
} else {
int delta = w / spanExpandCount;
int remainder = w % spanExpandCount, last = -1;
for (int k = 0; k < hSpan; k++) {
if (expandColumn[j-k]) {
minWidths[last=j-k] += delta;
}
}
if (last > -1) minWidths[last] += remainder;
}
}
}
}
}
}
}
if (this.makeColumnsEqualWidth) {
int minColumnWidth = 0;
int columnWidth = 0;
for (int i = 0; i < columnCount; i++) {
minColumnWidth = Math.max(minColumnWidth, minWidths[i]);
columnWidth = Math.max(columnWidth, widths[i]);
}
columnWidth = width == SWT.DEFAULT || expandCount == 0 ? columnWidth
: Math.max(minColumnWidth, availableWidth / columnCount);
for (int i = 0; i < columnCount; i++) {
expandColumn[i] = expandCount > 0;
widths[i] = columnWidth;
}
} else {
if (width != SWT.DEFAULT && expandCount > 0) {
int totalWidth = 0;
for (int i = 0; i < columnCount; i++) {
totalWidth += widths[i];
}
int count = expandCount;
int delta = (availableWidth - totalWidth) / count;
int remainder = (availableWidth - totalWidth) % count;
int last = -1;
while (totalWidth != availableWidth) {
for (int j = 0; j < columnCount; j++) {
if (expandColumn[j]) {
if (widths[j] + delta > minWidths[j]) {
widths[last = j] = widths[j] + delta;
} else {
widths[j] = minWidths[j];
expandColumn[j] = false;
count--;
}
}
}
if (last > -1) widths[last] += remainder;
for (int j = 0; j < columnCount; j++) {
for (int i = 0; i < rowCount; i++) {
GridData data =
getData(grid, i, j, rowCount, columnCount, false);
if (data != null) {
int hSpan = Math.max(1, Math.min(
data.horizontalSpan, columnCount));
if (hSpan > 1) {
if (!data.grabExcessHorizontalSpace
|| data.minimumWidth != 0) {
int spanWidth = 0, spanExpandCount = 0;
for (int k=0; k 0) {
if (spanExpandCount == 0) {
widths[j] += w;
} else {
int delta2 = w / spanExpandCount;
int remainder2 =
w % spanExpandCount, last2 = -1;
for (int k = 0; k < hSpan; k++) {
if (expandColumn[j-k]) {
widths[last2=j-k] += delta2;
}
}
if (last2 > -1) {
widths[last2] += remainder2;
}
}
}
}
}
}
}
}
if (count == 0) {
break;
}
totalWidth = 0;
for (int i = 0; i < columnCount; i++) {
totalWidth += widths[i];
}
delta = (availableWidth - totalWidth) / count;
remainder = (availableWidth - totalWidth) % count;
last = -1;
}
}
}
/* Wrapping */
GridData[] flush = null;
int flushLength = 0;
if (width != SWT.DEFAULT) {
for (int j = 0; j < columnCount; j++) {
for (int i = 0; i < rowCount; i++) {
GridData data =
getData(grid, i, j, rowCount, columnCount, false);
if (data != null) {
if (data.heightHint == SWT.DEFAULT) {
IFigure child = grid[i][j];
//TEMPORARY CODE
int hSpan = Math.max(1, Math.min(
data.horizontalSpan, columnCount));
int currentWidth = 0;
for (int k = 0; k < hSpan; k++) {
currentWidth += widths[j-k];
}
currentWidth += (hSpan - 1) * this.horizontalSpacing -
data.horizontalIndent;
if ((currentWidth != data.cacheWidth
&& data.horizontalAlignment == SWT.FILL)
||(data.cacheWidth > currentWidth)) {
int trim = 0;
// if (child instanceof Scrollable) {
// Rectangle rect = ((Scrollable) child).computeTrim(0, 0, 0, 0);
// trim = rect.width;
// } else {
// trim = child.getBorderWidth() * 2;
// }
data.cacheWidth = data.cacheHeight = SWT.DEFAULT;
data.getPreferredSize(child,
Math.max(0, currentWidth - trim),
data.heightHint, false);
if (data.grabExcessVerticalSpace
&& data.minimumHeight > 0) {
data.cacheHeight = Math.max(
data.cacheHeight, data.minimumHeight);
}
if (flush == null) {
flush = new GridData[childList.size()];
}
flush[flushLength++] = data;
}
}
}
}
}
}
/* Row heights */
int availableHeight = height - this.verticalSpacing * (rowCount - 1) -
(this.marginTop + this.marginHeight * 2 + this.marginBottom);
expandCount = 0;
int[] heights = new int[rowCount];
int[] minHeights = new int[rowCount];
boolean[] expandRow = new boolean[rowCount];
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columnCount; j++) {
GridData data = getData(grid, i, j, rowCount, columnCount, true);
if (data != null) {
int vSpan = Math.max(1, Math.min(data.verticalSpan, rowCount));
if (vSpan == 1) {
int h = data.cacheHeight + data.verticalIndent;
heights[i] = Math.max(heights[i], h);
if (data.grabExcessVerticalSpace) {
if (!expandRow[i]) {
expandCount++;
}
expandRow[i] = true;
}
if (!data.grabExcessVerticalSpace
|| data.minimumHeight != 0) {
h = !data.grabExcessVerticalSpace
|| data.minimumHeight == SWT.DEFAULT
? data.cacheHeight : data.minimumHeight;
h += data.verticalIndent;
minHeights[i] = Math.max(minHeights[i], h);
}
}
}
}
for (int j = 0; j < columnCount; j++) {
GridData data = getData(grid, i, j, rowCount, columnCount, false);
if (data != null) {
int vSpan = Math.max(1, Math.min(data.verticalSpan, rowCount));
if (vSpan > 1) {
int spanHeight = 0, spanMinHeight = 0, spanExpandCount = 0;
for (int k = 0; k < vSpan; k++) {
spanHeight += heights[i-k];
spanMinHeight += minHeights[i-k];
if (expandRow[i-k]) {
spanExpandCount++;
}
}
if (data.grabExcessVerticalSpace && spanExpandCount == 0) {
expandCount++;
expandRow[i] = true;
}
int h = data.cacheHeight + data.verticalIndent -
spanHeight - (vSpan - 1) * this.verticalSpacing;
if (h > 0) {
if (spanExpandCount == 0) {
heights[i] += h;
} else {
int delta = h / spanExpandCount;
int remainder = h % spanExpandCount, last = -1;
for (int k = 0; k < vSpan; k++) {
if (expandRow[i-k]) {
heights[last=i-k] += delta;
}
}
if (last > -1) {
heights[last] += remainder;
}
}
}
if (!data.grabExcessVerticalSpace
|| data.minimumHeight != 0) {
h = !data.grabExcessVerticalSpace
|| data.minimumHeight == SWT.DEFAULT
? data.cacheHeight : data.minimumHeight;
h += data.verticalIndent - spanMinHeight - (vSpan - 1) *
this.verticalSpacing;
if (h > 0) {
if (spanExpandCount == 0) {
minHeights[i] += h;
} else {
int delta = h / spanExpandCount;
int remainder = h % spanExpandCount, last = -1;
for (int k = 0; k < vSpan; k++) {
if (expandRow[i-k]) {
minHeights[last=i-k] += delta;
}
}
if (last > -1) {
minHeights[last] += remainder;
}
}
}
}
}
}
}
}
if (height != SWT.DEFAULT && expandCount > 0) {
int totalHeight = 0;
for (int i = 0; i < rowCount; i++) {
totalHeight += heights[i];
}
int count = expandCount;
int delta = (availableHeight - totalHeight) / count;
int remainder = (availableHeight - totalHeight) % count;
int last = -1;
while (totalHeight != availableHeight) {
for (int i = 0; i < rowCount; i++) {
if (expandRow[i]) {
if (heights[i] + delta > minHeights[i]) {
heights[last = i] = heights[i] + delta;
} else {
heights[i] = minHeights[i];
expandRow[i] = false;
count--;
}
}
}
if (last > -1) {
heights[last] += remainder;
}
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columnCount; j++) {
GridData gridData =
getData(grid, i, j, rowCount, columnCount, false);
if (gridData != null) {
int vSpan = Math.max(1, Math.min(
gridData.verticalSpan, rowCount));
if (vSpan > 1) {
if (!gridData.grabExcessVerticalSpace
|| gridData.minimumHeight != 0) {
int spanHeight = 0, spanExpandCount = 0;
for (int k = 0; k < vSpan; k++) {
spanHeight += heights[i-k];
if (expandRow[i-k]) {
spanExpandCount++;
}
}
int h = !gridData.grabExcessVerticalSpace
|| gridData.minimumHeight == SWT.DEFAULT
? gridData.cacheHeight
: gridData.minimumHeight;
h += gridData.verticalIndent - spanHeight -
(vSpan - 1) * this.verticalSpacing;
if (h > 0) {
if (spanExpandCount == 0) {
heights[i] += h;
} else {
int delta2 = h / spanExpandCount;
int remainder2 = h % spanExpandCount,
last2 = -1;
for (int k = 0; k < vSpan; k++) {
if (expandRow[i-k]) {
heights[last2=i-k] += delta2;
}
}
if (last2 > -1) {
heights[last2] += remainder2;
}
}
}
}
}
}
}
}
if (count == 0) break;
totalHeight = 0;
for (int i = 0; i < rowCount; i++) {
totalHeight += heights[i];
}
delta = (availableHeight - totalHeight) / count;
remainder = (availableHeight - totalHeight) % count;
last = -1;
}
}
/* Position the controls */
if (move) {
int gridY = y + this.marginTop + this.marginHeight;
for (int i = 0; i < rowCount; i++) {
int gridX = x + this.marginLeft + this.marginWidth;
for (int j=0; j