changeset 50:d48f7334742c

FormLayout, FormData, FormAttachment
author Frank Benoit <benoit@tionex.de>
date Fri, 11 Jan 2008 10:44:12 +0100
parents a67796650ad4
children e300eb95bec4
files dwt/layout/FormAttachment.d dwt/layout/FormData.d dwt/layout/FormLayout.d
diffstat 3 files changed, 1045 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/layout/FormAttachment.d	Fri Jan 11 10:44:12 2008 +0100
@@ -0,0 +1,304 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.layout.FormAttachment;
+
+import dwt.SWT;
+import dwt.widgets.Control;
+import dwt.layout.FormLayout;
+import dwt.layout.FormData;
+
+import tango.text.convert.Format;
+
+/**
+ * Instances of this class are used to define the edges of a control
+ * within a <code>FormLayout</code>.
+ * <p>
+ * <code>FormAttachments</code> are set into the top, bottom, left,
+ * and right fields of the <code>FormData</code> for a control.
+ * For example:
+ * <pre>
+ * 		FormData data = new FormData();
+ * 		data.top = new FormAttachment(0,5);
+ * 		data.bottom = new FormAttachment(100,-5);
+ * 		data.left = new FormAttachment(0,5);
+ * 		data.right = new FormAttachment(100,-5);
+ * 		button.setLayoutData(data);
+ * </pre>
+ * </p>
+ * <p>
+ * A <code>FormAttachment</code> defines where to attach the side of
+ * a control by using the equation, y = ax + b. The "a" term represents
+ * a fraction of the parent composite's width (from the left) or height
+ * (from the top). It can be defined using a numerator and denominator,
+ * or just a percentage value. If a percentage is used, the denominator
+ * is set to 100. The "b" term in the equation represents an offset, in
+ * pixels, from the attachment position. For example:
+ * <pre>
+ * 		FormAttachment attach = new FormAttachment (20, -5);
+ * </pre>
+ * specifies that the side to which the <code>FormAttachment</code>
+ * object belongs will lie at 20% of the parent composite, minus 5 pixels.
+ * </p>
+ * <p>
+ * Control sides can also be attached to another control.
+ * For example:
+ * <pre>
+ * 		FormAttachment attach = new FormAttachment (button, 10);
+ * </pre>
+ * specifies that the side to which the <code>FormAttachment</code>
+ * object belongs will lie in the same position as the adjacent side of
+ * the <code>button</code> control, plus 10 pixels. The control side can
+ * also be attached to the opposite side of the specified control.
+ * For example:
+ * <pre>
+ * 		FormData data = new FormData ();
+ * 		data.left = new FormAttachment (button, 0, SWT.LEFT);
+ * </pre>
+ * specifies that the left side of the control will lie in the same position
+ * as the left side of the <code>button</code> control. The control can also
+ * be attached in a position that will center the control on the specified
+ * control. For example:
+ * <pre>
+ * 		data.left = new FormAttachment (button, 0, SWT.CENTER);
+ * </pre>
+ * specifies that the left side of the control will be positioned so that it is
+ * centered between the left and right sides of the <code>button</code> control.
+ * If the alignment is not specified, the default is to attach to the adjacent side.
+ * </p>
+ *
+ * @see FormLayout
+ * @see FormData
+ *
+ * @since 2.0
+ */
+public final class FormAttachment {
+	/**
+	 * numerator specifies the numerator of the "a" term in the
+	 * equation, y = ax + b, which defines the attachment.
+	 */
+	public int numerator;
+
+	/**
+	 * denominator specifies the denominator of the "a" term in the
+	 * equation, y = ax + b, which defines the attachment.
+	 *
+	 * The default value is 100.
+	 */
+	public int denominator = 100;
+
+	/**
+	 * offset specifies the offset, in pixels, of the control side
+	 * from the attachment position.
+	 * If the offset is positive, then the control side is offset
+	 * to the right of or below the attachment position. If it is
+	 * negative, then the control side is offset to the left of or
+	 * above the attachment position.
+	 *
+	 * This is equivalent to the "b" term in the equation y = ax + b.
+	 * The default value is 0.
+	 */
+	public int offset;
+
+	/**
+	 * control specifies the control to which the control side is
+	 * attached.
+	 */
+	public Control control;
+
+	/**
+	 * alignment specifies the alignment of the control side that is
+	 * attached to a control.
+	 * <p>
+	 * For top and bottom attachments, TOP, BOTTOM and CENTER are used. For left
+	 * and right attachments, LEFT, RIGHT and CENTER are used. If any other case
+	 * occurs, the default will be used instead.
+	 * </p>
+	 *
+	 * <br>Possible values are: <ul>
+	 *    <li>TOP: Attach the side to the top side of the specified control.</li>
+	 *    <li>BOTTOM : Attach the side to the bottom side of the specified control.</li>
+	 *    <li>LEFT: Attach the side to the left side of the specified control.</li>
+	 *    <li>RIGHT: Attach the side to the right side of the specified control.</li>
+	 *    <li>CENTER: Attach the side at a position which will center the control on the specified control.</li>
+	 *    <li>DEFAULT: Attach the side to the adjacent side of the specified control.</li>
+	 * </ul>
+	 */
+	public int alignment;
+
+/**
+ * Constructs a new instance of this class.
+ * Since no numerator, denominator or offset is specified,
+ * the attachment is treated as a percentage of the form.
+ * The numerator is zero, the denominator is 100 and the
+ * offset is zero.
+ *
+ * @since 3.2
+ */
+public this () {
+}
+
+/**
+ * Constructs a new instance of this class given a numerator
+ * Since no denominator or offset is specified, the default
+ * is to treat the numerator as a percentage of the form, with a
+ * denominator of 100. The offset is zero.
+ *
+ * @param numerator the percentage of the position
+ *
+ * @since 3.0
+ */
+public this (int numerator) {
+	this (numerator, 100, 0);
+}
+
+/**
+ * Constructs a new instance of this class given a numerator
+ * and an offset. Since no denominator is specified, the default
+ * is to treat the numerator as a percentage of the form, with a
+ * denominator of 100.
+ *
+ * @param numerator the percentage of the position
+ * @param offset the offset of the side from the position
+ */
+public this (int numerator, int offset) {
+	this (numerator, 100, offset);
+}
+
+/**
+ * Constructs a new instance of this class given a numerator
+ * and denominator and an offset. The position of the side is
+ * given by the fraction of the form defined by the numerator
+ * and denominator.
+ *
+ * @param numerator the numerator of the position
+ * @param denominator the denominator of the position
+ * @param offset the offset of the side from the position
+ */
+public this (int numerator, int denominator, int offset) {
+	if (denominator == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO);
+	this.numerator = numerator;
+	this.denominator = denominator;
+	this.offset = offset;
+}
+
+/**
+ * Constructs a new instance of this class given a control.
+ * Since no alignment is specified, the default alignment is
+ * to attach the side to the adjacent side of the specified
+ * control. Since no offset is specified, an offset of 0 is
+ * used.
+ *
+ * @param control the control the side is attached to
+ */
+public this (Control control) {
+	this (control, 0, SWT.DEFAULT);
+}
+
+/**
+ * Constructs a new instance of this class given a control
+ * and an offset. Since no alignment is specified, the default
+ * alignment is to attach the side to the adjacent side of the
+ * specified control.
+ *
+ * @param control the control the side is attached to
+ * @param offset the offset of the side from the control
+ */
+public this (Control control, int offset) {
+	this (control, offset, SWT.DEFAULT);
+}
+
+/**
+ * Constructs a new instance of this class given a control,
+ * an offset and an alignment.
+ *
+ * @param control the control the side is attached to
+ * @param offset the offset of the side from the control
+ * @param alignment the alignment of the side to the control it is attached to
+ */
+public this (Control control, int offset, int alignment) {
+	this.control = control;
+	this.offset = offset;
+	this.alignment = alignment;
+}
+
+FormAttachment divide (int value) {
+	return new FormAttachment (numerator, denominator * value, offset / value);
+}
+
+int gcd (int m, int n) {
+	int temp;
+	m = Math.abs (m);
+	n = Math.abs (n);
+	if (m < n) {
+		temp = m;
+		m = n;
+		n = temp;
+	}
+	while (n != 0){
+		temp = m;
+		m = n;
+		n = temp % n;
+	}
+	return m;
+}
+
+FormAttachment minus (FormAttachment attachment) {
+	FormAttachment solution = new FormAttachment ();
+	solution.numerator = numerator * attachment.denominator - denominator * attachment.numerator;
+	solution.denominator = denominator * attachment.denominator;
+	int gcd = gcd (solution.denominator, solution.numerator);
+	solution.numerator = solution.numerator / gcd;
+	solution.denominator = solution.denominator / gcd;
+	solution.offset = offset - attachment.offset;
+	return solution;
+}
+
+FormAttachment minus (int value) {
+	return new FormAttachment (numerator, denominator, offset - value);
+}
+
+FormAttachment plus (FormAttachment attachment) {
+	FormAttachment solution = new FormAttachment ();
+	solution.numerator = numerator * attachment.denominator + denominator * attachment.numerator;
+	solution.denominator = denominator * attachment.denominator;
+	int gcd = gcd (solution.denominator, solution.numerator);
+	solution.numerator = solution.numerator / gcd;
+	solution.denominator = solution.denominator / gcd;
+	solution.offset = offset + attachment.offset;
+	return solution;
+}
+
+FormAttachment plus (int value) {
+	return new FormAttachment (numerator, denominator, offset + value);
+}
+
+int solveX (int value) {
+	if (denominator == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO);
+	return ((numerator * value) / denominator) + offset;
+}
+
+int solveY (int value) {
+	if (numerator == 0) SWT.error (SWT.ERROR_CANNOT_BE_ZERO);
+	return (value - offset) * denominator / numerator;
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the FormAttachment
+ */
+public char[] toString () {
+ 	char[] string = control != null ? control.toString () : Format( "{}/{}", numerator, denominator );
+	return Format("{{y = ({})x + {}}", string, ( offset >= 0 ? Format(")x + {}", offset ) : Format( ")x - {}", -offset)));
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/layout/FormData.d	Fri Jan 11 10:44:12 2008 +0100
@@ -0,0 +1,348 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.layout.FormData;
+
+
+import dwt.SWT;
+import dwt.graphics.Point;
+import dwt.widgets.Control;
+import dwt.layout.FormAttachment;
+import tango.text.Util;
+
+import tango.text.Util;
+import tango.util.Convert;
+
+/**
+ * Instances of this class are used to define the attachments
+ * of a control in a <code>FormLayout</code>.
+ * <p>
+ * To set a <code>FormData</code> object into a control, you use the
+ * <code>setLayoutData ()</code> method. To define attachments for the
+ * <code>FormData</code>, set the fields directly, like this:
+ * <pre>
+ * 		FormData data = new FormData();
+ * 		data.left = new FormAttachment(0,5);
+ * 		data.right = new FormAttachment(100,-5);
+ * 		button.setLayoutData(formData);
+ * </pre>
+ * </p>
+ * <p>
+ * <code>FormData</code> contains the <code>FormAttachments</code> for
+ * each edge of the control that the <code>FormLayout</code> uses to
+ * determine the size and position of the control. <code>FormData</code>
+ * objects also allow you to set the width and height of controls within
+ * a <code>FormLayout</code>.
+ * </p>
+ *
+ * @see FormLayout
+ * @see FormAttachment
+ *
+ * @since 2.0
+ */
+public final class FormData {
+	/**
+	 * width specifies the preferred width in pixels. This value
+	 * is the wHint passed into Control.computeSize(int, int, bool)
+	 * to determine the preferred size of the control.
+	 *
+	 * The default value is SWT.DEFAULT.
+	 *
+	 * @see Control#computeSize(int, int, bool)
+	 */
+	public int width = SWT.DEFAULT;
+	/**
+	 * height specifies the preferred height in pixels. This value
+	 * is the hHint passed into Control.computeSize(int, int, bool)
+	 * to determine the preferred size of the control.
+	 *
+	 * The default value is SWT.DEFAULT.
+	 *
+	 * @see Control#computeSize(int, int, bool)
+	 */
+	public int height = SWT.DEFAULT;
+	/**
+	 * left specifies the attachment of the left side of
+	 * the control.
+	 */
+	public FormAttachment left;
+	/**
+	 * right specifies the attachment of the right side of
+	 * the control.
+	 */
+	public FormAttachment right;
+	/**
+	 * top specifies the attachment of the top of the control.
+	 */
+	public FormAttachment top;
+	/**
+	 * bottom specifies the attachment of the bottom of the
+	 * control.
+	 */
+	public FormAttachment bottom;
+
+	int cacheWidth = -1, cacheHeight = -1;
+	int defaultWhint, defaultHhint, defaultWidth = -1, defaultHeight = -1;
+	int currentWhint, currentHhint, currentWidth = -1, currentHeight = -1;
+	FormAttachment cacheLeft, cacheRight, cacheTop, cacheBottom;
+	bool isVisited, needed;
+
+/**
+ * Constructs a new instance of FormData using
+ * default values.
+ */
+public this () {
+}
+
+/**
+ * Constructs a new instance of FormData according to the parameters.
+ * A value of SWT.DEFAULT indicates that no minimum width or
+ * no minimum height is specified.
+ *
+ * @param width a minimum width for the control
+ * @param height a minimum height for the control
+ */
+public this (int width, int height) {
+	this.width = width;
+	this.height = height;
+}
+
+void computeSize (Control control, int wHint, int hHint, bool flushCache_) {
+	if (cacheWidth !is -1 && cacheHeight !is -1) return;
+	if (wHint is this.width && hHint is this.height) {
+		if (defaultWidth is -1 || defaultHeight is -1 || wHint !is defaultWhint || hHint !is defaultHhint) {
+			Point size =  control.computeSize (wHint, hHint, flushCache_);
+			defaultWhint = wHint;
+			defaultHhint = hHint;
+			defaultWidth = size.x;
+			defaultHeight = size.y;
+		}
+		cacheWidth = defaultWidth;
+		cacheHeight = defaultHeight;
+		return;
+	}
+	if (currentWidth is -1 || currentHeight is -1 || wHint !is currentWhint || hHint !is currentHhint) {
+		Point size =  control.computeSize (wHint, hHint, flushCache_);
+		currentWhint = wHint;
+		currentHhint = hHint;
+		currentWidth = size.x;
+		currentHeight = size.y;
+	}
+	cacheWidth = currentWidth;
+	cacheHeight = currentHeight;
+}
+
+void flushCache () {
+	cacheWidth = cacheHeight = -1;
+	defaultHeight = defaultWidth = -1;
+	currentHeight = currentWidth = -1;
+}
+
+int getWidth (Control control, bool flushCache) {
+	needed = true;
+	computeSize (control, width, height, flushCache);
+	return cacheWidth;
+}
+
+int getHeight (Control control, bool flushCache) {
+	computeSize (control, width, height, flushCache);
+	return cacheHeight;
+}
+
+FormAttachment getBottomAttachment (Control control, int spacing, bool flushCache) {
+	if (cacheBottom !is null) return cacheBottom;
+	if (isVisited) return cacheBottom = new FormAttachment (0, getHeight (control, flushCache));
+	if (bottom is null) {
+		if (top is null) return cacheBottom = new FormAttachment (0, getHeight (control, flushCache));
+		return cacheBottom = getTopAttachment (control, spacing, flushCache).plus (getHeight (control, flushCache));
+	}
+	Control bottomControl = bottom.control;
+	if (bottomControl !is null) {
+		if (bottomControl.isDisposed ()) {
+			bottom.control = bottomControl = null;
+		} else {
+			if (bottomControl.getParent () !is control.getParent ()) {
+				bottomControl = null;
+			}
+		}
+	}
+	if (bottomControl is null) return cacheBottom = bottom;
+	isVisited = true;
+	FormData bottomData = cast(FormData) bottomControl.getLayoutData ();
+	FormAttachment bottomAttachment = bottomData.getBottomAttachment (bottomControl, spacing, flushCache);
+	switch (bottom.alignment) {
+		case SWT.BOTTOM:
+			cacheBottom = bottomAttachment.plus (bottom.offset);
+			break;
+		case SWT.CENTER: {
+			FormAttachment topAttachment = bottomData.getTopAttachment (bottomControl, spacing, flushCache);
+			FormAttachment bottomHeight = bottomAttachment.minus (topAttachment);
+			cacheBottom = bottomAttachment.minus (bottomHeight.minus (getHeight (control, flushCache)).divide (2));
+			break;
+		}
+		default: {
+			FormAttachment topAttachment = bottomData.getTopAttachment (bottomControl, spacing, flushCache);
+			cacheBottom = topAttachment.plus (bottom.offset - spacing);
+			break;
+		}
+	}
+	isVisited = false;
+	return cacheBottom;
+}
+
+FormAttachment getLeftAttachment (Control control, int spacing, bool flushCache) {
+	if (cacheLeft !is null) return cacheLeft;
+	if (isVisited) return cacheLeft = new FormAttachment (0, 0);
+	if (left is null) {
+		if (right is null) return cacheLeft = new FormAttachment (0, 0);
+		return cacheLeft = getRightAttachment (control, spacing, flushCache).minus (getWidth (control, flushCache));
+	}
+	Control leftControl = left.control;
+	if (leftControl !is null) {
+		if (leftControl.isDisposed ()) {
+			left.control = leftControl = null;
+		} else {
+			if (leftControl.getParent () !is control.getParent ()) {
+				leftControl = null;
+			}
+		}
+	}
+	if (leftControl is null) return cacheLeft = left;
+	isVisited = true;
+	FormData leftData = cast(FormData) leftControl.getLayoutData ();
+	FormAttachment leftAttachment = leftData.getLeftAttachment (leftControl, spacing, flushCache);
+	switch (left.alignment) {
+		case SWT.LEFT:
+			cacheLeft = leftAttachment.plus (left.offset);
+			break;
+		case SWT.CENTER: {
+			FormAttachment rightAttachment = leftData.getRightAttachment (leftControl, spacing, flushCache);
+			FormAttachment leftWidth = rightAttachment.minus (leftAttachment);
+			cacheLeft = leftAttachment.plus (leftWidth.minus (getWidth (control, flushCache)).divide (2));
+			break;
+		}
+		default: {
+			FormAttachment rightAttachment = leftData.getRightAttachment (leftControl, spacing, flushCache);
+			cacheLeft = rightAttachment.plus (left.offset + spacing);
+		}
+	}
+	isVisited = false;
+	return cacheLeft;
+}
+
+char[] getName () {
+    char[] string = this.classinfo.name;
+    int index = locatePrior( string, '.');
+    if (index is string.length ) return string;
+    return string[ index + 1 .. string.length ];
+}
+
+FormAttachment getRightAttachment (Control control, int spacing, bool flushCache) {
+	if (cacheRight !is null) return cacheRight;
+	if (isVisited) return cacheRight = new FormAttachment (0, getWidth (control, flushCache));
+	if (right is null) {
+		if (left is null) return cacheRight = new FormAttachment (0, getWidth (control, flushCache));
+		return cacheRight = getLeftAttachment (control, spacing, flushCache).plus (getWidth (control, flushCache));
+	}
+	Control rightControl = right.control;
+	if (rightControl !is null) {
+		if (rightControl.isDisposed ()) {
+			right.control = rightControl = null;
+		} else {
+			if (rightControl.getParent () !is control.getParent ()) {
+				rightControl = null;
+			}
+		}
+	}
+	if (rightControl is null) return cacheRight = right;
+	isVisited = true;
+	FormData rightData = cast(FormData) rightControl.getLayoutData ();
+	FormAttachment rightAttachment = rightData.getRightAttachment (rightControl, spacing, flushCache);
+	switch (right.alignment) {
+		case SWT.RIGHT:
+			cacheRight = rightAttachment.plus (right.offset);
+			break;
+		case SWT.CENTER: {
+			FormAttachment leftAttachment = rightData.getLeftAttachment (rightControl, spacing, flushCache);
+			FormAttachment rightWidth = rightAttachment.minus (leftAttachment);
+			cacheRight = rightAttachment.minus (rightWidth.minus (getWidth (control, flushCache)).divide (2));
+			break;
+		}
+		default: {
+			FormAttachment leftAttachment = rightData.getLeftAttachment (rightControl, spacing, flushCache);
+			cacheRight = leftAttachment.plus (right.offset - spacing);
+			break;
+		}
+	}
+	isVisited = false;
+	return cacheRight;
+}
+
+FormAttachment getTopAttachment (Control control, int spacing, bool flushCache) {
+	if (cacheTop !is null) return cacheTop;
+	if (isVisited) return cacheTop = new FormAttachment (0, 0);
+	if (top is null) {
+		if (bottom is null) return cacheTop = new FormAttachment (0, 0);
+		return cacheTop = getBottomAttachment (control, spacing, flushCache).minus (getHeight (control, flushCache));
+	}
+	Control topControl = top.control;
+	if (topControl !is null) {
+		if (topControl.isDisposed ()) {
+			top.control = topControl = null;
+		} else {
+			if (topControl.getParent () !is control.getParent ()) {
+				topControl = null;
+			}
+		}
+	}
+	if (topControl is null) return cacheTop = top;
+	isVisited = true;
+	FormData topData = cast(FormData) topControl.getLayoutData ();
+	FormAttachment topAttachment = topData.getTopAttachment (topControl, spacing, flushCache);
+	switch (top.alignment) {
+		case SWT.TOP:
+			cacheTop = topAttachment.plus (top.offset);
+			break;
+		case SWT.CENTER: {
+			FormAttachment bottomAttachment = topData.getBottomAttachment (topControl, spacing, flushCache);
+			FormAttachment topHeight = bottomAttachment.minus (topAttachment);
+			cacheTop = topAttachment.plus (topHeight.minus (getHeight (control, flushCache)).divide (2));
+			break;
+		}
+		default: {
+			FormAttachment bottomAttachment = topData.getBottomAttachment (topControl, spacing, flushCache);
+			cacheTop = bottomAttachment.plus (top.offset + spacing);
+			break;
+		}
+	}
+	isVisited = false;
+	return cacheTop;
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the FormData object
+ */
+public char[] toString () {
+ 	char[] string = getName()~" {";
+ 	if (width !is SWT.DEFAULT) string ~= "width="~to!(char[])(width)~" ";
+	if (height !is SWT.DEFAULT) string ~= "height="~to!(char[])(height)~" ";
+ 	if (left !is null) string ~= "left="~to!(char[])(left)~" ";
+ 	if (right !is null) string ~= "right="~to!(char[])(right)~" ";
+ 	if (top !is null) string ~= "top="~to!(char[])(top)~" ";
+ 	if (bottom !is null) string ~= "bottom="~to!(char[])(bottom)~" ";
+ 	string = trim( string );
+ 	string ~= "}";
+	return string;
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/layout/FormLayout.d	Fri Jan 11 10:44:12 2008 +0100
@@ -0,0 +1,393 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+module dwt.layout.FormLayout;
+
+import dwt.layout.FormAttachment;
+import dwt.layout.FormData;
+import dwt.SWT;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Control;
+import dwt.widgets.Layout;
+import dwt.widgets.Composite;
+import dwt.widgets.Scrollable;
+
+import tango.text.Util;
+import tango.util.Convert;
+import Math = tango.math.Math;
+
+/**
+ * Instances of this class control the position and size of the
+ * children of a composite control by using <code>FormAttachments</code>
+ * to optionally configure the left, top, right and bottom edges of
+ * each child.
+ * <p>
+ * The following example code creates a <code>FormLayout</code> and then sets
+ * it into a <code>Shell</code>:
+ * <pre>
+ * 		Display display = new Display ();
+ *		Shell shell = new Shell(display);
+ *		FormLayout layout = new FormLayout();
+ *		layout.marginWidth = 3;
+ *		layout.marginHeight = 3;
+ *		shell.setLayout(layout);
+ * </pre>
+ * </p>
+ * <p>
+ * To use a <code>FormLayout</code>, create a <code>FormData</code> with
+ * <code>FormAttachment</code> for each child of <code>Composite</code>.
+ * The following example code attaches <code>button1</code> to the top
+ * and left edge of the composite and <code>button2</code> to the right
+ * edge of <code>button1</code> and the top and right edges of the
+ * composite:
+ * <pre>
+ *		FormData data1 = new FormData();
+ *		data1.left = new FormAttachment(0, 0);
+ *		data1.top = new FormAttachment(0, 0);
+ *		button1.setLayoutData(data1);
+ *		FormData data2 = new FormData();
+ *		data2.left = new FormAttachment(button1);
+ *		data2.top = new FormAttachment(0, 0);
+ *		data2.right = new FormAttachment(100, 0);
+ *		button2.setLayoutData(data2);
+ * </pre>
+ * </p>
+ * <p>
+ * Each side of a child control can be attached to a position in the parent
+ * composite, or to other controls within the <code>Composite</code> by
+ * creating instances of <code>FormAttachment</code> and setting them into
+ * the top, bottom, left, and right fields of the child's <code>FormData</code>.
+ * </p>
+ * <p>
+ * If a side is not given an attachment, it is defined as not being attached
+ * to anything, causing the child to remain at its preferred size.  If a child
+ * is given no attachment on either the left or the right or top or bottom, it is
+ * automatically attached to the left and top of the composite respectively.
+ * The following code positions <code>button1</code> and <code>button2</code>
+ * but relies on default attachments:
+ * <pre>
+ *		FormData data2 = new FormData();
+ *		data2.left = new FormAttachment(button1);
+ *		data2.right = new FormAttachment(100, 0);
+ *		button2.setLayoutData(data2);
+ * </pre>
+ * </p>
+ * <p>
+ * IMPORTANT: Do not define circular attachments.  For example, do not attach
+ * the right edge of <code>button1</code> to the left edge of <code>button2</code>
+ * and then attach the left edge of <code>button2</code> to the right edge of
+ * <code>button1</code>.  This will over constrain the layout, causing undefined
+ * behavior.  The algorithm will terminate, but the results are undefined.
+ * </p>
+ *
+ * @see FormData
+ * @see FormAttachment
+ *
+ * @since 2.0
+ *
+ */
+public final class FormLayout : Layout {
+
+	/**
+	 * 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.
+	 */
+ 	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.
+	 */
+ 	public int marginHeight = 0;
+
+
+ 	/**
+	 * marginLeft specifies the number of pixels of horizontal margin
+	 * that will be placed along the left edge of the layout.
+	 *
+	 * The default value is 0.
+	 *
+	 * @since 3.1
+	 */
+	public int marginLeft = 0;
+
+	/**
+	 * marginTop specifies the number of pixels of vertical margin
+	 * that will be placed along the top edge of the layout.
+	 *
+	 * The default value is 0.
+	 *
+	 * @since 3.1
+	 */
+	public int marginTop = 0;
+
+	/**
+	 * marginRight specifies the number of pixels of horizontal margin
+	 * that will be placed along the right edge of the layout.
+	 *
+	 * The default value is 0.
+	 *
+	 * @since 3.1
+	 */
+	public int marginRight = 0;
+
+	/**
+	 * marginBottom specifies the number of pixels of vertical margin
+	 * that will be placed along the bottom edge of the layout.
+	 *
+	 * The default value is 0.
+	 *
+	 * @since 3.1
+	 */
+	public int marginBottom = 0;
+
+	/**
+	 * spacing specifies the number of pixels between the edge of one control
+	 * and the edge of its neighbouring control.
+	 *
+	 * The default value is 0.
+	 *
+	 * @since 3.0
+	 */
+	public int spacing = 0;
+
+/**
+ * Constructs a new instance of this class.
+ */
+public this () {
+}
+
+/*
+ * Computes the preferred height of the form with
+ * respect to the preferred height of the control.
+ *
+ * Given that the equations for top (T) and bottom (B)
+ * of the control in terms of the height of the form (X)
+ * are:
+ *		T = AX + B
+ *		B = CX + D
+ *
+ * The equation for the height of the control (H)
+ * is bottom (B) minus top (T) or (H = B - T) or:
+ *
+ *		H = (CX + D) - (AX + B)
+ *
+ * Solving for (X), the height of the form, we get:
+ *
+ *		X = (H + B - D) / (C - A)
+ *
+ * When (A = C), (C - A = 0) and the equation has no
+ * solution for X.  This is a special case meaning that
+ * the control does not constrain the height of the
+ * form.  In this case, we need to arbitrarily define
+ * the height of the form (X):
+ *
+ * Case 1: A = C, A = 0, C = 0
+ *
+ * 		Let X = D, the distance from the top of the form
+ * 		to the bottom edge of the control.  In this case,
+ * 		the control was attached to the top of the form
+ * 		and the form needs to be large enough to show the
+ * 		bottom edge of the control.
+ *
+ * Case 2: A = C, A = 1, C = 1
+ *
+ * 		Let X = -B, the distance from the bottom of the
+ *		form to the top edge of the control.  In this case,
+ * 		the control was attached to the bottom of the form
+ * 		and the only way that the control would be visible
+ * 		is if the offset is negative.  If the offset is
+ * 		positive, there is no possible height for the form
+ * 		that will show the control as it will always be
+ * 		below the bottom edge of the form.
+ *
+ * Case 3: A = C, A !is 0, C !is 0 and A !is 1, C !is 0
+ *
+ * 		Let X = D / (1 - C), the distance from the top of the
+ * 		form to the bottom edge of the control.  In this case,
+ * 		since C is not 0 or 1, it must be a fraction, U / V.
+ * 		The offset D is the distance from CX to the bottom edge
+ * 		of the control.  This represents a fraction of the form
+ * 		(1 - C)X. Since the height of a fraction of the form is
+ * 		known, the height of the entire form can be found by setting
+ * 		(1 - C)X = D.  We solve this equation for X in terms of U
+ * 		and V, giving us X = (U * D) / (U - V). Similarly, if the
+ * 		offset D is	negative, the control is positioned above CX.
+ * 		The offset -B is the distance from the top edge of the control
+ * 		to CX. We can find the height of the entire form by setting
+ * 		CX = -B. Solving in terms of U and V gives us X = (-B * V) / U.
+ */
+int computeHeight (Control control, FormData data, bool flushCache) {
+	FormAttachment top = data.getTopAttachment (control, spacing, flushCache);
+	FormAttachment bottom = data.getBottomAttachment (control, spacing, flushCache);
+	FormAttachment height = bottom.minus (top);
+	if (height.numerator is 0) {
+		if (bottom.numerator is 0) return bottom.offset;
+		if (bottom.numerator is bottom.denominator) return -top.offset;
+		if (bottom.offset <= 0) {
+			return -top.offset * top.denominator / bottom.numerator;
+		}
+		int divider = bottom.denominator - bottom.numerator;
+		return bottom.denominator * bottom.offset / divider;
+	}
+	return height.solveY (data.getHeight (control, flushCache));
+}
+
+protected Point computeSize (Composite composite, int wHint, int hHint, bool flushCache) {
+	Point size = layout (composite, false, 0, 0, wHint, hHint, flushCache);
+	if (wHint !is SWT.DEFAULT) size.x = wHint;
+	if (hHint !is SWT.DEFAULT) size.y = hHint;
+	return size;
+}
+
+protected bool flushCache (Control control) {
+	Object data = control.getLayoutData ();
+	if (data !is null) (cast(FormData) data).flushCache ();
+	return true;
+}
+
+char[] getName () {
+	char[] string = this.classinfo.name;
+	int index = locatePrior( string, '.');
+	if (index is string.length ) return string;
+	return string[ index + 1 .. string.length ];
+}
+
+/*
+ * Computes the preferred height of the form with
+ * respect to the preferred height of the control.
+ */
+int computeWidth (Control control, FormData data, bool flushCache) {
+	FormAttachment left = data.getLeftAttachment (control, spacing, flushCache);
+	FormAttachment right = data.getRightAttachment (control, spacing, flushCache);
+	FormAttachment width = right.minus (left);
+	if (width.numerator is 0) {
+		if (right.numerator is 0) return right.offset;
+		if (right.numerator is right.denominator) return -left.offset;
+		if (right.offset <= 0) {
+			return -left.offset * left.denominator / left.numerator;
+		}
+		int divider = right.denominator - right.numerator;
+		return right.denominator * right.offset / divider;
+	}
+	return width.solveY (data.getWidth (control, flushCache));
+}
+
+protected void layout (Composite composite, bool flushCache) {
+	Rectangle rect = composite.getClientArea ();
+	int x = rect.x + marginLeft + marginWidth;
+	int y = rect.y + marginTop + marginHeight;
+	int width = Math.max (0, rect.width - marginLeft - 2 * marginWidth - marginRight);
+	int height = Math.max (0, rect.height - marginTop - 2 * marginHeight - marginBottom);
+	layout (composite, true, x, y, width, height, flushCache);
+}
+
+Point layout (Composite composite, bool move, int x, int y, int width, int height, bool flushCache) {
+	Control [] children = composite.getChildren ();
+	for (int i=0; i<children.length; i++) {
+		Control child = children [i];
+		FormData data = cast(FormData) child.getLayoutData ();
+		if (data is null) child.setLayoutData (data = new FormData ());
+		if (flushCache) data.flushCache ();
+		data.cacheLeft = data.cacheRight = data.cacheTop = data.cacheBottom = null;
+	}
+	bool [] flush = null;
+	Rectangle [] bounds = null;
+	int w = 0, h = 0;
+	for (int i=0; i<children.length; i++) {
+		Control child = children [i];
+		FormData data = cast(FormData) child.getLayoutData ();
+		if (width !is SWT.DEFAULT) {
+			data.needed = false;
+			FormAttachment left = data.getLeftAttachment (child, spacing, flushCache);
+			FormAttachment right = data.getRightAttachment (child, spacing, flushCache);
+			int x1 = left.solveX (width), x2 = right.solveX (width);
+			if (data.height is SWT.DEFAULT && !data.needed) {
+				int trim = 0;
+				//TEMPORARY CODE
+				if ( auto sa = cast(Scrollable)child) {
+					Rectangle rect = sa.computeTrim (0, 0, 0, 0);
+					trim = rect.width;
+				} else {
+					trim = child.getBorderWidth () * 2;
+				}
+				data.cacheWidth = data.cacheHeight = -1;
+				int currentWidth = Math.max (0, x2 - x1 - trim);
+				data.computeSize (child, currentWidth, data.height, flushCache);
+				if (flush is null) flush = new bool [children.length];
+				flush [i] = true;
+			}
+			w = Math.max (x2, w);
+			if (move) {
+				if (bounds is null) bounds = new Rectangle [children.length];
+				bounds [i] = new Rectangle (0, 0, 0, 0);
+				bounds [i].x = x + x1;
+				bounds [i].width = x2 - x1;
+			}
+		} else {
+			w = Math.max (computeWidth (child, data, flushCache), w);
+		}
+	}
+	for (int i=0; i<children.length; i++) {
+		Control child = children [i];
+		FormData data = cast(FormData) child.getLayoutData ();
+		if (height !is SWT.DEFAULT) {
+			int y1 = data.getTopAttachment (child, spacing, flushCache).solveX (height);
+			int y2 = data.getBottomAttachment (child, spacing, flushCache).solveX (height);
+			h = Math.max (y2, h);
+			if (move) {
+				bounds [i].y = y + y1;
+				bounds [i].height = y2 - y1;
+			}
+		} else {
+			h = Math.max (computeHeight (child, data, flushCache), h);
+		}
+	}
+	for (int i=0; i<children.length; i++) {
+		Control child = children [i];
+		FormData data = cast(FormData) child.getLayoutData ();
+		if (flush !is null && flush [i]) data.cacheWidth = data.cacheHeight = -1;
+		data.cacheLeft = data.cacheRight = data.cacheTop = data.cacheBottom = null;
+	}
+	if (move) {
+		for (int i=0; i<children.length; i++) {
+			children [i].setBounds (bounds [i]);
+		}
+	}
+	w += marginLeft + marginWidth * 2 + marginRight;
+	h += marginTop + marginHeight * 2 + marginBottom;
+	return new Point (w, h);
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the layout
+ */
+public char[] toString () {
+ 	char[] string =  getName ()~" {";
+ 	if (marginWidth !is 0) string ~= "marginWidth="~to!(char[])(marginWidth)~" ";
+ 	if (marginHeight !is 0) string ~= "marginHeight="~to!(char[])(marginHeight)~" ";
+ 	if (marginLeft !is 0) string ~= "marginLeft="~to!(char[])(marginLeft)~" ";
+ 	if (marginRight !is 0) string ~= "marginRight="~to!(char[])(marginRight)~" ";
+ 	if (marginTop !is 0) string ~= "marginTop="~to!(char[])(marginTop)~" ";
+ 	if (marginBottom !is 0) string ~= "marginBottom="~to!(char[])(marginBottom)~" ";
+ 	if (spacing !is 0) string ~= "spacing="~to!(char[])(spacing)~" ";
+ 	string = trim( string );
+ 	string ~= "}";
+ 	return string;
+}
+}