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