blob: 0e801a7131c9aad3e57529761a7a1db180003406 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 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.ui.internal.layout;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
/**
* <code>CellData</code> is the layout data object associated with
* <code>CellLayout</code>. You can attach a CellData object to a
* control by using the <code>setLayoutData</code> method. CellData
* objects are optional. If you do not attach any layout data to a control,
* it will behave just like attaching a CellData created using its default
* constructor.
*
* @since 3.0
**/
public final class CellData {
/**
* hintType flag (value = 0) indicating that the control's computeSize method should be used
* to determine the control size. If modifierType is set to NONE, then the widthHint
* and heightHint fields will be ignored.
*/
public final static int NONE = 0;
/**
* hintType flag (value = 1) indicating that the widthHint and heightHint should be used
* as the control's size instead of the result of computeSize
* <p>
* This flag is useful for list boxes, text boxes, tree controls, and other controls
* whose contents can change dynamically. For example, create a tree control and set
* its width and height hints to the default size for that control. This will cause
* the hints to be used instead of the preferred size of the tree control.</p>
*/
public final static int OVERRIDE = 1;
/**
* hintType(value = 2) indicating that the width of the control should be no less than
* widthHint (if provided) and the height of the control should be no less
* than heightHint (if provided).
* <p>
* This flag is useful for buttons. For example, set the width and height hints to
* the default button size. This will use the default button size unless the button
* label is too large to fit on the button.
* </p>
*/
public final static int MINIMUM = 2;
/**
* hintType flag (value = 3) indicating that the width of the control should be no more than
* widthHint (if provided) and the height of the control should be no more
* than heightHint (if provided).
* <p>
* This flag is useful for wrapping text. For example, set heightHint to SWT.DEFAULT
* and set widthHint to the desired number of pixels after which text should wrap. This
* will cause the text to wrap after the given number of pixels, but will not allocate
* extra space in the column if the text widget does not fill an entire line.
* </p>
*/
public final static int MAXIMUM = 3;
/**
* This flag controls how the width and height hints are to be treated. See the constants
* above.
*/
public int hintType = OVERRIDE;
/**
* Width hint. This modifies the width of the control, in pixels. If set to SWT.DEFAULT,
* this dimension will not be constrained. Depending on the value of modifierType,
* this may be a minimum size, a maximum size, or simply replace the preferred control
* size.
*/
public int widthHint = SWT.DEFAULT;
/**
* Height hint. This modifies the height of the control, in pixels. If set to SWT.DEFAULT,
* this dimension will not be constrained. Depending on the value of modifierType,
* this will be a minimum size, a maximum size, or a replacement for the control's preferred
* size.
*/
public int heightHint = SWT.DEFAULT;
/**
* Number of rows spanned by this cell (default = 1)
*/
public int verticalSpan = 1;
/**
* Number of columns spanned by this cell (default = 1)
*/
public int horizontalSpan = 1;
/**
* Horizontal alignment of the control within the cell. May be one
* of SWT.LEFT, SWT.RIGHT, SWT.CENTER, or SWT.NORMAL. SWT.NORMAL indicates
* that the control should be made as wide as the cell.
*/
public int horizontalAlignment = SWT.FILL;
/**
* Vertical alignment of the control within the cell. May be one of
* SWT.TOP, SWT.BOTTOM, SWT.CENTER, or SWT.NORMAL. SWT.NORMAL indicates
* that the control should be made as wide as the cell.
*/
public int verticalAlignment = SWT.FILL;
/**
* Horizontal indentation (pixels). Positive values move the control
* to the right, negative to the left.
*/
public int horizontalIndent = 0;
/**
* Vertical indentation (pixels). Positive values move the control
* down, negative values move the control up.
*/
public int verticalIndent = 0;
/**
* Constructs a CellData with default properties
*/
public CellData() {
// Use the default values for all fields.
}
/**
* Creates a new CellData that with properties that are as close as possible to
* the given GridData. This is used for converting GridLayouts into CellLayouts.
*
* @param data
*/
public CellData(GridData data) {
verticalSpan = data.verticalSpan;
horizontalSpan = data.horizontalSpan;
switch (data.horizontalAlignment) {
case GridData.BEGINNING:
horizontalAlignment = SWT.LEFT;
break;
case GridData.CENTER:
horizontalAlignment = SWT.CENTER;
break;
case GridData.END:
horizontalAlignment = SWT.RIGHT;
break;
case GridData.FILL:
horizontalAlignment = SWT.FILL;
break;
}
switch (data.verticalAlignment) {
case GridData.BEGINNING:
verticalAlignment = SWT.LEFT;
break;
case GridData.CENTER:
verticalAlignment = SWT.CENTER;
break;
case GridData.END:
verticalAlignment = SWT.RIGHT;
break;
case GridData.FILL:
verticalAlignment = SWT.FILL;
break;
}
widthHint = data.widthHint;
heightHint = data.heightHint;
horizontalIndent = data.horizontalIndent;
hintType = OVERRIDE;
}
/**
* Copies the given CellData
*
* @param newData
*/
public CellData(CellData newData) {
hintType = newData.hintType;
widthHint = newData.widthHint;
heightHint = newData.heightHint;
horizontalAlignment = newData.horizontalAlignment;
verticalAlignment = newData.verticalAlignment;
horizontalSpan = newData.horizontalSpan;
verticalSpan = newData.verticalSpan;
}
/**
* Sets the size hint for this control. This is used to modify the control's
* preferred size. If one dimension should remain unmodified, that hint can be
* set to SWT.DEFAULT. Using a size hint of CellData.MINIMUM ensures that the preferred
* control size is larger than the hint. Using a size hint of CellData.MAXIMUM ensures
* that the preferred size is smaller than the hint. Using a size hint of CellData.OVERRIDE
* ensures that the preferred size is always equal to the hint.
*
* @param hintType one of CellData.MINIMUM, CellData.MAXIMUM, or CellData.OVERRIDE
* @param hint size hint (in pixels). If either dimension is set to SWT.DEFAULT, the
* hint will not affect that dimension
* @return this
*/
public CellData setHint(int hintType, Point hint) {
return setHint(hintType, hint.x, hint.y);
}
/**
* Sets the size hint for this control. This is used to modify the control's
* preferred size. If one dimension should remain unmodified, that hint can be
* set to SWT.DEFAULT. Using a size hint of CellData.MINIMUM ensures that the preferred
* control size is larger than the hint. Using a size hint of CellData.MAXIMUM ensures
* that the preferred size is smaller than the hint. Using a size hint of CellData.OVERRIDE
* ensures that the preferred size is always equal to the hint. If both hints are equal
* to SWT.DEFAULT, then the control's preferred size is unmodified.
*
* @param hintType one of CellData.MINIMUM, CellData.MAXIMUM, or CellData.OVERRIDE
* @param horizontal horizontal hint (pixels). A value of SWT.DEFAULT will leave the result
* of the control's computeSize method unmodified.
* @param vertical vertical hint (pixels). A value of SWT.DEFAULT will leave the result of
* the control's computeSize method unmodified.
* @return this
*/
public CellData setHint(int hintType, int horizontal, int vertical) {
this.hintType = hintType;
this.heightHint = vertical;
this.widthHint = horizontal;
return this;
}
/**
* Sets the alignment for this control
*
* @param horizontalAlignment one of SWT.LEFT, SWT.RIGHT, SWT.FILL, or SWT.CENTER
* @param verticalAlignment one of SWT.TOP, SWT.BOTTOM, SWT.FILL, or SWT.CENTER
* @return this
*/
public CellData align(int horizontalAlignment, int verticalAlignment) {
this.horizontalAlignment = horizontalAlignment;
this.verticalAlignment = verticalAlignment;
return this;
}
/**
* Sets the number of rows and columns spanned by this control.
*
* @param horizontalSpan number of columns spanned by the control (> 0)
* @param verticalSpan number of rows spanned by the control (> 0)
* @return this
*/
public CellData span(int horizontalSpan, int verticalSpan) {
this.horizontalSpan = horizontalSpan;
this.verticalSpan = verticalSpan;
return this;
}
/**
* Sets the indentation for this control. The indentation is added to
* the control's position within the cell. For example, indentation of
* (10,4) will move the control right by 10 pixels and down by 4 pixels.
*
* @param indent indentation (pixels)
* @return this
*/
public CellData indent(Point indent) {
return this.indent(indent.x, indent.y);
}
/**
* Sets the indentation for this cell
*
* @param horizontalIndent distance (pixels) to move the control to the right
* @param verticalIndent distance (pixels) to move the control down
* @return this
*/
public CellData indent(int horizontalIndent, int verticalIndent) {
this.horizontalIndent = horizontalIndent;
this.verticalIndent = verticalIndent;
return this;
}
/**
* Returns the preferred size of the given control, given the known dimensions of
* its cell.
*
* @param toCompute the control whose size is to be computed
* @param cellWidth width of the cell, in pixels (or SWT.DEFAULT if unknown)
* @param cellHeight height of the cell, in pixels (or SWT.DEFAULT if unknown)
* @return the preferred size of the given control, in pixels
*/
public Point computeSize(SizeCache toCompute, int cellWidth, int cellHeight) {
int absHorizontalIndent = Math.abs(horizontalIndent);
int absVerticalIndent = Math.abs(verticalIndent);
// If we're going to indent, subtract off the space that will be required for indentation from
// the available space
if (cellWidth != SWT.DEFAULT) {
cellWidth -= absHorizontalIndent;
}
if (cellHeight != SWT.DEFAULT) {
cellHeight -= absVerticalIndent;
}
int controlWidth = horizontalAlignment == SWT.FILL ? cellWidth
: SWT.DEFAULT;
int controlHeight = verticalAlignment == SWT.FILL ? cellHeight
: SWT.DEFAULT;
// Note: this could be optimized further. If we're using a MAXIMUM hint and
// non-FILL alignment, we could simply call computeMaximumBoundedSize using the
// minimum of the cell size and the hint as the boundary -- basically, rather
// than applying two limits for the hint and the cell boundary, we can do it in
// one step and reduce the size computations by half (for this specific case).
Point controlSize = computeControlSize(toCompute, controlWidth,
controlHeight);
if (cellWidth != SWT.DEFAULT && controlSize.x > cellWidth) {
controlSize = computeControlSize(toCompute, cellWidth,
controlHeight);
if (cellHeight != SWT.DEFAULT && controlSize.y > cellHeight) {
controlSize.y = cellHeight;
}
} else if (cellHeight != SWT.DEFAULT && controlSize.y > cellHeight) {
controlSize = computeControlSize(toCompute, controlWidth,
cellHeight);
if (cellWidth != SWT.DEFAULT && controlSize.x > cellWidth) {
controlSize.x = cellWidth;
}
}
// If we're going to indent, add the indentation to the required space
controlSize.x += absHorizontalIndent;
controlSize.y += absVerticalIndent;
return controlSize;
}
/**
* Arranges the given control within the given rectangle using the
* criteria described by this CellData.
*
* @param control
* @param cellBounds
* @since 3.0
*/
public void positionControl(SizeCache cache, Rectangle cellBounds) {
int startx = cellBounds.x;
int starty = cellBounds.y;
int availableWidth = cellBounds.width - horizontalIndent;
int availableHeight = cellBounds.height - verticalIndent;
Point size = computeSize(cache, availableWidth, availableHeight);
// Horizontal justification
switch (horizontalAlignment) {
case SWT.RIGHT:
startx = cellBounds.x + availableWidth - size.x;
break;
case SWT.CENTER:
startx = cellBounds.x + (availableWidth - size.x) / 2;
break;
}
// Vertical justification
switch (verticalAlignment) {
case SWT.BOTTOM:
starty = cellBounds.y + availableHeight - size.y;
break;
case SWT.CENTER:
starty = cellBounds.y + (availableHeight - size.y) / 2;
break;
}
// Position the control
cache.getControl().setBounds(startx + horizontalIndent,
starty + verticalIndent, size.x, size.y);
}
/**
* Returns the preferred size of the given control in this cell, given one or both
* known dimensions of the control. This differs from computeSize, which takes known
* dimensions of the <b>cell</b> as arguments.
*
* @param toCompute
* @param controlWidth
* @param controlHeight
* @return
* @since 3.0
*/
private Point computeControlSize(SizeCache toCompute, int controlWidth,
int controlHeight) {
switch (hintType) {
case OVERRIDE:
return computeOverrideSize(toCompute, controlWidth, controlHeight,
widthHint, heightHint);
case MINIMUM:
return computeMinimumBoundedSize(toCompute, controlWidth,
controlHeight, widthHint, heightHint);
case MAXIMUM:
return computeMaximumBoundedSize(toCompute, controlWidth,
controlHeight, widthHint, heightHint);
}
return computeRawSize(toCompute, controlWidth, controlHeight);
}
/**
* Computes the size of the control, given its outer dimensions. This should be used in
* place of calling Control.computeSize, since Control.computeSize takes control-specific
* inner dimensions as hints.
*
* @param toCompute Control whose size will be computed
* @param controlWidth width of the control (pixels or SWT.DEFAULT if unknown)
* @param controlHeight height of the control (pixels or SWT.DEFAULT if unknown)
* @return preferred dimensions of the control
*/
private static Point computeRawSize(SizeCache toCompute, int controlWidth,
int controlHeight) {
if (controlWidth != SWT.DEFAULT && controlHeight != SWT.DEFAULT) {
return new Point(controlWidth, controlHeight);
}
// Known bug: we pass the OUTER dimension of the control into computeSize, even though
// SWT expects a control-specific inner dimension as width and height hints. Currently,
// SWT does not provide any means to convert outer dimensions into inner dimensions.
// Fortunately, the outer and inner dimensions tend to be quite close so we
// pass in the outer dimension and adjust the result if it differs from one of the
// hints. This may cause incorrect text wrapping in rare cases, and should be fixed
// once SWT provides a way to convert the outer dimension of a control into a valid
// width or height hint for Control.computeSize. Note that the distinction between outer
// and inner dimensions is undocumented in SWT, and most examples also contain this
// bug.
Point result = toCompute.computeSize(controlWidth, controlHeight);
// Hack: If the result of computeSize differs from the width or height-hints, adjust it.
// See above. Don't remove this hack until SWT provides some way to pass correct width
// and height hints into computeSize. Once this happens, these conditions should always
// return false and the hack will have no effect.
if (controlWidth != SWT.DEFAULT) {
result.x = controlWidth;
} else if (controlHeight != SWT.DEFAULT) {
result.y = controlHeight;
}
return result;
}
/**
* Computes the preferred size of the control. Optionally, one or both dimensions
* may be fixed to a given size.
*
* @param control object that can compute the size of the control of interest
* @param wHint known width (or SWT.DEFAULT if the width needs to be computed)
* @param hHint known height (or SWT.DEFAULT if the height needs to be computed)
* @param overrideW width that should always be returned by the control,
* or SWT.DEFAULT if the width is not being constrained
* @param overrideH height that should always be returned by the control,
* or SWT.DEFAULT if the height is not being constrained
* @return
*/
private static Point computeOverrideSize(SizeCache control, int wHint,
int hHint, int overrideW, int overrideH) {
int resultWidth = overrideW;
int resultHeight = overrideH;
if (wHint != SWT.DEFAULT) {
resultWidth = wHint;
}
if (hHint != SWT.DEFAULT) {
resultHeight = hHint;
}
if (resultWidth == SWT.DEFAULT || resultHeight == SWT.DEFAULT) {
Point result = computeRawSize(control, resultWidth, resultHeight);
return result;
}
return new Point(resultWidth, resultHeight);
}
/**
* Computes the size for the control, optionally bounding the size in the x and
* y directions. The various hints are used to determine which dimensions are
* already known and which dimensions need to be computed.
*
* @param control The control whose size should be computed
* @param wHint known width (or SWT.DEFAULT if the width needs to be computed)
* @param hHint known height (or SWT.DEFAULT if the height needs to be computed)
* @param boundedWidth maximum width for the control (or SWT.DEFAULT if the width is unbounded)
* @param boundedHeight maximum height for the control (or SWT.DEFAULT if the height is unbounded)
* @return the preferred size of the control, given that it cannot exceed the given bounds
*/
private static Point computeMaximumBoundedSize(SizeCache control,
int wHint, int hHint, int boundedWidth, int boundedHeight) {
Point controlSize = computeRawSize(control, wHint, hHint);
if (wHint == SWT.DEFAULT && boundedWidth != SWT.DEFAULT
&& controlSize.x > boundedWidth) {
return computeMaximumBoundedSize(control, boundedWidth, hHint,
boundedWidth, boundedHeight);
}
if (hHint == SWT.DEFAULT && boundedHeight != SWT.DEFAULT
&& controlSize.y > boundedHeight) {
return computeMaximumBoundedSize(control, wHint, boundedHeight,
boundedWidth, boundedHeight);
}
return controlSize;
}
private static Point computeMinimumBoundedSize(SizeCache control,
int wHint, int hHint, int minimumWidth, int minimumHeight) {
Point controlSize = computeRawSize(control, wHint, hHint);
if (minimumWidth != SWT.DEFAULT && wHint == SWT.DEFAULT
&& controlSize.x < minimumWidth) {
return computeMinimumBoundedSize(control, minimumWidth, hHint,
minimumWidth, minimumHeight);
}
if (minimumHeight != SWT.DEFAULT && hHint == SWT.DEFAULT
&& controlSize.y < minimumHeight) {
return computeMinimumBoundedSize(control, wHint, minimumHeight,
minimumWidth, minimumHeight);
}
return controlSize;
}
}