blob: fbe1cbd8828a34899eae1ebbd83cb7a6526af85a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.swt.layout;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.*;
/**
* Instances of this class determine the size and position of the children of a
* <code>Composite</code> by placing them either in horizontal rows or
* vertical columns within the parent <code>Composite</code>.
* <p>
* <code>RowLayout</code> aligns all controls in one row if the
* <code>type</code> is set to horizontal, and one column if it is set to
* vertical. It has the ability to wrap, and provides configurable margins and
* spacing. <code>RowLayout</code> has a number of configuration fields. In
* addition, the height and width of each control in a <code>RowLayout</code>
* can be specified by setting a <code>RowData</code> object into the control
* using <code>setLayoutData ()</code>.
* </p>
* <p>
* The following example code creates a <code>RowLayout</code>, sets all of
* its fields to non-default values, and then sets it into a <code>Shell</code>.
*
* <pre>
* RowLayout rowLayout = new RowLayout();
* rowLayout.wrap = false;
* rowLayout.pack = false;
* rowLayout.justify = true;
* rowLayout.type = SWT.VERTICAL;
* rowLayout.marginLeft = 5;
* rowLayout.marginTop = 5;
* rowLayout.marginRight = 5;
* rowLayout.marginBottom = 5;
* rowLayout.spacing = 0;
* shell.setLayout( rowLayout );
* </pre>
*
* If you are using the default field values, you only need one line of code:
*
* <pre>
* shell.setLayout( new RowLayout() );
* </pre>
*
* </p>
*
* @see RowData
*/
public final class RowLayout extends Layout {
/**
* type specifies whether the layout places controls in rows or columns. The
* default value is HORIZONTAL. Possible values are:
* <ul>
* <li>HORIZONTAL: Position the controls horizontally from left to right</li>
* <li>VERTICAL: Position the controls vertically from top to bottom</li>
* </ul>
*
* @since 2.0
*/
public int type = SWT.HORIZONTAL;
/**
* marginWidth specifies the number of pixels of horizontal margin that will
* be placed along the left and right edges of the layout. The default value
* is 0.
*
* @since 3.0
*/
public int marginWidth = 0;
/**
* marginHeight specifies the number of pixels of vertical margin that will be
* placed along the top and bottom edges of the layout. The default value is
* 0.
*
* @since 3.0
*/
public int marginHeight = 0;
/**
* spacing specifies the number of pixels between the edge of one cell and the
* edge of its neighbouring cell. The default value is 3.
*/
public int spacing = 3;
/**
* wrap specifies whether a control will be wrapped to the next row if there
* is insufficient space on the current row. The default value is true.
*/
public boolean wrap = true;
/**
* pack specifies whether all controls in the layout take their preferred
* size. If pack is false, all controls will have the same size which is the
* size required to accommodate the largest preferred height and the largest
* preferred width of all the controls in the layout. The default value is
* true.
*/
public boolean pack = true;
/**
* fill specifies whether the controls in a row should be all the same height
* for horizontal layouts, or the same width for vertical layouts. The default
* value is false.
*
* @since 3.0
*/
public boolean fill = false;
/**
* justify specifies whether the controls in a row should be fully justified,
* with any extra space placed between the controls. The default value is
* false.
*/
public boolean justify = false;
/**
* marginLeft specifies the number of pixels of horizontal margin that will be
* placed along the left edge of the layout. The default value is 3.
*/
public int marginLeft = 3;
/**
* marginTop specifies the number of pixels of vertical margin that will be
* placed along the top edge of the layout. The default value is 3.
*/
public int marginTop = 3;
/**
* marginRight specifies the number of pixels of horizontal margin that will
* be placed along the right edge of the layout. The default value is 3.
*/
public int marginRight = 3;
/**
* marginBottom specifies the number of pixels of vertical margin that will be
* placed along the bottom edge of the layout. The default value is 3.
*/
public int marginBottom = 3;
/**
* Constructs a new instance of this class.
*/
public RowLayout() {
}
/**
* Constructs a new instance of this class given the type.
*
* @param type the type of row layout
* @since 2.0
*/
public RowLayout( int type ) {
this.type = type;
}
protected Point computeSize( Composite composite,
int wHint,
int hHint,
boolean flushCache )
{
Point extent;
if( type == SWT.HORIZONTAL ) {
extent = layoutHorizontal( composite, false, ( wHint != SWT.DEFAULT )
&& wrap, wHint, flushCache );
} else {
extent = layoutVertical( composite, false, ( hHint != SWT.DEFAULT )
&& wrap, hHint, flushCache );
}
if( wHint != SWT.DEFAULT )
extent.x = wHint;
if( hHint != SWT.DEFAULT )
extent.y = hHint;
return extent;
}
Point computeSize( Control control, boolean flushCache ) {
int wHint = SWT.DEFAULT, hHint = SWT.DEFAULT;
RowData data = ( RowData )control.getLayoutData();
if( data != null ) {
wHint = data.width;
hHint = data.height;
}
return control.computeSize( wHint, hHint, flushCache );
}
protected boolean flushCache( Control control ) {
return true;
}
String getName() {
String string = getClass().getName();
int index = string.lastIndexOf( '.' );
if( index == -1 )
return string;
return string.substring( index + 1, string.length() );
}
protected void layout( Composite composite, boolean flushCache ) {
Rectangle clientArea = composite.getClientArea();
if( type == SWT.HORIZONTAL ) {
layoutHorizontal( composite, true, wrap, clientArea.width, flushCache );
} else {
layoutVertical( composite, true, wrap, clientArea.height, flushCache );
}
}
Point layoutHorizontal( Composite composite,
boolean move,
boolean wrap,
int width,
boolean flushCache )
{
Control[] children = composite.getChildren();
int count = 0;
for( int i = 0; i < children.length; i++ ) {
Control control = children[ i ];
RowData data = ( RowData )control.getLayoutData();
if( data == null || !data.exclude ) {
children[ count++ ] = children[ i ];
}
}
if( count == 0 ) {
return new Point( marginLeft + marginWidth * 2 + marginRight,
marginTop + marginHeight * 2 + marginBottom );
}
int childWidth = 0, childHeight = 0, maxHeight = 0;
if( !pack ) {
for( int i = 0; i < count; i++ ) {
Control child = children[ i ];
Point size = computeSize( child, flushCache );
childWidth = Math.max( childWidth, size.x );
childHeight = Math.max( childHeight, size.y );
}
maxHeight = childHeight;
}
int clientX = 0, clientY = 0;
if( move ) {
Rectangle rect = composite.getClientArea();
clientX = rect.x;
clientY = rect.y;
}
int[] wraps = null;
boolean wrapped = false;
Rectangle[] bounds = null;
if( move && ( justify || fill ) ) {
bounds = new Rectangle[ count ];
wraps = new int[ count ];
}
int maxX = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight;
for( int i = 0; i < count; i++ ) {
Control child = children[ i ];
if( pack ) {
Point size = computeSize( child, flushCache );
childWidth = size.x;
childHeight = size.y;
}
if( wrap && ( i != 0 ) && ( x + childWidth > width ) ) {
wrapped = true;
if( move && ( justify || fill ) )
wraps[ i - 1 ] = maxHeight;
x = marginLeft + marginWidth;
y += spacing + maxHeight;
if( pack )
maxHeight = 0;
}
if( pack || fill ) {
maxHeight = Math.max( maxHeight, childHeight );
}
if( move ) {
int childX = x + clientX, childY = y + clientY;
if( justify || fill ) {
bounds[ i ] = new Rectangle( childX, childY, childWidth, childHeight );
} else {
child.setBounds( childX, childY, childWidth, childHeight );
}
}
x += spacing + childWidth;
maxX = Math.max( maxX, x );
}
maxX = Math.max( clientX + marginLeft + marginWidth, maxX - spacing );
if( !wrapped )
maxX += marginRight + marginWidth;
if( move && ( justify || fill ) ) {
int space = 0, margin = 0;
if( !wrapped ) {
space = Math.max( 0, ( width - maxX ) / ( count + 1 ) );
margin = Math.max( 0, ( ( width - maxX ) % ( count + 1 ) ) / 2 );
} else {
if( fill || justify ) {
int last = 0;
if( count > 0 )
wraps[ count - 1 ] = maxHeight;
for( int i = 0; i < count; i++ ) {
if( wraps[ i ] != 0 ) {
int wrapCount = i - last + 1;
if( justify ) {
int wrapX = 0;
for( int j = last; j <= i; j++ ) {
wrapX += bounds[ j ].width + spacing;
}
space = Math.max( 0, ( width - wrapX ) / ( wrapCount + 1 ) );
margin = Math.max( 0,
( ( width - wrapX ) % ( wrapCount + 1 ) ) / 2 );
}
for( int j = last; j <= i; j++ ) {
if( justify )
bounds[ j ].x += ( space * ( j - last + 1 ) ) + margin;
if( fill )
bounds[ j ].height = wraps[ i ];
}
last = i + 1;
}
}
}
}
for( int i = 0; i < count; i++ ) {
if( !wrapped ) {
if( justify )
bounds[ i ].x += ( space * ( i + 1 ) ) + margin;
if( fill )
bounds[ i ].height = maxHeight;
}
children[ i ].setBounds( bounds[ i ] );
}
}
return new Point( maxX, y + maxHeight + marginBottom + marginHeight );
}
Point layoutVertical( Composite composite,
boolean move,
boolean wrap,
int height,
boolean flushCache )
{
Control[] children = composite.getChildren();
int count = 0;
for( int i = 0; i < children.length; i++ ) {
Control control = children[ i ];
RowData data = ( RowData )control.getLayoutData();
if( data == null || !data.exclude ) {
children[ count++ ] = children[ i ];
}
}
if( count == 0 ) {
return new Point( marginLeft + marginWidth * 2 + marginRight,
marginTop + marginHeight * 2 + marginBottom );
}
int childWidth = 0, childHeight = 0, maxWidth = 0;
if( !pack ) {
for( int i = 0; i < count; i++ ) {
Control child = children[ i ];
Point size = computeSize( child, flushCache );
childWidth = Math.max( childWidth, size.x );
childHeight = Math.max( childHeight, size.y );
}
maxWidth = childWidth;
}
int clientX = 0, clientY = 0;
if( move ) {
Rectangle rect = composite.getClientArea();
clientX = rect.x;
clientY = rect.y;
}
int[] wraps = null;
boolean wrapped = false;
Rectangle[] bounds = null;
if( move && ( justify || fill ) ) {
bounds = new Rectangle[ count ];
wraps = new int[ count ];
}
int maxY = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight;
for( int i = 0; i < count; i++ ) {
Control child = children[ i ];
if( pack ) {
Point size = computeSize( child, flushCache );
childWidth = size.x;
childHeight = size.y;
}
if( wrap && ( i != 0 ) && ( y + childHeight > height ) ) {
wrapped = true;
if( move && ( justify || fill ) )
wraps[ i - 1 ] = maxWidth;
x += spacing + maxWidth;
y = marginTop + marginHeight;
if( pack )
maxWidth = 0;
}
if( pack || fill ) {
maxWidth = Math.max( maxWidth, childWidth );
}
if( move ) {
int childX = x + clientX, childY = y + clientY;
if( justify || fill ) {
bounds[ i ] = new Rectangle( childX, childY, childWidth, childHeight );
} else {
child.setBounds( childX, childY, childWidth, childHeight );
}
}
y += spacing + childHeight;
maxY = Math.max( maxY, y );
}
maxY = Math.max( clientY + marginTop + marginHeight, maxY - spacing );
if( !wrapped )
maxY += marginBottom + marginHeight;
if( move && ( justify || fill ) ) {
int space = 0, margin = 0;
if( !wrapped ) {
space = Math.max( 0, ( height - maxY ) / ( count + 1 ) );
margin = Math.max( 0, ( ( height - maxY ) % ( count + 1 ) ) / 2 );
} else {
if( fill || justify ) {
int last = 0;
if( count > 0 )
wraps[ count - 1 ] = maxWidth;
for( int i = 0; i < count; i++ ) {
if( wraps[ i ] != 0 ) {
int wrapCount = i - last + 1;
if( justify ) {
int wrapY = 0;
for( int j = last; j <= i; j++ ) {
wrapY += bounds[ j ].height + spacing;
}
space = Math.max( 0, ( height - wrapY ) / ( wrapCount + 1 ) );
margin = Math.max( 0,
( ( height - wrapY ) % ( wrapCount + 1 ) ) / 2 );
}
for( int j = last; j <= i; j++ ) {
if( justify )
bounds[ j ].y += ( space * ( j - last + 1 ) ) + margin;
if( fill )
bounds[ j ].width = wraps[ i ];
}
last = i + 1;
}
}
}
}
for( int i = 0; i < count; i++ ) {
if( !wrapped ) {
if( justify )
bounds[ i ].y += ( space * ( i + 1 ) ) + margin;
if( fill )
bounds[ i ].width = maxWidth;
}
children[ i ].setBounds( bounds[ i ] );
}
}
return new Point( x + maxWidth + marginRight + marginWidth, maxY );
}
/**
* Returns a string containing a concise, human-readable description of the
* receiver.
*
* @return a string representation of the layout
*/
public String toString() {
String string = getName() + " {";
string += "type=" + ( ( type != SWT.HORIZONTAL )
? "SWT.VERTICAL"
: "SWT.HORIZONTAL" ) + " ";
if( marginWidth != 0 )
string += "marginWidth=" + marginWidth + " ";
if( marginHeight != 0 )
string += "marginHeight=" + marginHeight + " ";
if( marginLeft != 0 )
string += "marginLeft=" + marginLeft + " ";
if( marginTop != 0 )
string += "marginTop=" + marginTop + " ";
if( marginRight != 0 )
string += "marginRight=" + marginRight + " ";
if( marginBottom != 0 )
string += "marginBottom=" + marginBottom + " ";
if( spacing != 0 )
string += "spacing=" + spacing + " ";
string += "wrap=" + wrap + " ";
string += "pack=" + pack + " ";
string += "fill=" + fill + " ";
string += "justify=" + justify + " ";
string = string.trim();
string += "}";
return string;
}
}