blob: 42e93759567bfb62e0c03eceecbbbcc36ec6f8ab [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 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
* dinko.ivanov@sap.com - patch #70790
*******************************************************************************/
package org.eclipse.ui.forms.widgets;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
/**
* This layout manager arranges children of the composite parent in vertical
* columns. All the columns are identical size and children are stretched
* horizontally to fill the column width. The goal is to give layout some
* reasonable range of column numbers to allow it to handle various parent
* widths. That way, column number will drop to the lowest number in the range
* when width decreases, and grow up to the highest number in the range when
* allowed by the parent width.
* <p>
* In addition, the layout attempts to 'fill the space' equally i.e. to avoid
* large gaps at the and of the last column.
* <p>
* Child controls are layed out according to their 'natural' (preferred) size.
* For 'stretchy' controls that do not have natural preferred size, it is
* possible to set width and/or height hints using ColumnLayoutData objects.
*
* @see ColumnLayoutData
* @since 3.0
*/
public final class ColumnLayout extends Layout implements ILayoutExtension {
/**
* Minimum number of columns (default is 1).
*/
public int minNumColumns = 1;
/**
* Maximum number of columns (default is 3).
*/
public int maxNumColumns = 3;
/**
* Horizontal spacing between columns (default is 5).
*/
public int horizontalSpacing = 5;
/**
* Vertical spacing between controls (default is 5).
*/
public int verticalSpacing = 5;
/**
* Top margin (default is 5).
*/
public int topMargin = 5;
/**
* Left margin (default is 5).
*/
public int leftMargin = 5;
/**
* Bottom margin (default is 5).
*/
public int bottomMargin = 5;
/**
* Right margin (default is 5).
*/
public int rightMargin = 5;
/**
* Creates a new instance of the column layout.
*/
public ColumnLayout() {
}
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
if (wHint == 0)
return computeSize(composite, wHint, hHint, minNumColumns);
else if (wHint == SWT.DEFAULT)
return computeSize(composite, wHint, hHint, maxNumColumns);
else
return computeSize(composite, wHint, hHint, -1);
}
private Point computeSize(Composite parent, int wHint, int hHint, int ncolumns) {
Control[] children = parent.getChildren();
int cwidth = 0;
int cheight = 0;
Point[] sizes = new Point[children.length];
int cwHint = SWT.DEFAULT;
if (ncolumns != -1) {
cwHint = wHint - leftMargin - rightMargin - (ncolumns - 1) * horizontalSpacing;
if (cwHint <= 0)
cwHint = 0;
else
cwHint /= ncolumns;
}
for (int i = 0; i < children.length; i++) {
sizes[i] = computeControlSize(children[i], cwHint);
cwidth = Math.max(cwidth, sizes[i].x);
cheight += sizes[i].y;
}
if (ncolumns == -1) {
// must compute
ncolumns = (wHint - leftMargin - rightMargin - horizontalSpacing) / (cwidth + horizontalSpacing);
ncolumns = Math.min(ncolumns, children.length);
ncolumns = Math.max(ncolumns, minNumColumns);
ncolumns = Math.min(ncolumns, maxNumColumns);
}
int perColHeight = cheight / ncolumns;
if (cheight % ncolumns != 0)
perColHeight++;
int colHeight = 0;
int[] heights = new int[ncolumns];
int ncol = 0;
boolean fillIn = false;
for (int i = 0; i < sizes.length; i++) {
int childHeight = sizes[i].y;
if (i>0 && colHeight + childHeight > perColHeight) {
heights[ncol] = colHeight;
ncol++;
if (ncol == ncolumns || fillIn) {
// overflow - start filling in
fillIn = true;
ncol = findShortestColumn(heights);
}
colHeight = heights[ncol];
}
if (colHeight > 0)
colHeight += verticalSpacing;
colHeight += childHeight;
}
heights[ncol] = Math.max(heights[ncol],colHeight);
Point size = new Point(0, 0);
for (int i = 0; i < ncolumns; i++) {
size.y = Math.max(size.y, heights[i]);
}
size.x = cwidth * ncolumns + (ncolumns - 1) * horizontalSpacing;
size.x += leftMargin + rightMargin;
//System.out.println("ColumnLayout: whint="+wHint+", size.x="+size.x);
size.y += topMargin + bottomMargin;
return size;
}
private Point computeControlSize(Control c, int wHint) {
ColumnLayoutData cd = (ColumnLayoutData) c.getLayoutData();
int widthHint = cd != null ? cd.widthHint : wHint;
int heightHint = cd != null ? cd.heightHint : SWT.DEFAULT;
return c.computeSize(widthHint, heightHint);
}
private int findShortestColumn(int[] heights) {
int result = 0;
int height = Integer.MAX_VALUE;
for (int i = 0; i < heights.length; i++) {
if (height > heights[i]) {
height = heights[i];
result = i;
}
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
* boolean)
*/
protected void layout(Composite parent, boolean flushCache) {
Control[] children = parent.getChildren();
Rectangle carea = parent.getClientArea();
int cwidth = 0;
int cheight = 0;
Point[] sizes = new Point[children.length];
for (int i = 0; i < children.length; i++) {
sizes[i] = computeControlSize(children[i], SWT.DEFAULT);
cwidth = Math.max(cwidth, sizes[i].x);
cheight += sizes[i].y;
}
int ncolumns = (carea.width - leftMargin - rightMargin - horizontalSpacing) / (cwidth + horizontalSpacing);
ncolumns = Math.min(ncolumns, children.length);
ncolumns = Math.max(ncolumns, minNumColumns);
ncolumns = Math.min(ncolumns, maxNumColumns);
int realWidth = (carea.width - leftMargin - rightMargin + horizontalSpacing) / ncolumns - horizontalSpacing;
// int childrenPerColumn = children.length / ncolumns;
// if (children.length % ncolumns != 0)
// childrenPerColumn++;
// int colWidth = 0;
int fillWidth = Math.max(cwidth, realWidth);
int perColHeight = cheight / ncolumns;
if (cheight % ncolumns != 0)
perColHeight++;
int colHeight = 0;
int[] heights = new int[ncolumns];
int ncol = 0;
int x = leftMargin;
boolean fillIn = false;
for (int i = 0; i < sizes.length; i++) {
Control child = children[i];
Point csize = sizes[i];
ColumnLayoutData cd = (ColumnLayoutData) child.getLayoutData();
int align = cd != null ? cd.horizontalAlignment : ColumnLayoutData.FILL;
int childWidth = align == ColumnLayoutData.FILL ? fillWidth : csize.x;
if (i>0 && colHeight + csize.y > perColHeight) {
heights[ncol] = colHeight;
if (fillIn || ncol == ncolumns-1) {
// overflow - start filling in
fillIn = true;
ncol = findShortestColumn(heights);
x = leftMargin + ncol * (fillWidth + horizontalSpacing);
}
else {
ncol++;
x += fillWidth + horizontalSpacing;
}
colHeight = heights[ncol];
}
if (colHeight > 0)
colHeight += verticalSpacing;
switch (align) {
case ColumnLayoutData.LEFT :
case ColumnLayoutData.FILL :
child.setBounds(x, topMargin+colHeight, childWidth, csize.y);
break;
case ColumnLayoutData.RIGHT :
child.setBounds(x + fillWidth - childWidth, topMargin+colHeight, childWidth, csize.y);
break;
case ColumnLayoutData.CENTER :
child.setBounds(x + fillWidth / 2 - childWidth / 2, topMargin+colHeight, childWidth, csize.y);
break;
}
colHeight += csize.y;
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.forms.widgets.ILayoutExtension#computeMaximumWidth(org.eclipse.swt.widgets.Composite,
* boolean)
*/
public int computeMaximumWidth(Composite parent, boolean changed) {
return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.forms.widgets.ILayoutExtension#computeMinimumWidth(org.eclipse.swt.widgets.Composite,
* boolean)
*/
public int computeMinimumWidth(Composite parent, boolean changed) {
return computeSize(parent, 0, SWT.DEFAULT, changed).x;
}
}